roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.weightClass = ["btn-default", 
538                        "btn-primary", 
539                        "btn-success", 
540                        "btn-info", 
541                        "btn-warning",
542                        "btn-danger",
543                        "btn-link"
544                       ],  
545     this.addEvents({
546         // raw events
547         /**
548          * @event click
549          * When a butotn is pressed
550          * @param {Roo.bootstrap.Button} this
551          * @param {Roo.EventObject} e
552          */
553         "click" : true,
554          /**
555          * @event toggle
556          * After the button has been toggles
557          * @param {Roo.EventObject} e
558          * @param {boolean} pressed (also available as button.pressed)
559          */
560         "toggle" : true
561     });
562 };
563
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
565     html: false,
566     active: false,
567     weight: '',
568     size: '',
569     tag: 'button',
570     href: '',
571     disabled: false,
572     isClose: false,
573     glyphicon: '',
574     badge: '',
575     theme: 'default',
576     inverse: false,
577     
578     toggle: false,
579     ontext: 'ON',
580     offtext: 'OFF',
581     defaulton: true,
582     preventDefault: true,
583     removeClass: false,
584     name: false,
585     target: false,
586     
587     
588     pressed : null,
589      
590     
591     getAutoCreate : function(){
592         
593         var cfg = {
594             tag : 'button',
595             cls : 'roo-button',
596             html: ''
597         };
598         
599         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
601             this.tag = 'button';
602         } else {
603             cfg.tag = this.tag;
604         }
605         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606         
607         if (this.toggle == true) {
608             cfg={
609                 tag: 'div',
610                 cls: 'slider-frame roo-button',
611                 cn: [
612                     {
613                         tag: 'span',
614                         'data-on-text':'ON',
615                         'data-off-text':'OFF',
616                         cls: 'slider-button',
617                         html: this.offtext
618                     }
619                 ]
620             };
621             
622             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623                 cfg.cls += ' '+this.weight;
624             }
625             
626             return cfg;
627         }
628         
629         if (this.isClose) {
630             cfg.cls += ' close';
631             
632             cfg["aria-hidden"] = true;
633             
634             cfg.html = "&times;";
635             
636             return cfg;
637         }
638         
639          
640         if (this.theme==='default') {
641             cfg.cls = 'btn roo-button';
642             
643             //if (this.parentType != 'Navbar') {
644             this.weight = this.weight.length ?  this.weight : 'default';
645             //}
646             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647                 
648                 cfg.cls += ' btn-' + this.weight;
649             }
650         } else if (this.theme==='glow') {
651             
652             cfg.tag = 'a';
653             cfg.cls = 'btn-glow roo-button';
654             
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' ' + this.weight;
658             }
659         }
660    
661         
662         if (this.inverse) {
663             this.cls += ' inverse';
664         }
665         
666         
667         if (this.active) {
668             cfg.cls += ' active';
669         }
670         
671         if (this.disabled) {
672             cfg.disabled = 'disabled';
673         }
674         
675         if (this.items) {
676             Roo.log('changing to ul' );
677             cfg.tag = 'ul';
678             this.glyphicon = 'caret';
679         }
680         
681         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682          
683         //gsRoo.log(this.parentType);
684         if (this.parentType === 'Navbar' && !this.parent().bar) {
685             Roo.log('changing to li?');
686             
687             cfg.tag = 'li';
688             
689             cfg.cls = '';
690             cfg.cn =  [{
691                 tag : 'a',
692                 cls : 'roo-button',
693                 html : this.html,
694                 href : this.href || '#'
695             }];
696             if (this.menu) {
697                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
698                 cfg.cls += ' dropdown';
699             }   
700             
701             delete cfg.html;
702             
703         }
704         
705        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
706         
707         if (this.glyphicon) {
708             cfg.html = ' ' + cfg.html;
709             
710             cfg.cn = [
711                 {
712                     tag: 'span',
713                     cls: 'glyphicon glyphicon-' + this.glyphicon
714                 }
715             ];
716         }
717         
718         if (this.badge) {
719             cfg.html += ' ';
720             
721             cfg.tag = 'a';
722             
723 //            cfg.cls='btn roo-button';
724             
725             cfg.href=this.href;
726             
727             var value = cfg.html;
728             
729             if(this.glyphicon){
730                 value = {
731                             tag: 'span',
732                             cls: 'glyphicon glyphicon-' + this.glyphicon,
733                             html: this.html
734                         };
735                 
736             }
737             
738             cfg.cn = [
739                 value,
740                 {
741                     tag: 'span',
742                     cls: 'badge',
743                     html: this.badge
744                 }
745             ];
746             
747             cfg.html='';
748         }
749         
750         if (this.menu) {
751             cfg.cls += ' dropdown';
752             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
753         }
754         
755         if (cfg.tag !== 'a' && this.href !== '') {
756             throw "Tag must be a to set href.";
757         } else if (this.href.length > 0) {
758             cfg.href = this.href;
759         }
760         
761         if(this.removeClass){
762             cfg.cls = '';
763         }
764         
765         if(this.target){
766             cfg.target = this.target;
767         }
768         
769         return cfg;
770     },
771     initEvents: function() {
772        // Roo.log('init events?');
773 //        Roo.log(this.el.dom);
774         // add the menu...
775         
776         if (typeof (this.menu) != 'undefined') {
777             this.menu.parentType = this.xtype;
778             this.menu.triggerEl = this.el;
779             this.addxtype(Roo.apply({}, this.menu));
780         }
781
782
783        if (this.el.hasClass('roo-button')) {
784             this.el.on('click', this.onClick, this);
785        } else {
786             this.el.select('.roo-button').on('click', this.onClick, this);
787        }
788        
789        if(this.removeClass){
790            this.el.on('click', this.onClick, this);
791        }
792        
793        this.el.enableDisplayMode();
794         
795     },
796     onClick : function(e)
797     {
798         if (this.disabled) {
799             return;
800         }
801         
802         
803         Roo.log('button on click ');
804         if(this.preventDefault){
805             e.preventDefault();
806         }
807         if (this.pressed === true || this.pressed === false) {
808             this.pressed = !this.pressed;
809             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810             this.fireEvent('toggle', this, e, this.pressed);
811         }
812         
813         
814         this.fireEvent('click', this, e);
815     },
816     
817     /**
818      * Enables this button
819      */
820     enable : function()
821     {
822         this.disabled = false;
823         this.el.removeClass('disabled');
824     },
825     
826     /**
827      * Disable this button
828      */
829     disable : function()
830     {
831         this.disabled = true;
832         this.el.addClass('disabled');
833     },
834      /**
835      * sets the active state on/off, 
836      * @param {Boolean} state (optional) Force a particular state
837      */
838     setActive : function(v) {
839         
840         this.el[v ? 'addClass' : 'removeClass']('active');
841     },
842      /**
843      * toggles the current active state 
844      */
845     toggleActive : function()
846     {
847        var active = this.el.hasClass('active');
848        this.setActive(!active);
849        
850         
851     },
852     setText : function(str)
853     {
854         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
855     },
856     getText : function()
857     {
858         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859     },
860     hide: function() {
861        
862      
863         this.el.hide();   
864     },
865     show: function() {
866        
867         this.el.show();   
868     },
869     setWeight : function(str)
870     {
871           this.el.removeClass(this.weightClass);
872         this.el.addClass('btn-' + str);        
873     }
874     
875     
876 });
877
878  /*
879  * - LGPL
880  *
881  * column
882  * 
883  */
884
885 /**
886  * @class Roo.bootstrap.Column
887  * @extends Roo.bootstrap.Component
888  * Bootstrap Column class
889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
897  *
898  * 
899  * @cfg {Boolean} hidden (true|false) hide the element
900  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901  * @cfg {String} fa (ban|check|...) font awesome icon
902  * @cfg {Number} fasize (1|2|....) font awsome size
903
904  * @cfg {String} icon (info-sign|check|...) glyphicon name
905
906  * @cfg {String} html content of column.
907  * 
908  * @constructor
909  * Create a new Column
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.Column = function(config){
914     Roo.bootstrap.Column.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
918     
919     xs: false,
920     sm: false,
921     md: false,
922     lg: false,
923     xsoff: false,
924     smoff: false,
925     mdoff: false,
926     lgoff: false,
927     html: '',
928     offset: 0,
929     alert: false,
930     fa: false,
931     icon : false,
932     hidden : false,
933     fasize : 1,
934     
935     getAutoCreate : function(){
936         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
937         
938         cfg = {
939             tag: 'div',
940             cls: 'column'
941         };
942         
943         var settings=this;
944         ['xs','sm','md','lg'].map(function(size){
945             //Roo.log( size + ':' + settings[size]);
946             
947             if (settings[size+'off'] !== false) {
948                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
949             }
950             
951             if (settings[size] === false) {
952                 return;
953             }
954             
955             if (!settings[size]) { // 0 = hidden
956                 cfg.cls += ' hidden-' + size;
957                 return;
958             }
959             cfg.cls += ' col-' + size + '-' + settings[size];
960             
961         });
962         
963         if (this.hidden) {
964             cfg.cls += ' hidden';
965         }
966         
967         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968             cfg.cls +=' alert alert-' + this.alert;
969         }
970         
971         
972         if (this.html.length) {
973             cfg.html = this.html;
974         }
975         if (this.fa) {
976             var fasize = '';
977             if (this.fasize > 1) {
978                 fasize = ' fa-' + this.fasize + 'x';
979             }
980             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
981             
982             
983         }
984         if (this.icon) {
985             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
986         }
987         
988         return cfg;
989     }
990    
991 });
992
993  
994
995  /*
996  * - LGPL
997  *
998  * page container.
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Container
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Container class
1007  * @cfg {Boolean} jumbotron is it a jumbotron element
1008  * @cfg {String} html content of element
1009  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1011  * @cfg {String} header content of header (for panel)
1012  * @cfg {String} footer content of footer (for panel)
1013  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014  * @cfg {String} tag (header|aside|section) type of HTML tag.
1015  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016  * @cfg {String} fa font awesome icon
1017  * @cfg {String} icon (info-sign|check|...) glyphicon name
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {Boolean} expandable (true|false) default false
1020  * @cfg {Boolean} expanded (true|false) default true
1021  * @cfg {String} rheader contet on the right of header
1022  * @cfg {Boolean} clickable (true|false) default false
1023
1024  *     
1025  * @constructor
1026  * Create a new Container
1027  * @param {Object} config The config object
1028  */
1029
1030 Roo.bootstrap.Container = function(config){
1031     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035          /**
1036          * @event expand
1037          * After the panel has been expand
1038          * 
1039          * @param {Roo.bootstrap.Container} this
1040          */
1041         "expand" : true,
1042         /**
1043          * @event collapse
1044          * After the panel has been collapsed
1045          * 
1046          * @param {Roo.bootstrap.Container} this
1047          */
1048         "collapse" : true,
1049         /**
1050          * @event click
1051          * When a element is chick
1052          * @param {Roo.bootstrap.Container} this
1053          * @param {Roo.EventObject} e
1054          */
1055         "click" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1060     
1061     jumbotron : false,
1062     well: '',
1063     panel : '',
1064     header: '',
1065     footer : '',
1066     sticky: '',
1067     tag : false,
1068     alert : false,
1069     fa: false,
1070     icon : false,
1071     expandable : false,
1072     rheader : '',
1073     expanded : true,
1074     clickable: false,
1075   
1076      
1077     getChildContainer : function() {
1078         
1079         if(!this.el){
1080             return false;
1081         }
1082         
1083         if (this.panel.length) {
1084             return this.el.select('.panel-body',true).first();
1085         }
1086         
1087         return this.el;
1088     },
1089     
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : this.tag || 'div',
1095             html : '',
1096             cls : ''
1097         };
1098         if (this.jumbotron) {
1099             cfg.cls = 'jumbotron';
1100         }
1101         
1102         
1103         
1104         // - this is applied by the parent..
1105         //if (this.cls) {
1106         //    cfg.cls = this.cls + '';
1107         //}
1108         
1109         if (this.sticky.length) {
1110             
1111             var bd = Roo.get(document.body);
1112             if (!bd.hasClass('bootstrap-sticky')) {
1113                 bd.addClass('bootstrap-sticky');
1114                 Roo.select('html',true).setStyle('height', '100%');
1115             }
1116              
1117             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1118         }
1119         
1120         
1121         if (this.well.length) {
1122             switch (this.well) {
1123                 case 'lg':
1124                 case 'sm':
1125                     cfg.cls +=' well well-' +this.well;
1126                     break;
1127                 default:
1128                     cfg.cls +=' well';
1129                     break;
1130             }
1131         }
1132         
1133         if (this.hidden) {
1134             cfg.cls += ' hidden';
1135         }
1136         
1137         
1138         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139             cfg.cls +=' alert alert-' + this.alert;
1140         }
1141         
1142         var body = cfg;
1143         
1144         if (this.panel.length) {
1145             cfg.cls += ' panel panel-' + this.panel;
1146             cfg.cn = [];
1147             if (this.header.length) {
1148                 
1149                 var h = [];
1150                 
1151                 if(this.expandable){
1152                     
1153                     cfg.cls = cfg.cls + ' expandable';
1154                     
1155                     h.push({
1156                         tag: 'i',
1157                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1158                     });
1159                     
1160                 }
1161                 
1162                 h.push(
1163                     {
1164                         tag: 'span',
1165                         cls : 'panel-title',
1166                         html : (this.expandable ? '&nbsp;' : '') + this.header
1167                     },
1168                     {
1169                         tag: 'span',
1170                         cls: 'panel-header-right',
1171                         html: this.rheader
1172                     }
1173                 );
1174                 
1175                 cfg.cn.push({
1176                     cls : 'panel-heading',
1177                     style : this.expandable ? 'cursor: pointer' : '',
1178                     cn : h
1179                 });
1180                 
1181             }
1182             
1183             body = false;
1184             cfg.cn.push({
1185                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1186                 html : this.html
1187             });
1188             
1189             
1190             if (this.footer.length) {
1191                 cfg.cn.push({
1192                     cls : 'panel-footer',
1193                     html : this.footer
1194                     
1195                 });
1196             }
1197             
1198         }
1199         
1200         if (body) {
1201             body.html = this.html || cfg.html;
1202             // prefix with the icons..
1203             if (this.fa) {
1204                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1205             }
1206             if (this.icon) {
1207                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1208             }
1209             
1210             
1211         }
1212         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213             cfg.cls =  'container';
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() 
1220     {
1221         if(this.expandable){
1222             var headerEl = this.headerEl();
1223         
1224             if(headerEl){
1225                 headerEl.on('click', this.onToggleClick, this);
1226             }
1227         }
1228         
1229         if(this.clickable){
1230             this.el.on('click', this.onClick, this);
1231         }
1232         
1233     },
1234     
1235     onToggleClick : function()
1236     {
1237         var headerEl = this.headerEl();
1238         
1239         if(!headerEl){
1240             return;
1241         }
1242         
1243         if(this.expanded){
1244             this.collapse();
1245             return;
1246         }
1247         
1248         this.expand();
1249     },
1250     
1251     expand : function()
1252     {
1253         if(this.fireEvent('expand', this)) {
1254             
1255             this.expanded = true;
1256             
1257             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258             
1259             this.el.select('.panel-body',true).first().removeClass('hide');
1260             
1261             var toggleEl = this.toggleEl();
1262
1263             if(!toggleEl){
1264                 return;
1265             }
1266
1267             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1268         }
1269         
1270     },
1271     
1272     collapse : function()
1273     {
1274         if(this.fireEvent('collapse', this)) {
1275             
1276             this.expanded = false;
1277             
1278             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279             this.el.select('.panel-body',true).first().addClass('hide');
1280         
1281             var toggleEl = this.toggleEl();
1282
1283             if(!toggleEl){
1284                 return;
1285             }
1286
1287             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1288         }
1289     },
1290     
1291     toggleEl : function()
1292     {
1293         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1294             return;
1295         }
1296         
1297         return this.el.select('.panel-heading .fa',true).first();
1298     },
1299     
1300     headerEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading',true).first()
1307     },
1308     
1309     bodyEl : function()
1310     {
1311         if(!this.el || !this.panel.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-body',true).first()
1316     },
1317     
1318     titleEl : function()
1319     {
1320         if(!this.el || !this.panel.length || !this.header.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-title',true).first();
1325     },
1326     
1327     setTitle : function(v)
1328     {
1329         var titleEl = this.titleEl();
1330         
1331         if(!titleEl){
1332             return;
1333         }
1334         
1335         titleEl.dom.innerHTML = v;
1336     },
1337     
1338     getTitle : function()
1339     {
1340         
1341         var titleEl = this.titleEl();
1342         
1343         if(!titleEl){
1344             return '';
1345         }
1346         
1347         return titleEl.dom.innerHTML;
1348     },
1349     
1350     setRightTitle : function(v)
1351     {
1352         var t = this.el.select('.panel-header-right',true).first();
1353         
1354         if(!t){
1355             return;
1356         }
1357         
1358         t.dom.innerHTML = v;
1359     },
1360     
1361     onClick : function(e)
1362     {
1363         e.preventDefault();
1364         
1365         this.fireEvent('click', this, e);
1366     }
1367    
1368 });
1369
1370  /*
1371  * - LGPL
1372  *
1373  * image
1374  * 
1375  */
1376
1377
1378 /**
1379  * @class Roo.bootstrap.Img
1380  * @extends Roo.bootstrap.Component
1381  * Bootstrap Img class
1382  * @cfg {Boolean} imgResponsive false | true
1383  * @cfg {String} border rounded | circle | thumbnail
1384  * @cfg {String} src image source
1385  * @cfg {String} alt image alternative text
1386  * @cfg {String} href a tag href
1387  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388  * @cfg {String} xsUrl xs image source
1389  * @cfg {String} smUrl sm image source
1390  * @cfg {String} mdUrl md image source
1391  * @cfg {String} lgUrl lg image source
1392  * 
1393  * @constructor
1394  * Create a new Input
1395  * @param {Object} config The config object
1396  */
1397
1398 Roo.bootstrap.Img = function(config){
1399     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1400     
1401     this.addEvents({
1402         // img events
1403         /**
1404          * @event click
1405          * The img click event for the img.
1406          * @param {Roo.EventObject} e
1407          */
1408         "click" : true
1409     });
1410 };
1411
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1413     
1414     imgResponsive: true,
1415     border: '',
1416     src: 'about:blank',
1417     href: false,
1418     target: false,
1419     xsUrl: '',
1420     smUrl: '',
1421     mdUrl: '',
1422     lgUrl: '',
1423
1424     getAutoCreate : function()
1425     {   
1426         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427             return this.createSingleImg();
1428         }
1429         
1430         var cfg = {
1431             tag: 'div',
1432             cls: 'roo-image-responsive-group',
1433             cn: []
1434         };
1435         var _this = this;
1436         
1437         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438             
1439             if(!_this[size + 'Url']){
1440                 return;
1441             }
1442             
1443             var img = {
1444                 tag: 'img',
1445                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446                 html: _this.html || cfg.html,
1447                 src: _this[size + 'Url']
1448             };
1449             
1450             img.cls += ' roo-image-responsive-' + size;
1451             
1452             var s = ['xs', 'sm', 'md', 'lg'];
1453             
1454             s.splice(s.indexOf(size), 1);
1455             
1456             Roo.each(s, function(ss){
1457                 img.cls += ' hidden-' + ss;
1458             });
1459             
1460             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461                 cfg.cls += ' img-' + _this.border;
1462             }
1463             
1464             if(_this.alt){
1465                 cfg.alt = _this.alt;
1466             }
1467             
1468             if(_this.href){
1469                 var a = {
1470                     tag: 'a',
1471                     href: _this.href,
1472                     cn: [
1473                         img
1474                     ]
1475                 };
1476
1477                 if(this.target){
1478                     a.target = _this.target;
1479                 }
1480             }
1481             
1482             cfg.cn.push((_this.href) ? a : img);
1483             
1484         });
1485         
1486         return cfg;
1487     },
1488     
1489     createSingleImg : function()
1490     {
1491         var cfg = {
1492             tag: 'img',
1493             cls: (this.imgResponsive) ? 'img-responsive' : '',
1494             html : null,
1495             src : 'about:blank'  // just incase src get's set to undefined?!?
1496         };
1497         
1498         cfg.html = this.html || cfg.html;
1499         
1500         cfg.src = this.src || cfg.src;
1501         
1502         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503             cfg.cls += ' img-' + this.border;
1504         }
1505         
1506         if(this.alt){
1507             cfg.alt = this.alt;
1508         }
1509         
1510         if(this.href){
1511             var a = {
1512                 tag: 'a',
1513                 href: this.href,
1514                 cn: [
1515                     cfg
1516                 ]
1517             };
1518             
1519             if(this.target){
1520                 a.target = this.target;
1521             }
1522             
1523         }
1524         
1525         return (this.href) ? a : cfg;
1526     },
1527     
1528     initEvents: function() 
1529     {
1530         if(!this.href){
1531             this.el.on('click', this.onClick, this);
1532         }
1533         
1534     },
1535     
1536     onClick : function(e)
1537     {
1538         Roo.log('img onclick');
1539         this.fireEvent('click', this, e);
1540     },
1541     /**
1542      * Sets the url of the image - used to update it
1543      * @param {String} url the url of the image
1544      */
1545     
1546     setSrc : function(url)
1547     {
1548         this.src =  url;
1549         
1550         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551             this.el.dom.src =  url;
1552             return;
1553         }
1554         
1555         this.el.select('img', true).first().dom.src =  url;
1556     }
1557     
1558     
1559    
1560 });
1561
1562  /*
1563  * - LGPL
1564  *
1565  * image
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Link
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Link Class
1574  * @cfg {String} alt image alternative text
1575  * @cfg {String} href a tag href
1576  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577  * @cfg {String} html the content of the link.
1578  * @cfg {String} anchor name for the anchor link
1579  * @cfg {String} fa - favicon
1580
1581  * @cfg {Boolean} preventDefault (true | false) default false
1582
1583  * 
1584  * @constructor
1585  * Create a new Input
1586  * @param {Object} config The config object
1587  */
1588
1589 Roo.bootstrap.Link = function(config){
1590     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1591     
1592     this.addEvents({
1593         // img events
1594         /**
1595          * @event click
1596          * The img click event for the img.
1597          * @param {Roo.EventObject} e
1598          */
1599         "click" : true
1600     });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1604     
1605     href: false,
1606     target: false,
1607     preventDefault: false,
1608     anchor : false,
1609     alt : false,
1610     fa: false,
1611
1612
1613     getAutoCreate : function()
1614     {
1615         var html = this.html || '';
1616         
1617         if (this.fa !== false) {
1618             html = '<i class="fa fa-' + this.fa + '"></i>';
1619         }
1620         var cfg = {
1621             tag: 'a'
1622         };
1623         // anchor's do not require html/href...
1624         if (this.anchor === false) {
1625             cfg.html = html;
1626             cfg.href = this.href || '#';
1627         } else {
1628             cfg.name = this.anchor;
1629             if (this.html !== false || this.fa !== false) {
1630                 cfg.html = html;
1631             }
1632             if (this.href !== false) {
1633                 cfg.href = this.href;
1634             }
1635         }
1636         
1637         if(this.alt !== false){
1638             cfg.alt = this.alt;
1639         }
1640         
1641         
1642         if(this.target !== false) {
1643             cfg.target = this.target;
1644         }
1645         
1646         return cfg;
1647     },
1648     
1649     initEvents: function() {
1650         
1651         if(!this.href || this.preventDefault){
1652             this.el.on('click', this.onClick, this);
1653         }
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         if(this.preventDefault){
1659             e.preventDefault();
1660         }
1661         //Roo.log('img onclick');
1662         this.fireEvent('click', this, e);
1663     }
1664    
1665 });
1666
1667  /*
1668  * - LGPL
1669  *
1670  * header
1671  * 
1672  */
1673
1674 /**
1675  * @class Roo.bootstrap.Header
1676  * @extends Roo.bootstrap.Component
1677  * Bootstrap Header class
1678  * @cfg {String} html content of header
1679  * @cfg {Number} level (1|2|3|4|5|6) default 1
1680  * 
1681  * @constructor
1682  * Create a new Header
1683  * @param {Object} config The config object
1684  */
1685
1686
1687 Roo.bootstrap.Header  = function(config){
1688     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1689 };
1690
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1692     
1693     //href : false,
1694     html : false,
1695     level : 1,
1696     
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         
1702         
1703         var cfg = {
1704             tag: 'h' + (1 *this.level),
1705             html: this.html || ''
1706         } ;
1707         
1708         return cfg;
1709     }
1710    
1711 });
1712
1713  
1714
1715  /*
1716  * Based on:
1717  * Ext JS Library 1.1.1
1718  * Copyright(c) 2006-2007, Ext JS, LLC.
1719  *
1720  * Originally Released Under LGPL - original licence link has changed is not relivant.
1721  *
1722  * Fork - LGPL
1723  * <script type="text/javascript">
1724  */
1725  
1726 /**
1727  * @class Roo.bootstrap.MenuMgr
1728  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1729  * @singleton
1730  */
1731 Roo.bootstrap.MenuMgr = function(){
1732    var menus, active, groups = {}, attached = false, lastShow = new Date();
1733
1734    // private - called when first menu is created
1735    function init(){
1736        menus = {};
1737        active = new Roo.util.MixedCollection();
1738        Roo.get(document).addKeyListener(27, function(){
1739            if(active.length > 0){
1740                hideAll();
1741            }
1742        });
1743    }
1744
1745    // private
1746    function hideAll(){
1747        if(active && active.length > 0){
1748            var c = active.clone();
1749            c.each(function(m){
1750                m.hide();
1751            });
1752        }
1753    }
1754
1755    // private
1756    function onHide(m){
1757        active.remove(m);
1758        if(active.length < 1){
1759            Roo.get(document).un("mouseup", onMouseDown);
1760             
1761            attached = false;
1762        }
1763    }
1764
1765    // private
1766    function onShow(m){
1767        var last = active.last();
1768        lastShow = new Date();
1769        active.add(m);
1770        if(!attached){
1771           Roo.get(document).on("mouseup", onMouseDown);
1772            
1773            attached = true;
1774        }
1775        if(m.parentMenu){
1776           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777           m.parentMenu.activeChild = m;
1778        }else if(last && last.isVisible()){
1779           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1780        }
1781    }
1782
1783    // private
1784    function onBeforeHide(m){
1785        if(m.activeChild){
1786            m.activeChild.hide();
1787        }
1788        if(m.autoHideTimer){
1789            clearTimeout(m.autoHideTimer);
1790            delete m.autoHideTimer;
1791        }
1792    }
1793
1794    // private
1795    function onBeforeShow(m){
1796        var pm = m.parentMenu;
1797        if(!pm && !m.allowOtherMenus){
1798            hideAll();
1799        }else if(pm && pm.activeChild && active != m){
1800            pm.activeChild.hide();
1801        }
1802    }
1803
1804    // private this should really trigger on mouseup..
1805    function onMouseDown(e){
1806         Roo.log("on Mouse Up");
1807         
1808         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809             Roo.log("MenuManager hideAll");
1810             hideAll();
1811             e.stopEvent();
1812         }
1813         
1814         
1815    }
1816
1817    // private
1818    function onBeforeCheck(mi, state){
1819        if(state){
1820            var g = groups[mi.group];
1821            for(var i = 0, l = g.length; i < l; i++){
1822                if(g[i] != mi){
1823                    g[i].setChecked(false);
1824                }
1825            }
1826        }
1827    }
1828
1829    return {
1830
1831        /**
1832         * Hides all menus that are currently visible
1833         */
1834        hideAll : function(){
1835             hideAll();  
1836        },
1837
1838        // private
1839        register : function(menu){
1840            if(!menus){
1841                init();
1842            }
1843            menus[menu.id] = menu;
1844            menu.on("beforehide", onBeforeHide);
1845            menu.on("hide", onHide);
1846            menu.on("beforeshow", onBeforeShow);
1847            menu.on("show", onShow);
1848            var g = menu.group;
1849            if(g && menu.events["checkchange"]){
1850                if(!groups[g]){
1851                    groups[g] = [];
1852                }
1853                groups[g].push(menu);
1854                menu.on("checkchange", onCheck);
1855            }
1856        },
1857
1858         /**
1859          * Returns a {@link Roo.menu.Menu} object
1860          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861          * be used to generate and return a new Menu instance.
1862          */
1863        get : function(menu){
1864            if(typeof menu == "string"){ // menu id
1865                return menus[menu];
1866            }else if(menu.events){  // menu instance
1867                return menu;
1868            }
1869            /*else if(typeof menu.length == 'number'){ // array of menu items?
1870                return new Roo.bootstrap.Menu({items:menu});
1871            }else{ // otherwise, must be a config
1872                return new Roo.bootstrap.Menu(menu);
1873            }
1874            */
1875            return false;
1876        },
1877
1878        // private
1879        unregister : function(menu){
1880            delete menus[menu.id];
1881            menu.un("beforehide", onBeforeHide);
1882            menu.un("hide", onHide);
1883            menu.un("beforeshow", onBeforeShow);
1884            menu.un("show", onShow);
1885            var g = menu.group;
1886            if(g && menu.events["checkchange"]){
1887                groups[g].remove(menu);
1888                menu.un("checkchange", onCheck);
1889            }
1890        },
1891
1892        // private
1893        registerCheckable : function(menuItem){
1894            var g = menuItem.group;
1895            if(g){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menuItem);
1900                menuItem.on("beforecheckchange", onBeforeCheck);
1901            }
1902        },
1903
1904        // private
1905        unregisterCheckable : function(menuItem){
1906            var g = menuItem.group;
1907            if(g){
1908                groups[g].remove(menuItem);
1909                menuItem.un("beforecheckchange", onBeforeCheck);
1910            }
1911        }
1912    };
1913 }();/*
1914  * - LGPL
1915  *
1916  * menu
1917  * 
1918  */
1919
1920 /**
1921  * @class Roo.bootstrap.Menu
1922  * @extends Roo.bootstrap.Component
1923  * Bootstrap Menu class - container for MenuItems
1924  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1926  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1927  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1928  * 
1929  * @constructor
1930  * Create a new Menu
1931  * @param {Object} config The config object
1932  */
1933
1934
1935 Roo.bootstrap.Menu = function(config){
1936     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937     if (this.registerMenu && this.type != 'treeview')  {
1938         Roo.bootstrap.MenuMgr.register(this);
1939     }
1940     this.addEvents({
1941         /**
1942          * @event beforeshow
1943          * Fires before this menu is displayed
1944          * @param {Roo.menu.Menu} this
1945          */
1946         beforeshow : true,
1947         /**
1948          * @event beforehide
1949          * Fires before this menu is hidden
1950          * @param {Roo.menu.Menu} this
1951          */
1952         beforehide : true,
1953         /**
1954          * @event show
1955          * Fires after this menu is displayed
1956          * @param {Roo.menu.Menu} this
1957          */
1958         show : true,
1959         /**
1960          * @event hide
1961          * Fires after this menu is hidden
1962          * @param {Roo.menu.Menu} this
1963          */
1964         hide : true,
1965         /**
1966          * @event click
1967          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968          * @param {Roo.menu.Menu} this
1969          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970          * @param {Roo.EventObject} e
1971          */
1972         click : true,
1973         /**
1974          * @event mouseover
1975          * Fires when the mouse is hovering over this menu
1976          * @param {Roo.menu.Menu} this
1977          * @param {Roo.EventObject} e
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          */
1980         mouseover : true,
1981         /**
1982          * @event mouseout
1983          * Fires when the mouse exits this menu
1984          * @param {Roo.menu.Menu} this
1985          * @param {Roo.EventObject} e
1986          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1987          */
1988         mouseout : true,
1989         /**
1990          * @event itemclick
1991          * Fires when a menu item contained in this menu is clicked
1992          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993          * @param {Roo.EventObject} e
1994          */
1995         itemclick: true
1996     });
1997     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1998 };
1999
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2001     
2002    /// html : false,
2003     //align : '',
2004     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2005     type: false,
2006     /**
2007      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008      */
2009     registerMenu : true,
2010     
2011     menuItems :false, // stores the menu items..
2012     
2013     hidden:true,
2014         
2015     parentMenu : false,
2016     
2017     stopEvent : true,
2018     
2019     isLink : false,
2020     
2021     getChildContainer : function() {
2022         return this.el;  
2023     },
2024     
2025     getAutoCreate : function(){
2026          
2027         //if (['right'].indexOf(this.align)!==-1) {
2028         //    cfg.cn[1].cls += ' pull-right'
2029         //}
2030         
2031         
2032         var cfg = {
2033             tag : 'ul',
2034             cls : 'dropdown-menu' ,
2035             style : 'z-index:1000'
2036             
2037         };
2038         
2039         if (this.type === 'submenu') {
2040             cfg.cls = 'submenu active';
2041         }
2042         if (this.type === 'treeview') {
2043             cfg.cls = 'treeview-menu';
2044         }
2045         
2046         return cfg;
2047     },
2048     initEvents : function() {
2049         
2050        // Roo.log("ADD event");
2051        // Roo.log(this.triggerEl.dom);
2052         
2053         this.triggerEl.on('click', this.onTriggerClick, this);
2054         
2055         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056         
2057         this.triggerEl.addClass('dropdown-toggle');
2058         
2059         if (Roo.isTouch) {
2060             this.el.on('touchstart'  , this.onTouch, this);
2061         }
2062         this.el.on('click' , this.onClick, this);
2063
2064         this.el.on("mouseover", this.onMouseOver, this);
2065         this.el.on("mouseout", this.onMouseOut, this);
2066         
2067     },
2068     
2069     findTargetItem : function(e)
2070     {
2071         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2072         if(!t){
2073             return false;
2074         }
2075         //Roo.log(t);         Roo.log(t.id);
2076         if(t && t.id){
2077             //Roo.log(this.menuitems);
2078             return this.menuitems.get(t.id);
2079             
2080             //return this.items.get(t.menuItemId);
2081         }
2082         
2083         return false;
2084     },
2085     
2086     onTouch : function(e) 
2087     {
2088         Roo.log("menu.onTouch");
2089         //e.stopEvent(); this make the user popdown broken
2090         this.onClick(e);
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log("menu.onClick");
2096         
2097         var t = this.findTargetItem(e);
2098         if(!t || t.isContainer){
2099             return;
2100         }
2101         Roo.log(e);
2102         /*
2103         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2104             if(t == this.activeItem && t.shouldDeactivate(e)){
2105                 this.activeItem.deactivate();
2106                 delete this.activeItem;
2107                 return;
2108             }
2109             if(t.canActivate){
2110                 this.setActiveItem(t, true);
2111             }
2112             return;
2113             
2114             
2115         }
2116         */
2117        
2118         Roo.log('pass click event');
2119         
2120         t.onClick(e);
2121         
2122         this.fireEvent("click", this, t, e);
2123         
2124         var _this = this;
2125         
2126         (function() { _this.hide(); }).defer(100);
2127     },
2128     
2129     onMouseOver : function(e){
2130         var t  = this.findTargetItem(e);
2131         //Roo.log(t);
2132         //if(t){
2133         //    if(t.canActivate && !t.disabled){
2134         //        this.setActiveItem(t, true);
2135         //    }
2136         //}
2137         
2138         this.fireEvent("mouseover", this, e, t);
2139     },
2140     isVisible : function(){
2141         return !this.hidden;
2142     },
2143      onMouseOut : function(e){
2144         var t  = this.findTargetItem(e);
2145         
2146         //if(t ){
2147         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2148         //        this.activeItem.deactivate();
2149         //        delete this.activeItem;
2150         //    }
2151         //}
2152         this.fireEvent("mouseout", this, e, t);
2153     },
2154     
2155     
2156     /**
2157      * Displays this menu relative to another element
2158      * @param {String/HTMLElement/Roo.Element} element The element to align to
2159      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2160      * the element (defaults to this.defaultAlign)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     show : function(el, pos, parentMenu){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         this.fireEvent("beforeshow", this);
2169         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2170     },
2171      /**
2172      * Displays this menu at a specific xy position
2173      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2174      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2175      */
2176     showAt : function(xy, parentMenu, /* private: */_e){
2177         this.parentMenu = parentMenu;
2178         if(!this.el){
2179             this.render();
2180         }
2181         if(_e !== false){
2182             this.fireEvent("beforeshow", this);
2183             //xy = this.el.adjustForConstraints(xy);
2184         }
2185         
2186         //this.el.show();
2187         this.hideMenuItems();
2188         this.hidden = false;
2189         this.triggerEl.addClass('open');
2190         
2191         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2192             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2193         }
2194         
2195         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2196             this.el.setXY(xy);
2197         }
2198         
2199         this.focus();
2200         this.fireEvent("show", this);
2201     },
2202     
2203     focus : function(){
2204         return;
2205         if(!this.hidden){
2206             this.doFocus.defer(50, this);
2207         }
2208     },
2209
2210     doFocus : function(){
2211         if(!this.hidden){
2212             this.focusEl.focus();
2213         }
2214     },
2215
2216     /**
2217      * Hides this menu and optionally all parent menus
2218      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2219      */
2220     hide : function(deep)
2221     {
2222         
2223         this.hideMenuItems();
2224         if(this.el && this.isVisible()){
2225             this.fireEvent("beforehide", this);
2226             if(this.activeItem){
2227                 this.activeItem.deactivate();
2228                 this.activeItem = null;
2229             }
2230             this.triggerEl.removeClass('open');;
2231             this.hidden = true;
2232             this.fireEvent("hide", this);
2233         }
2234         if(deep === true && this.parentMenu){
2235             this.parentMenu.hide(true);
2236         }
2237     },
2238     
2239     onTriggerClick : function(e)
2240     {
2241         Roo.log('trigger click');
2242         
2243         var target = e.getTarget();
2244         
2245         Roo.log(target.nodeName.toLowerCase());
2246         
2247         if(target.nodeName.toLowerCase() === 'i'){
2248             e.preventDefault();
2249         }
2250         
2251     },
2252     
2253     onTriggerPress  : function(e)
2254     {
2255         Roo.log('trigger press');
2256         //Roo.log(e.getTarget());
2257        // Roo.log(this.triggerEl.dom);
2258        
2259         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2260         var pel = Roo.get(e.getTarget());
2261         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2262             Roo.log('is treeview or dropdown?');
2263             return;
2264         }
2265         
2266         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2267             return;
2268         }
2269         
2270         if (this.isVisible()) {
2271             Roo.log('hide');
2272             this.hide();
2273         } else {
2274             Roo.log('show');
2275             this.show(this.triggerEl, false, false);
2276         }
2277         
2278         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2279             e.stopEvent();
2280         }
2281         
2282     },
2283        
2284     
2285     hideMenuItems : function()
2286     {
2287         Roo.log("hide Menu Items");
2288         if (!this.el) { 
2289             return;
2290         }
2291         //$(backdrop).remove()
2292         this.el.select('.open',true).each(function(aa) {
2293             
2294             aa.removeClass('open');
2295           //var parent = getParent($(this))
2296           //var relatedTarget = { relatedTarget: this }
2297           
2298            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2299           //if (e.isDefaultPrevented()) return
2300            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2301         });
2302     },
2303     addxtypeChild : function (tree, cntr) {
2304         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2305           
2306         this.menuitems.add(comp);
2307         return comp;
2308
2309     },
2310     getEl : function()
2311     {
2312         Roo.log(this.el);
2313         return this.el;
2314     }
2315 });
2316
2317  
2318  /*
2319  * - LGPL
2320  *
2321  * menu item
2322  * 
2323  */
2324
2325
2326 /**
2327  * @class Roo.bootstrap.MenuItem
2328  * @extends Roo.bootstrap.Component
2329  * Bootstrap MenuItem class
2330  * @cfg {String} html the menu label
2331  * @cfg {String} href the link
2332  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2333  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2334  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2335  * @cfg {String} fa favicon to show on left of menu item.
2336  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2337  * 
2338  * 
2339  * @constructor
2340  * Create a new MenuItem
2341  * @param {Object} config The config object
2342  */
2343
2344
2345 Roo.bootstrap.MenuItem = function(config){
2346     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2347     this.addEvents({
2348         // raw events
2349         /**
2350          * @event click
2351          * The raw click event for the entire grid.
2352          * @param {Roo.bootstrap.MenuItem} this
2353          * @param {Roo.EventObject} e
2354          */
2355         "click" : true
2356     });
2357 };
2358
2359 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2360     
2361     href : false,
2362     html : false,
2363     preventDefault: false,
2364     isContainer : false,
2365     active : false,
2366     fa: false,
2367     
2368     getAutoCreate : function(){
2369         
2370         if(this.isContainer){
2371             return {
2372                 tag: 'li',
2373                 cls: 'dropdown-menu-item'
2374             };
2375         }
2376         var ctag = {
2377             tag: 'span',
2378             html: 'Link'
2379         };
2380         
2381         var anc = {
2382             tag : 'a',
2383             href : '#',
2384             cn : [  ]
2385         };
2386         
2387         if (this.fa !== false) {
2388             anc.cn.push({
2389                 tag : 'i',
2390                 cls : 'fa fa-' + this.fa
2391             });
2392         }
2393         
2394         anc.cn.push(ctag);
2395         
2396         
2397         var cfg= {
2398             tag: 'li',
2399             cls: 'dropdown-menu-item',
2400             cn: [ anc ]
2401         };
2402         if (this.parent().type == 'treeview') {
2403             cfg.cls = 'treeview-menu';
2404         }
2405         if (this.active) {
2406             cfg.cls += ' active';
2407         }
2408         
2409         
2410         
2411         anc.href = this.href || cfg.cn[0].href ;
2412         ctag.html = this.html || cfg.cn[0].html ;
2413         return cfg;
2414     },
2415     
2416     initEvents: function()
2417     {
2418         if (this.parent().type == 'treeview') {
2419             this.el.select('a').on('click', this.onClick, this);
2420         }
2421         if (this.menu) {
2422             this.menu.parentType = this.xtype;
2423             this.menu.triggerEl = this.el;
2424             this.menu = this.addxtype(Roo.apply({}, this.menu));
2425         }
2426         
2427     },
2428     onClick : function(e)
2429     {
2430         Roo.log('item on click ');
2431         
2432         if(this.preventDefault){
2433             e.preventDefault();
2434         }
2435         //this.parent().hideMenuItems();
2436         
2437         this.fireEvent('click', this, e);
2438     },
2439     getEl : function()
2440     {
2441         return this.el;
2442     } 
2443 });
2444
2445  
2446
2447  /*
2448  * - LGPL
2449  *
2450  * menu separator
2451  * 
2452  */
2453
2454
2455 /**
2456  * @class Roo.bootstrap.MenuSeparator
2457  * @extends Roo.bootstrap.Component
2458  * Bootstrap MenuSeparator class
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuSeparator = function(config){
2467     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2468 };
2469
2470 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2471     
2472     getAutoCreate : function(){
2473         var cfg = {
2474             cls: 'divider',
2475             tag : 'li'
2476         };
2477         
2478         return cfg;
2479     }
2480    
2481 });
2482
2483  
2484
2485  
2486 /*
2487 * Licence: LGPL
2488 */
2489
2490 /**
2491  * @class Roo.bootstrap.Modal
2492  * @extends Roo.bootstrap.Component
2493  * Bootstrap Modal class
2494  * @cfg {String} title Title of dialog
2495  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2496  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2497  * @cfg {Boolean} specificTitle default false
2498  * @cfg {Array} buttons Array of buttons or standard button set..
2499  * @cfg {String} buttonPosition (left|right|center) default right
2500  * @cfg {Boolean} animate default true
2501  * @cfg {Boolean} allow_close default true
2502  * @cfg {Boolean} fitwindow default false
2503  * @cfg {String} size (sm|lg) default empty
2504  *
2505  *
2506  * @constructor
2507  * Create a new Modal Dialog
2508  * @param {Object} config The config object
2509  */
2510
2511 Roo.bootstrap.Modal = function(config){
2512     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2513     this.addEvents({
2514         // raw events
2515         /**
2516          * @event btnclick
2517          * The raw btnclick event for the button
2518          * @param {Roo.EventObject} e
2519          */
2520         "btnclick" : true,
2521         /**
2522          * @event resize
2523          * Fire when dialog resize
2524          * @param {Roo.bootstrap.Modal} this
2525          * @param {Roo.EventObject} e
2526          */
2527         "resize" : true
2528     });
2529     this.buttons = this.buttons || [];
2530
2531     if (this.tmpl) {
2532         this.tmpl = Roo.factory(this.tmpl);
2533     }
2534
2535 };
2536
2537 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2538
2539     title : 'test dialog',
2540
2541     buttons : false,
2542
2543     // set on load...
2544
2545     html: false,
2546
2547     tmp: false,
2548
2549     specificTitle: false,
2550
2551     buttonPosition: 'right',
2552
2553     allow_close : true,
2554
2555     animate : true,
2556
2557     fitwindow: false,
2558
2559
2560      // private
2561     dialogEl: false,
2562     bodyEl:  false,
2563     footerEl:  false,
2564     titleEl:  false,
2565     closeEl:  false,
2566
2567     size: '',
2568
2569
2570     onRender : function(ct, position)
2571     {
2572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2573
2574         if(!this.el){
2575             var cfg = Roo.apply({},  this.getAutoCreate());
2576             cfg.id = Roo.id();
2577             //if(!cfg.name){
2578             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2579             //}
2580             //if (!cfg.name.length) {
2581             //    delete cfg.name;
2582            // }
2583             if (this.cls) {
2584                 cfg.cls += ' ' + this.cls;
2585             }
2586             if (this.style) {
2587                 cfg.style = this.style;
2588             }
2589             this.el = Roo.get(document.body).createChild(cfg, position);
2590         }
2591         //var type = this.el.dom.type;
2592
2593
2594         if(this.tabIndex !== undefined){
2595             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2596         }
2597
2598         this.dialogEl = this.el.select('.modal-dialog',true).first();
2599         this.bodyEl = this.el.select('.modal-body',true).first();
2600         this.closeEl = this.el.select('.modal-header .close', true).first();
2601         this.headerEl = this.el.select('.modal-header',true).first();
2602         this.titleEl = this.el.select('.modal-title',true).first();
2603         this.footerEl = this.el.select('.modal-footer',true).first();
2604
2605         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2606         this.maskEl.enableDisplayMode("block");
2607         this.maskEl.hide();
2608         //this.el.addClass("x-dlg-modal");
2609
2610         if (this.buttons.length) {
2611             Roo.each(this.buttons, function(bb) {
2612                 var b = Roo.apply({}, bb);
2613                 b.xns = b.xns || Roo.bootstrap;
2614                 b.xtype = b.xtype || 'Button';
2615                 if (typeof(b.listeners) == 'undefined') {
2616                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2617                 }
2618
2619                 var btn = Roo.factory(b);
2620
2621                 btn.render(this.el.select('.modal-footer div').first());
2622
2623             },this);
2624         }
2625         // render the children.
2626         var nitems = [];
2627
2628         if(typeof(this.items) != 'undefined'){
2629             var items = this.items;
2630             delete this.items;
2631
2632             for(var i =0;i < items.length;i++) {
2633                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2634             }
2635         }
2636
2637         this.items = nitems;
2638
2639         // where are these used - they used to be body/close/footer
2640
2641
2642         this.initEvents();
2643         //this.el.addClass([this.fieldClass, this.cls]);
2644
2645     },
2646
2647     getAutoCreate : function(){
2648
2649
2650         var bdy = {
2651                 cls : 'modal-body',
2652                 html : this.html || ''
2653         };
2654
2655         var title = {
2656             tag: 'h4',
2657             cls : 'modal-title',
2658             html : this.title
2659         };
2660
2661         if(this.specificTitle){
2662             title = this.title;
2663
2664         };
2665
2666         var header = [];
2667         if (this.allow_close) {
2668             header.push({
2669                 tag: 'button',
2670                 cls : 'close',
2671                 html : '&times'
2672             });
2673         }
2674
2675         header.push(title);
2676
2677         var size = '';
2678
2679         if(this.size.length){
2680             size = 'modal-' + this.size;
2681         }
2682
2683         var modal = {
2684             cls: "modal",
2685             style : 'display: none',
2686             cn : [
2687                 {
2688                     cls: "modal-dialog " + size,
2689                     cn : [
2690                         {
2691                             cls : "modal-content",
2692                             cn : [
2693                                 {
2694                                     cls : 'modal-header',
2695                                     cn : header
2696                                 },
2697                                 bdy,
2698                                 {
2699                                     cls : 'modal-footer',
2700                                     cn : [
2701                                         {
2702                                             tag: 'div',
2703                                             cls: 'btn-' + this.buttonPosition
2704                                         }
2705                                     ]
2706
2707                                 }
2708
2709
2710                             ]
2711
2712                         }
2713                     ]
2714
2715                 }
2716             ]
2717         };
2718
2719         if(this.animate){
2720             modal.cls += ' fade';
2721         }
2722
2723         return modal;
2724
2725     },
2726     getChildContainer : function() {
2727
2728          return this.bodyEl;
2729
2730     },
2731     getButtonContainer : function() {
2732          return this.el.select('.modal-footer div',true).first();
2733
2734     },
2735     initEvents : function()
2736     {
2737         if (this.allow_close) {
2738             this.closeEl.on('click', this.hide, this);
2739         }
2740         Roo.EventManager.onWindowResize(this.resize, this, true);
2741
2742
2743     },
2744
2745     resize : function()
2746     {
2747         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2748         if (this.fitwindow) {
2749             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2750             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2751             this.setSize(w,h);
2752         }
2753     },
2754
2755     setSize : function(w,h)
2756     {
2757         if (!w && !h) {
2758             return;
2759         }
2760         this.resizeTo(w,h);
2761     },
2762
2763     show : function() {
2764
2765         if (!this.rendered) {
2766             this.render();
2767         }
2768
2769         this.el.setStyle('display', 'block');
2770
2771         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2772             var _this = this;
2773             (function(){
2774                 this.el.addClass('in');
2775             }).defer(50, this);
2776         }else{
2777             this.el.addClass('in');
2778
2779         }
2780
2781         // not sure how we can show data in here..
2782         //if (this.tmpl) {
2783         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2784         //}
2785
2786         Roo.get(document.body).addClass("x-body-masked");
2787         
2788         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2789         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2790         this.maskEl.show();
2791         
2792         this.resize();
2793         
2794         this.fireEvent('show', this);
2795
2796         // set zindex here - otherwise it appears to be ignored...
2797         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2798
2799         (function () {
2800             this.items.forEach( function(e) {
2801                 e.layout ? e.layout() : false;
2802
2803             });
2804         }).defer(100,this);
2805
2806     },
2807     hide : function()
2808     {
2809         if(this.fireEvent("beforehide", this) !== false){
2810             this.maskEl.hide();
2811             Roo.get(document.body).removeClass("x-body-masked");
2812             this.el.removeClass('in');
2813             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2814
2815             if(this.animate){ // why
2816                 var _this = this;
2817                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2818             }else{
2819                 this.el.setStyle('display', 'none');
2820             }
2821             this.fireEvent('hide', this);
2822         }
2823     },
2824
2825     addButton : function(str, cb)
2826     {
2827
2828
2829         var b = Roo.apply({}, { html : str } );
2830         b.xns = b.xns || Roo.bootstrap;
2831         b.xtype = b.xtype || 'Button';
2832         if (typeof(b.listeners) == 'undefined') {
2833             b.listeners = { click : cb.createDelegate(this)  };
2834         }
2835
2836         var btn = Roo.factory(b);
2837
2838         btn.render(this.el.select('.modal-footer div').first());
2839
2840         return btn;
2841
2842     },
2843
2844     setDefaultButton : function(btn)
2845     {
2846         //this.el.select('.modal-footer').()
2847     },
2848     diff : false,
2849
2850     resizeTo: function(w,h)
2851     {
2852         // skip.. ?? why??
2853
2854         this.dialogEl.setWidth(w);
2855         if (this.diff === false) {
2856             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2857         }
2858
2859         this.bodyEl.setHeight(h-this.diff);
2860
2861         this.fireEvent('resize', this);
2862
2863     },
2864     setContentSize  : function(w, h)
2865     {
2866
2867     },
2868     onButtonClick: function(btn,e)
2869     {
2870         //Roo.log([a,b,c]);
2871         this.fireEvent('btnclick', btn.name, e);
2872     },
2873      /**
2874      * Set the title of the Dialog
2875      * @param {String} str new Title
2876      */
2877     setTitle: function(str) {
2878         this.titleEl.dom.innerHTML = str;
2879     },
2880     /**
2881      * Set the body of the Dialog
2882      * @param {String} str new Title
2883      */
2884     setBody: function(str) {
2885         this.bodyEl.dom.innerHTML = str;
2886     },
2887     /**
2888      * Set the body of the Dialog using the template
2889      * @param {Obj} data - apply this data to the template and replace the body contents.
2890      */
2891     applyBody: function(obj)
2892     {
2893         if (!this.tmpl) {
2894             Roo.log("Error - using apply Body without a template");
2895             //code
2896         }
2897         this.tmpl.overwrite(this.bodyEl, obj);
2898     }
2899
2900 });
2901
2902
2903 Roo.apply(Roo.bootstrap.Modal,  {
2904     /**
2905          * Button config that displays a single OK button
2906          * @type Object
2907          */
2908         OK :  [{
2909             name : 'ok',
2910             weight : 'primary',
2911             html : 'OK'
2912         }],
2913         /**
2914          * Button config that displays Yes and No buttons
2915          * @type Object
2916          */
2917         YESNO : [
2918             {
2919                 name  : 'no',
2920                 html : 'No'
2921             },
2922             {
2923                 name  :'yes',
2924                 weight : 'primary',
2925                 html : 'Yes'
2926             }
2927         ],
2928
2929         /**
2930          * Button config that displays OK and Cancel buttons
2931          * @type Object
2932          */
2933         OKCANCEL : [
2934             {
2935                name : 'cancel',
2936                 html : 'Cancel'
2937             },
2938             {
2939                 name : 'ok',
2940                 weight : 'primary',
2941                 html : 'OK'
2942             }
2943         ],
2944         /**
2945          * Button config that displays Yes, No and Cancel buttons
2946          * @type Object
2947          */
2948         YESNOCANCEL : [
2949             {
2950                 name : 'yes',
2951                 weight : 'primary',
2952                 html : 'Yes'
2953             },
2954             {
2955                 name : 'no',
2956                 html : 'No'
2957             },
2958             {
2959                 name : 'cancel',
2960                 html : 'Cancel'
2961             }
2962         ],
2963         
2964         zIndex : 10001
2965 });
2966 /*
2967  * - LGPL
2968  *
2969  * messagebox - can be used as a replace
2970  * 
2971  */
2972 /**
2973  * @class Roo.MessageBox
2974  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2975  * Example usage:
2976  *<pre><code>
2977 // Basic alert:
2978 Roo.Msg.alert('Status', 'Changes saved successfully.');
2979
2980 // Prompt for user data:
2981 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2982     if (btn == 'ok'){
2983         // process text value...
2984     }
2985 });
2986
2987 // Show a dialog using config options:
2988 Roo.Msg.show({
2989    title:'Save Changes?',
2990    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2991    buttons: Roo.Msg.YESNOCANCEL,
2992    fn: processResult,
2993    animEl: 'elId'
2994 });
2995 </code></pre>
2996  * @singleton
2997  */
2998 Roo.bootstrap.MessageBox = function(){
2999     var dlg, opt, mask, waitTimer;
3000     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3001     var buttons, activeTextEl, bwidth;
3002
3003     
3004     // private
3005     var handleButton = function(button){
3006         dlg.hide();
3007         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3008     };
3009
3010     // private
3011     var handleHide = function(){
3012         if(opt && opt.cls){
3013             dlg.el.removeClass(opt.cls);
3014         }
3015         //if(waitTimer){
3016         //    Roo.TaskMgr.stop(waitTimer);
3017         //    waitTimer = null;
3018         //}
3019     };
3020
3021     // private
3022     var updateButtons = function(b){
3023         var width = 0;
3024         if(!b){
3025             buttons["ok"].hide();
3026             buttons["cancel"].hide();
3027             buttons["yes"].hide();
3028             buttons["no"].hide();
3029             //dlg.footer.dom.style.display = 'none';
3030             return width;
3031         }
3032         dlg.footerEl.dom.style.display = '';
3033         for(var k in buttons){
3034             if(typeof buttons[k] != "function"){
3035                 if(b[k]){
3036                     buttons[k].show();
3037                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3038                     width += buttons[k].el.getWidth()+15;
3039                 }else{
3040                     buttons[k].hide();
3041                 }
3042             }
3043         }
3044         return width;
3045     };
3046
3047     // private
3048     var handleEsc = function(d, k, e){
3049         if(opt && opt.closable !== false){
3050             dlg.hide();
3051         }
3052         if(e){
3053             e.stopEvent();
3054         }
3055     };
3056
3057     return {
3058         /**
3059          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3060          * @return {Roo.BasicDialog} The BasicDialog element
3061          */
3062         getDialog : function(){
3063            if(!dlg){
3064                 dlg = new Roo.bootstrap.Modal( {
3065                     //draggable: true,
3066                     //resizable:false,
3067                     //constraintoviewport:false,
3068                     //fixedcenter:true,
3069                     //collapsible : false,
3070                     //shim:true,
3071                     //modal: true,
3072                 //    width: 'auto',
3073                   //  height:100,
3074                     //buttonAlign:"center",
3075                     closeClick : function(){
3076                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3077                             handleButton("no");
3078                         }else{
3079                             handleButton("cancel");
3080                         }
3081                     }
3082                 });
3083                 dlg.render();
3084                 dlg.on("hide", handleHide);
3085                 mask = dlg.mask;
3086                 //dlg.addKeyListener(27, handleEsc);
3087                 buttons = {};
3088                 this.buttons = buttons;
3089                 var bt = this.buttonText;
3090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3094                 //Roo.log(buttons);
3095                 bodyEl = dlg.bodyEl.createChild({
3096
3097                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3098                         '<textarea class="roo-mb-textarea"></textarea>' +
3099                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3100                 });
3101                 msgEl = bodyEl.dom.firstChild;
3102                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3103                 textboxEl.enableDisplayMode();
3104                 textboxEl.addKeyListener([10,13], function(){
3105                     if(dlg.isVisible() && opt && opt.buttons){
3106                         if(opt.buttons.ok){
3107                             handleButton("ok");
3108                         }else if(opt.buttons.yes){
3109                             handleButton("yes");
3110                         }
3111                     }
3112                 });
3113                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3114                 textareaEl.enableDisplayMode();
3115                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3116                 progressEl.enableDisplayMode();
3117                 
3118                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3119                 //var pf = progressEl.dom.firstChild;
3120                 //if (pf) {
3121                     //pp = Roo.get(pf.firstChild);
3122                     //pp.setHeight(pf.offsetHeight);
3123                 //}
3124                 
3125             }
3126             return dlg;
3127         },
3128
3129         /**
3130          * Updates the message box body text
3131          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3132          * the XHTML-compliant non-breaking space character '&amp;#160;')
3133          * @return {Roo.MessageBox} This message box
3134          */
3135         updateText : function(text)
3136         {
3137             if(!dlg.isVisible() && !opt.width){
3138                 dlg.dialogEl.setWidth(this.maxWidth);
3139                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3140             }
3141             msgEl.innerHTML = text || '&#160;';
3142       
3143             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3144             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3145             var w = Math.max(
3146                     Math.min(opt.width || cw , this.maxWidth), 
3147                     Math.max(opt.minWidth || this.minWidth, bwidth)
3148             );
3149             if(opt.prompt){
3150                 activeTextEl.setWidth(w);
3151             }
3152             if(dlg.isVisible()){
3153                 dlg.fixedcenter = false;
3154             }
3155             // to big, make it scroll. = But as usual stupid IE does not support
3156             // !important..
3157             
3158             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3159                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3160                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3161             } else {
3162                 bodyEl.dom.style.height = '';
3163                 bodyEl.dom.style.overflowY = '';
3164             }
3165             if (cw > w) {
3166                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3167             } else {
3168                 bodyEl.dom.style.overflowX = '';
3169             }
3170             
3171             dlg.setContentSize(w, bodyEl.getHeight());
3172             if(dlg.isVisible()){
3173                 dlg.fixedcenter = true;
3174             }
3175             return this;
3176         },
3177
3178         /**
3179          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3180          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3181          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3182          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3183          * @return {Roo.MessageBox} This message box
3184          */
3185         updateProgress : function(value, text){
3186             if(text){
3187                 this.updateText(text);
3188             }
3189             if (pp) { // weird bug on my firefox - for some reason this is not defined
3190                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3191             }
3192             return this;
3193         },        
3194
3195         /**
3196          * Returns true if the message box is currently displayed
3197          * @return {Boolean} True if the message box is visible, else false
3198          */
3199         isVisible : function(){
3200             return dlg && dlg.isVisible();  
3201         },
3202
3203         /**
3204          * Hides the message box if it is displayed
3205          */
3206         hide : function(){
3207             if(this.isVisible()){
3208                 dlg.hide();
3209             }  
3210         },
3211
3212         /**
3213          * Displays a new message box, or reinitializes an existing message box, based on the config options
3214          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3215          * The following config object properties are supported:
3216          * <pre>
3217 Property    Type             Description
3218 ----------  ---------------  ------------------------------------------------------------------------------------
3219 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3220                                    closes (defaults to undefined)
3221 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3222                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3223 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3224                                    progress and wait dialogs will ignore this property and always hide the
3225                                    close button as they can only be closed programmatically.
3226 cls               String           A custom CSS class to apply to the message box element
3227 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3228                                    displayed (defaults to 75)
3229 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3230                                    function will be btn (the name of the button that was clicked, if applicable,
3231                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3232                                    Progress and wait dialogs will ignore this option since they do not respond to
3233                                    user actions and can only be closed programmatically, so any required function
3234                                    should be called by the same code after it closes the dialog.
3235 icon              String           A CSS class that provides a background image to be used as an icon for
3236                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3237 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3238 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3239 modal             Boolean          False to allow user interaction with the page while the message box is
3240                                    displayed (defaults to true)
3241 msg               String           A string that will replace the existing message box body text (defaults
3242                                    to the XHTML-compliant non-breaking space character '&#160;')
3243 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3244 progress          Boolean          True to display a progress bar (defaults to false)
3245 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3246 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3247 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3248 title             String           The title text
3249 value             String           The string value to set into the active textbox element if displayed
3250 wait              Boolean          True to display a progress bar (defaults to false)
3251 width             Number           The width of the dialog in pixels
3252 </pre>
3253          *
3254          * Example usage:
3255          * <pre><code>
3256 Roo.Msg.show({
3257    title: 'Address',
3258    msg: 'Please enter your address:',
3259    width: 300,
3260    buttons: Roo.MessageBox.OKCANCEL,
3261    multiline: true,
3262    fn: saveAddress,
3263    animEl: 'addAddressBtn'
3264 });
3265 </code></pre>
3266          * @param {Object} config Configuration options
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         show : function(options)
3270         {
3271             
3272             // this causes nightmares if you show one dialog after another
3273             // especially on callbacks..
3274              
3275             if(this.isVisible()){
3276                 
3277                 this.hide();
3278                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3279                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3280                 Roo.log("New Dialog Message:" +  options.msg )
3281                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3282                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3283                 
3284             }
3285             var d = this.getDialog();
3286             opt = options;
3287             d.setTitle(opt.title || "&#160;");
3288             d.closeEl.setDisplayed(opt.closable !== false);
3289             activeTextEl = textboxEl;
3290             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3291             if(opt.prompt){
3292                 if(opt.multiline){
3293                     textboxEl.hide();
3294                     textareaEl.show();
3295                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3296                         opt.multiline : this.defaultTextHeight);
3297                     activeTextEl = textareaEl;
3298                 }else{
3299                     textboxEl.show();
3300                     textareaEl.hide();
3301                 }
3302             }else{
3303                 textboxEl.hide();
3304                 textareaEl.hide();
3305             }
3306             progressEl.setDisplayed(opt.progress === true);
3307             this.updateProgress(0);
3308             activeTextEl.dom.value = opt.value || "";
3309             if(opt.prompt){
3310                 dlg.setDefaultButton(activeTextEl);
3311             }else{
3312                 var bs = opt.buttons;
3313                 var db = null;
3314                 if(bs && bs.ok){
3315                     db = buttons["ok"];
3316                 }else if(bs && bs.yes){
3317                     db = buttons["yes"];
3318                 }
3319                 dlg.setDefaultButton(db);
3320             }
3321             bwidth = updateButtons(opt.buttons);
3322             this.updateText(opt.msg);
3323             if(opt.cls){
3324                 d.el.addClass(opt.cls);
3325             }
3326             d.proxyDrag = opt.proxyDrag === true;
3327             d.modal = opt.modal !== false;
3328             d.mask = opt.modal !== false ? mask : false;
3329             if(!d.isVisible()){
3330                 // force it to the end of the z-index stack so it gets a cursor in FF
3331                 document.body.appendChild(dlg.el.dom);
3332                 d.animateTarget = null;
3333                 d.show(options.animEl);
3334             }
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3340          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3341          * and closing the message box when the process is complete.
3342          * @param {String} title The title bar text
3343          * @param {String} msg The message box body text
3344          * @return {Roo.MessageBox} This message box
3345          */
3346         progress : function(title, msg){
3347             this.show({
3348                 title : title,
3349                 msg : msg,
3350                 buttons: false,
3351                 progress:true,
3352                 closable:false,
3353                 minWidth: this.minProgressWidth,
3354                 modal : true
3355             });
3356             return this;
3357         },
3358
3359         /**
3360          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3361          * If a callback function is passed it will be called after the user clicks the button, and the
3362          * id of the button that was clicked will be passed as the only parameter to the callback
3363          * (could also be the top-right close button).
3364          * @param {String} title The title bar text
3365          * @param {String} msg The message box body text
3366          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3367          * @param {Object} scope (optional) The scope of the callback function
3368          * @return {Roo.MessageBox} This message box
3369          */
3370         alert : function(title, msg, fn, scope)
3371         {
3372             this.show({
3373                 title : title,
3374                 msg : msg,
3375                 buttons: this.OK,
3376                 fn: fn,
3377                 closable : false,
3378                 scope : scope,
3379                 modal : true
3380             });
3381             return this;
3382         },
3383
3384         /**
3385          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3386          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3387          * You are responsible for closing the message box when the process is complete.
3388          * @param {String} msg The message box body text
3389          * @param {String} title (optional) The title bar text
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         wait : function(msg, title){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: false,
3397                 closable:false,
3398                 progress:true,
3399                 modal:true,
3400                 width:300,
3401                 wait:true
3402             });
3403             waitTimer = Roo.TaskMgr.start({
3404                 run: function(i){
3405                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3406                 },
3407                 interval: 1000
3408             });
3409             return this;
3410         },
3411
3412         /**
3413          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3414          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3415          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3416          * @param {String} title The title bar text
3417          * @param {String} msg The message box body text
3418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3419          * @param {Object} scope (optional) The scope of the callback function
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         confirm : function(title, msg, fn, scope){
3423             this.show({
3424                 title : title,
3425                 msg : msg,
3426                 buttons: this.YESNO,
3427                 fn: fn,
3428                 scope : scope,
3429                 modal : true
3430             });
3431             return this;
3432         },
3433
3434         /**
3435          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3436          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3437          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3438          * (could also be the top-right close button) and the text that was entered will be passed as the two
3439          * parameters to the callback.
3440          * @param {String} title The title bar text
3441          * @param {String} msg The message box body text
3442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3443          * @param {Object} scope (optional) The scope of the callback function
3444          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3445          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         prompt : function(title, msg, fn, scope, multiline){
3449             this.show({
3450                 title : title,
3451                 msg : msg,
3452                 buttons: this.OKCANCEL,
3453                 fn: fn,
3454                 minWidth:250,
3455                 scope : scope,
3456                 prompt:true,
3457                 multiline: multiline,
3458                 modal : true
3459             });
3460             return this;
3461         },
3462
3463         /**
3464          * Button config that displays a single OK button
3465          * @type Object
3466          */
3467         OK : {ok:true},
3468         /**
3469          * Button config that displays Yes and No buttons
3470          * @type Object
3471          */
3472         YESNO : {yes:true, no:true},
3473         /**
3474          * Button config that displays OK and Cancel buttons
3475          * @type Object
3476          */
3477         OKCANCEL : {ok:true, cancel:true},
3478         /**
3479          * Button config that displays Yes, No and Cancel buttons
3480          * @type Object
3481          */
3482         YESNOCANCEL : {yes:true, no:true, cancel:true},
3483
3484         /**
3485          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3486          * @type Number
3487          */
3488         defaultTextHeight : 75,
3489         /**
3490          * The maximum width in pixels of the message box (defaults to 600)
3491          * @type Number
3492          */
3493         maxWidth : 600,
3494         /**
3495          * The minimum width in pixels of the message box (defaults to 100)
3496          * @type Number
3497          */
3498         minWidth : 100,
3499         /**
3500          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3501          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3502          * @type Number
3503          */
3504         minProgressWidth : 250,
3505         /**
3506          * An object containing the default button text strings that can be overriden for localized language support.
3507          * Supported properties are: ok, cancel, yes and no.
3508          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3509          * @type Object
3510          */
3511         buttonText : {
3512             ok : "OK",
3513             cancel : "Cancel",
3514             yes : "Yes",
3515             no : "No"
3516         }
3517     };
3518 }();
3519
3520 /**
3521  * Shorthand for {@link Roo.MessageBox}
3522  */
3523 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3524 Roo.Msg = Roo.Msg || Roo.MessageBox;
3525 /*
3526  * - LGPL
3527  *
3528  * navbar
3529  * 
3530  */
3531
3532 /**
3533  * @class Roo.bootstrap.Navbar
3534  * @extends Roo.bootstrap.Component
3535  * Bootstrap Navbar class
3536
3537  * @constructor
3538  * Create a new Navbar
3539  * @param {Object} config The config object
3540  */
3541
3542
3543 Roo.bootstrap.Navbar = function(config){
3544     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3545     this.addEvents({
3546         // raw events
3547         /**
3548          * @event beforetoggle
3549          * Fire before toggle the menu
3550          * @param {Roo.EventObject} e
3551          */
3552         "beforetoggle" : true
3553     });
3554 };
3555
3556 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3557     
3558     
3559    
3560     // private
3561     navItems : false,
3562     loadMask : false,
3563     
3564     
3565     getAutoCreate : function(){
3566         
3567         
3568         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3569         
3570     },
3571     
3572     initEvents :function ()
3573     {
3574         //Roo.log(this.el.select('.navbar-toggle',true));
3575         this.el.select('.navbar-toggle',true).on('click', function() {
3576             if(this.fireEvent('beforetoggle', this) !== false){
3577                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3578             }
3579             
3580         }, this);
3581         
3582         var mark = {
3583             tag: "div",
3584             cls:"x-dlg-mask"
3585         };
3586         
3587         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3588         
3589         var size = this.el.getSize();
3590         this.maskEl.setSize(size.width, size.height);
3591         this.maskEl.enableDisplayMode("block");
3592         this.maskEl.hide();
3593         
3594         if(this.loadMask){
3595             this.maskEl.show();
3596         }
3597     },
3598     
3599     
3600     getChildContainer : function()
3601     {
3602         if (this.el.select('.collapse').getCount()) {
3603             return this.el.select('.collapse',true).first();
3604         }
3605         
3606         return this.el;
3607     },
3608     
3609     mask : function()
3610     {
3611         this.maskEl.show();
3612     },
3613     
3614     unmask : function()
3615     {
3616         this.maskEl.hide();
3617     } 
3618     
3619     
3620     
3621     
3622 });
3623
3624
3625
3626  
3627
3628  /*
3629  * - LGPL
3630  *
3631  * navbar
3632  * 
3633  */
3634
3635 /**
3636  * @class Roo.bootstrap.NavSimplebar
3637  * @extends Roo.bootstrap.Navbar
3638  * Bootstrap Sidebar class
3639  *
3640  * @cfg {Boolean} inverse is inverted color
3641  * 
3642  * @cfg {String} type (nav | pills | tabs)
3643  * @cfg {Boolean} arrangement stacked | justified
3644  * @cfg {String} align (left | right) alignment
3645  * 
3646  * @cfg {Boolean} main (true|false) main nav bar? default false
3647  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3648  * 
3649  * @cfg {String} tag (header|footer|nav|div) default is nav 
3650
3651  * 
3652  * 
3653  * 
3654  * @constructor
3655  * Create a new Sidebar
3656  * @param {Object} config The config object
3657  */
3658
3659
3660 Roo.bootstrap.NavSimplebar = function(config){
3661     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3662 };
3663
3664 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3665     
3666     inverse: false,
3667     
3668     type: false,
3669     arrangement: '',
3670     align : false,
3671     
3672     
3673     
3674     main : false,
3675     
3676     
3677     tag : false,
3678     
3679     
3680     getAutoCreate : function(){
3681         
3682         
3683         var cfg = {
3684             tag : this.tag || 'div',
3685             cls : 'navbar'
3686         };
3687           
3688         
3689         cfg.cn = [
3690             {
3691                 cls: 'nav',
3692                 tag : 'ul'
3693             }
3694         ];
3695         
3696          
3697         this.type = this.type || 'nav';
3698         if (['tabs','pills'].indexOf(this.type)!==-1) {
3699             cfg.cn[0].cls += ' nav-' + this.type
3700         
3701         
3702         } else {
3703             if (this.type!=='nav') {
3704                 Roo.log('nav type must be nav/tabs/pills')
3705             }
3706             cfg.cn[0].cls += ' navbar-nav'
3707         }
3708         
3709         
3710         
3711         
3712         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3713             cfg.cn[0].cls += ' nav-' + this.arrangement;
3714         }
3715         
3716         
3717         if (this.align === 'right') {
3718             cfg.cn[0].cls += ' navbar-right';
3719         }
3720         
3721         if (this.inverse) {
3722             cfg.cls += ' navbar-inverse';
3723             
3724         }
3725         
3726         
3727         return cfg;
3728     
3729         
3730     }
3731     
3732     
3733     
3734 });
3735
3736
3737
3738  
3739
3740  
3741        /*
3742  * - LGPL
3743  *
3744  * navbar
3745  * 
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavHeaderbar
3750  * @extends Roo.bootstrap.NavSimplebar
3751  * Bootstrap Sidebar class
3752  *
3753  * @cfg {String} brand what is brand
3754  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3755  * @cfg {String} brand_href href of the brand
3756  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3757  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3758  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3759  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3760  * 
3761  * @constructor
3762  * Create a new Sidebar
3763  * @param {Object} config The config object
3764  */
3765
3766
3767 Roo.bootstrap.NavHeaderbar = function(config){
3768     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3769       
3770 };
3771
3772 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3773     
3774     position: '',
3775     brand: '',
3776     brand_href: false,
3777     srButton : true,
3778     autohide : false,
3779     desktopCenter : false,
3780    
3781     
3782     getAutoCreate : function(){
3783         
3784         var   cfg = {
3785             tag: this.nav || 'nav',
3786             cls: 'navbar',
3787             role: 'navigation',
3788             cn: []
3789         };
3790         
3791         var cn = cfg.cn;
3792         if (this.desktopCenter) {
3793             cn.push({cls : 'container', cn : []});
3794             cn = cn[0].cn;
3795         }
3796         
3797         if(this.srButton){
3798             cn.push({
3799                 tag: 'div',
3800                 cls: 'navbar-header',
3801                 cn: [
3802                     {
3803                         tag: 'button',
3804                         type: 'button',
3805                         cls: 'navbar-toggle',
3806                         'data-toggle': 'collapse',
3807                         cn: [
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'sr-only',
3811                                 html: 'Toggle navigation'
3812                             },
3813                             {
3814                                 tag: 'span',
3815                                 cls: 'icon-bar'
3816                             },
3817                             {
3818                                 tag: 'span',
3819                                 cls: 'icon-bar'
3820                             },
3821                             {
3822                                 tag: 'span',
3823                                 cls: 'icon-bar'
3824                             }
3825                         ]
3826                     }
3827                 ]
3828             });
3829         }
3830         
3831         cn.push({
3832             tag: 'div',
3833             cls: 'collapse navbar-collapse',
3834             cn : []
3835         });
3836         
3837         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3838         
3839         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3840             cfg.cls += ' navbar-' + this.position;
3841             
3842             // tag can override this..
3843             
3844             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3845         }
3846         
3847         if (this.brand !== '') {
3848             cn[0].cn.push({
3849                 tag: 'a',
3850                 href: this.brand_href ? this.brand_href : '#',
3851                 cls: 'navbar-brand',
3852                 cn: [
3853                 this.brand
3854                 ]
3855             });
3856         }
3857         
3858         if(this.main){
3859             cfg.cls += ' main-nav';
3860         }
3861         
3862         
3863         return cfg;
3864
3865         
3866     },
3867     getHeaderChildContainer : function()
3868     {
3869         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3870             return this.el.select('.navbar-header',true).first();
3871         }
3872         
3873         return this.getChildContainer();
3874     },
3875     
3876     
3877     initEvents : function()
3878     {
3879         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3880         
3881         if (this.autohide) {
3882             
3883             var prevScroll = 0;
3884             var ft = this.el;
3885             
3886             Roo.get(document).on('scroll',function(e) {
3887                 var ns = Roo.get(document).getScroll().top;
3888                 var os = prevScroll;
3889                 prevScroll = ns;
3890                 
3891                 if(ns > os){
3892                     ft.removeClass('slideDown');
3893                     ft.addClass('slideUp');
3894                     return;
3895                 }
3896                 ft.removeClass('slideUp');
3897                 ft.addClass('slideDown');
3898                  
3899               
3900           },this);
3901         }
3902     }    
3903     
3904 });
3905
3906
3907
3908  
3909
3910  /*
3911  * - LGPL
3912  *
3913  * navbar
3914  * 
3915  */
3916
3917 /**
3918  * @class Roo.bootstrap.NavSidebar
3919  * @extends Roo.bootstrap.Navbar
3920  * Bootstrap Sidebar class
3921  * 
3922  * @constructor
3923  * Create a new Sidebar
3924  * @param {Object} config The config object
3925  */
3926
3927
3928 Roo.bootstrap.NavSidebar = function(config){
3929     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3930 };
3931
3932 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3933     
3934     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3935     
3936     getAutoCreate : function(){
3937         
3938         
3939         return  {
3940             tag: 'div',
3941             cls: 'sidebar sidebar-nav'
3942         };
3943     
3944         
3945     }
3946     
3947     
3948     
3949 });
3950
3951
3952
3953  
3954
3955  /*
3956  * - LGPL
3957  *
3958  * nav group
3959  * 
3960  */
3961
3962 /**
3963  * @class Roo.bootstrap.NavGroup
3964  * @extends Roo.bootstrap.Component
3965  * Bootstrap NavGroup class
3966  * @cfg {String} align (left|right)
3967  * @cfg {Boolean} inverse
3968  * @cfg {String} type (nav|pills|tab) default nav
3969  * @cfg {String} navId - reference Id for navbar.
3970
3971  * 
3972  * @constructor
3973  * Create a new nav group
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.NavGroup = function(config){
3978     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3979     this.navItems = [];
3980    
3981     Roo.bootstrap.NavGroup.register(this);
3982      this.addEvents({
3983         /**
3984              * @event changed
3985              * Fires when the active item changes
3986              * @param {Roo.bootstrap.NavGroup} this
3987              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3988              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3989          */
3990         'changed': true
3991      });
3992     
3993 };
3994
3995 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3996     
3997     align: '',
3998     inverse: false,
3999     form: false,
4000     type: 'nav',
4001     navId : '',
4002     // private
4003     
4004     navItems : false, 
4005     
4006     getAutoCreate : function()
4007     {
4008         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4009         
4010         cfg = {
4011             tag : 'ul',
4012             cls: 'nav' 
4013         };
4014         
4015         if (['tabs','pills'].indexOf(this.type)!==-1) {
4016             cfg.cls += ' nav-' + this.type
4017         } else {
4018             if (this.type!=='nav') {
4019                 Roo.log('nav type must be nav/tabs/pills')
4020             }
4021             cfg.cls += ' navbar-nav'
4022         }
4023         
4024         if (this.parent().sidebar) {
4025             cfg = {
4026                 tag: 'ul',
4027                 cls: 'dashboard-menu sidebar-menu'
4028             };
4029             
4030             return cfg;
4031         }
4032         
4033         if (this.form === true) {
4034             cfg = {
4035                 tag: 'form',
4036                 cls: 'navbar-form'
4037             };
4038             
4039             if (this.align === 'right') {
4040                 cfg.cls += ' navbar-right';
4041             } else {
4042                 cfg.cls += ' navbar-left';
4043             }
4044         }
4045         
4046         if (this.align === 'right') {
4047             cfg.cls += ' navbar-right';
4048         }
4049         
4050         if (this.inverse) {
4051             cfg.cls += ' navbar-inverse';
4052             
4053         }
4054         
4055         
4056         return cfg;
4057     },
4058     /**
4059     * sets the active Navigation item
4060     * @param {Roo.bootstrap.NavItem} the new current navitem
4061     */
4062     setActiveItem : function(item)
4063     {
4064         var prev = false;
4065         Roo.each(this.navItems, function(v){
4066             if (v == item) {
4067                 return ;
4068             }
4069             if (v.isActive()) {
4070                 v.setActive(false, true);
4071                 prev = v;
4072                 
4073             }
4074             
4075         });
4076
4077         item.setActive(true, true);
4078         this.fireEvent('changed', this, item, prev);
4079         
4080         
4081     },
4082     /**
4083     * gets the active Navigation item
4084     * @return {Roo.bootstrap.NavItem} the current navitem
4085     */
4086     getActive : function()
4087     {
4088         
4089         var prev = false;
4090         Roo.each(this.navItems, function(v){
4091             
4092             if (v.isActive()) {
4093                 prev = v;
4094                 
4095             }
4096             
4097         });
4098         return prev;
4099     },
4100     
4101     indexOfNav : function()
4102     {
4103         
4104         var prev = false;
4105         Roo.each(this.navItems, function(v,i){
4106             
4107             if (v.isActive()) {
4108                 prev = i;
4109                 
4110             }
4111             
4112         });
4113         return prev;
4114     },
4115     /**
4116     * adds a Navigation item
4117     * @param {Roo.bootstrap.NavItem} the navitem to add
4118     */
4119     addItem : function(cfg)
4120     {
4121         var cn = new Roo.bootstrap.NavItem(cfg);
4122         this.register(cn);
4123         cn.parentId = this.id;
4124         cn.onRender(this.el, null);
4125         return cn;
4126     },
4127     /**
4128     * register a Navigation item
4129     * @param {Roo.bootstrap.NavItem} the navitem to add
4130     */
4131     register : function(item)
4132     {
4133         this.navItems.push( item);
4134         item.navId = this.navId;
4135     
4136     },
4137     
4138     /**
4139     * clear all the Navigation item
4140     */
4141    
4142     clearAll : function()
4143     {
4144         this.navItems = [];
4145         this.el.dom.innerHTML = '';
4146     },
4147     
4148     getNavItem: function(tabId)
4149     {
4150         var ret = false;
4151         Roo.each(this.navItems, function(e) {
4152             if (e.tabId == tabId) {
4153                ret =  e;
4154                return false;
4155             }
4156             return true;
4157             
4158         });
4159         return ret;
4160     },
4161     
4162     setActiveNext : function()
4163     {
4164         var i = this.indexOfNav(this.getActive());
4165         if (i > this.navItems.length) {
4166             return;
4167         }
4168         this.setActiveItem(this.navItems[i+1]);
4169     },
4170     setActivePrev : function()
4171     {
4172         var i = this.indexOfNav(this.getActive());
4173         if (i  < 1) {
4174             return;
4175         }
4176         this.setActiveItem(this.navItems[i-1]);
4177     },
4178     clearWasActive : function(except) {
4179         Roo.each(this.navItems, function(e) {
4180             if (e.tabId != except.tabId && e.was_active) {
4181                e.was_active = false;
4182                return false;
4183             }
4184             return true;
4185             
4186         });
4187     },
4188     getWasActive : function ()
4189     {
4190         var r = false;
4191         Roo.each(this.navItems, function(e) {
4192             if (e.was_active) {
4193                r = e;
4194                return false;
4195             }
4196             return true;
4197             
4198         });
4199         return r;
4200     }
4201     
4202     
4203 });
4204
4205  
4206 Roo.apply(Roo.bootstrap.NavGroup, {
4207     
4208     groups: {},
4209      /**
4210     * register a Navigation Group
4211     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4212     */
4213     register : function(navgrp)
4214     {
4215         this.groups[navgrp.navId] = navgrp;
4216         
4217     },
4218     /**
4219     * fetch a Navigation Group based on the navigation ID
4220     * @param {string} the navgroup to add
4221     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4222     */
4223     get: function(navId) {
4224         if (typeof(this.groups[navId]) == 'undefined') {
4225             return false;
4226             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4227         }
4228         return this.groups[navId] ;
4229     }
4230     
4231     
4232     
4233 });
4234
4235  /*
4236  * - LGPL
4237  *
4238  * row
4239  * 
4240  */
4241
4242 /**
4243  * @class Roo.bootstrap.NavItem
4244  * @extends Roo.bootstrap.Component
4245  * Bootstrap Navbar.NavItem class
4246  * @cfg {String} href  link to
4247  * @cfg {String} html content of button
4248  * @cfg {String} badge text inside badge
4249  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4250  * @cfg {String} glyphicon name of glyphicon
4251  * @cfg {String} icon name of font awesome icon
4252  * @cfg {Boolean} active Is item active
4253  * @cfg {Boolean} disabled Is item disabled
4254  
4255  * @cfg {Boolean} preventDefault (true | false) default false
4256  * @cfg {String} tabId the tab that this item activates.
4257  * @cfg {String} tagtype (a|span) render as a href or span?
4258  * @cfg {Boolean} animateRef (true|false) link to element default false  
4259   
4260  * @constructor
4261  * Create a new Navbar Item
4262  * @param {Object} config The config object
4263  */
4264 Roo.bootstrap.NavItem = function(config){
4265     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4266     this.addEvents({
4267         // raw events
4268         /**
4269          * @event click
4270          * The raw click event for the entire grid.
4271          * @param {Roo.EventObject} e
4272          */
4273         "click" : true,
4274          /**
4275             * @event changed
4276             * Fires when the active item active state changes
4277             * @param {Roo.bootstrap.NavItem} this
4278             * @param {boolean} state the new state
4279              
4280          */
4281         'changed': true,
4282         /**
4283             * @event scrollto
4284             * Fires when scroll to element
4285             * @param {Roo.bootstrap.NavItem} this
4286             * @param {Object} options
4287             * @param {Roo.EventObject} e
4288              
4289          */
4290         'scrollto': true
4291     });
4292    
4293 };
4294
4295 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4296     
4297     href: false,
4298     html: '',
4299     badge: '',
4300     icon: false,
4301     glyphicon: false,
4302     active: false,
4303     preventDefault : false,
4304     tabId : false,
4305     tagtype : 'a',
4306     disabled : false,
4307     animateRef : false,
4308     was_active : false,
4309     
4310     getAutoCreate : function(){
4311          
4312         var cfg = {
4313             tag: 'li',
4314             cls: 'nav-item'
4315             
4316         };
4317         
4318         if (this.active) {
4319             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4320         }
4321         if (this.disabled) {
4322             cfg.cls += ' disabled';
4323         }
4324         
4325         if (this.href || this.html || this.glyphicon || this.icon) {
4326             cfg.cn = [
4327                 {
4328                     tag: this.tagtype,
4329                     href : this.href || "#",
4330                     html: this.html || ''
4331                 }
4332             ];
4333             
4334             if (this.icon) {
4335                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4336             }
4337
4338             if(this.glyphicon) {
4339                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4340             }
4341             
4342             if (this.menu) {
4343                 
4344                 cfg.cn[0].html += " <span class='caret'></span>";
4345              
4346             }
4347             
4348             if (this.badge !== '') {
4349                  
4350                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4351             }
4352         }
4353         
4354         
4355         
4356         return cfg;
4357     },
4358     initEvents: function() 
4359     {
4360         if (typeof (this.menu) != 'undefined') {
4361             this.menu.parentType = this.xtype;
4362             this.menu.triggerEl = this.el;
4363             this.menu = this.addxtype(Roo.apply({}, this.menu));
4364         }
4365         
4366         this.el.select('a',true).on('click', this.onClick, this);
4367         
4368         if(this.tagtype == 'span'){
4369             this.el.select('span',true).on('click', this.onClick, this);
4370         }
4371        
4372         // at this point parent should be available..
4373         this.parent().register(this);
4374     },
4375     
4376     onClick : function(e)
4377     {
4378         if (e.getTarget('.dropdown-menu-item')) {
4379             // did you click on a menu itemm.... - then don't trigger onclick..
4380             return;
4381         }
4382         
4383         if(
4384                 this.preventDefault || 
4385                 this.href == '#' 
4386         ){
4387             Roo.log("NavItem - prevent Default?");
4388             e.preventDefault();
4389         }
4390         
4391         if (this.disabled) {
4392             return;
4393         }
4394         
4395         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4396         if (tg && tg.transition) {
4397             Roo.log("waiting for the transitionend");
4398             return;
4399         }
4400         
4401         
4402         
4403         //Roo.log("fire event clicked");
4404         if(this.fireEvent('click', this, e) === false){
4405             return;
4406         };
4407         
4408         if(this.tagtype == 'span'){
4409             return;
4410         }
4411         
4412         //Roo.log(this.href);
4413         var ael = this.el.select('a',true).first();
4414         //Roo.log(ael);
4415         
4416         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4417             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4418             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4419                 return; // ignore... - it's a 'hash' to another page.
4420             }
4421             Roo.log("NavItem - prevent Default?");
4422             e.preventDefault();
4423             this.scrollToElement(e);
4424         }
4425         
4426         
4427         var p =  this.parent();
4428    
4429         if (['tabs','pills'].indexOf(p.type)!==-1) {
4430             if (typeof(p.setActiveItem) !== 'undefined') {
4431                 p.setActiveItem(this);
4432             }
4433         }
4434         
4435         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4436         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4437             // remove the collapsed menu expand...
4438             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4439         }
4440     },
4441     
4442     isActive: function () {
4443         return this.active
4444     },
4445     setActive : function(state, fire, is_was_active)
4446     {
4447         if (this.active && !state && this.navId) {
4448             this.was_active = true;
4449             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4450             if (nv) {
4451                 nv.clearWasActive(this);
4452             }
4453             
4454         }
4455         this.active = state;
4456         
4457         if (!state ) {
4458             this.el.removeClass('active');
4459         } else if (!this.el.hasClass('active')) {
4460             this.el.addClass('active');
4461         }
4462         if (fire) {
4463             this.fireEvent('changed', this, state);
4464         }
4465         
4466         // show a panel if it's registered and related..
4467         
4468         if (!this.navId || !this.tabId || !state || is_was_active) {
4469             return;
4470         }
4471         
4472         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4473         if (!tg) {
4474             return;
4475         }
4476         var pan = tg.getPanelByName(this.tabId);
4477         if (!pan) {
4478             return;
4479         }
4480         // if we can not flip to new panel - go back to old nav highlight..
4481         if (false == tg.showPanel(pan)) {
4482             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4483             if (nv) {
4484                 var onav = nv.getWasActive();
4485                 if (onav) {
4486                     onav.setActive(true, false, true);
4487                 }
4488             }
4489             
4490         }
4491         
4492         
4493         
4494     },
4495      // this should not be here...
4496     setDisabled : function(state)
4497     {
4498         this.disabled = state;
4499         if (!state ) {
4500             this.el.removeClass('disabled');
4501         } else if (!this.el.hasClass('disabled')) {
4502             this.el.addClass('disabled');
4503         }
4504         
4505     },
4506     
4507     /**
4508      * Fetch the element to display the tooltip on.
4509      * @return {Roo.Element} defaults to this.el
4510      */
4511     tooltipEl : function()
4512     {
4513         return this.el.select('' + this.tagtype + '', true).first();
4514     },
4515     
4516     scrollToElement : function(e)
4517     {
4518         var c = document.body;
4519         
4520         /*
4521          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4522          */
4523         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4524             c = document.documentElement;
4525         }
4526         
4527         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4528         
4529         if(!target){
4530             return;
4531         }
4532
4533         var o = target.calcOffsetsTo(c);
4534         
4535         var options = {
4536             target : target,
4537             value : o[1]
4538         };
4539         
4540         this.fireEvent('scrollto', this, options, e);
4541         
4542         Roo.get(c).scrollTo('top', options.value, true);
4543         
4544         return;
4545     }
4546 });
4547  
4548
4549  /*
4550  * - LGPL
4551  *
4552  * sidebar item
4553  *
4554  *  li
4555  *    <span> icon </span>
4556  *    <span> text </span>
4557  *    <span>badge </span>
4558  */
4559
4560 /**
4561  * @class Roo.bootstrap.NavSidebarItem
4562  * @extends Roo.bootstrap.NavItem
4563  * Bootstrap Navbar.NavSidebarItem class
4564  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4565  * {bool} open is the menu open
4566  * @constructor
4567  * Create a new Navbar Button
4568  * @param {Object} config The config object
4569  */
4570 Roo.bootstrap.NavSidebarItem = function(config){
4571     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4572     this.addEvents({
4573         // raw events
4574         /**
4575          * @event click
4576          * The raw click event for the entire grid.
4577          * @param {Roo.EventObject} e
4578          */
4579         "click" : true,
4580          /**
4581             * @event changed
4582             * Fires when the active item active state changes
4583             * @param {Roo.bootstrap.NavSidebarItem} this
4584             * @param {boolean} state the new state
4585              
4586          */
4587         'changed': true
4588     });
4589    
4590 };
4591
4592 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4593     
4594     badgeWeight : 'default',
4595     
4596     open: false,
4597     
4598     getAutoCreate : function(){
4599         
4600         
4601         var a = {
4602                 tag: 'a',
4603                 href : this.href || '#',
4604                 cls: '',
4605                 html : '',
4606                 cn : []
4607         };
4608         var cfg = {
4609             tag: 'li',
4610             cls: '',
4611             cn: [ a ]
4612         };
4613         var span = {
4614             tag: 'span',
4615             html : this.html || ''
4616         };
4617         
4618         
4619         if (this.active) {
4620             cfg.cls += ' active';
4621         }
4622         
4623         if (this.disabled) {
4624             cfg.cls += ' disabled';
4625         }
4626         if (this.open) {
4627             cfg.cls += ' open x-open';
4628         }
4629         // left icon..
4630         if (this.glyphicon || this.icon) {
4631             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4632             a.cn.push({ tag : 'i', cls : c }) ;
4633         }
4634         // html..
4635         a.cn.push(span);
4636         // then badge..
4637         if (this.badge !== '') {
4638             
4639             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4640         }
4641         // fi
4642         if (this.menu) {
4643             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4644             a.cls += 'dropdown-toggle treeview' ;
4645         }
4646         
4647         return cfg;
4648          
4649            
4650     },
4651     
4652     initEvents : function()
4653     { 
4654         if (typeof (this.menu) != 'undefined') {
4655             this.menu.parentType = this.xtype;
4656             this.menu.triggerEl = this.el;
4657             this.menu = this.addxtype(Roo.apply({}, this.menu));
4658         }
4659         
4660         this.el.on('click', this.onClick, this);
4661        
4662     
4663         if(this.badge !== ''){
4664  
4665             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4666         }
4667         
4668     },
4669     
4670     onClick : function(e)
4671     {
4672         if(this.disabled){
4673             e.preventDefault();
4674             return;
4675         }
4676         
4677         if(this.preventDefault){
4678             e.preventDefault();
4679         }
4680         
4681         this.fireEvent('click', this);
4682     },
4683     
4684     disable : function()
4685     {
4686         this.setDisabled(true);
4687     },
4688     
4689     enable : function()
4690     {
4691         this.setDisabled(false);
4692     },
4693     
4694     setDisabled : function(state)
4695     {
4696         if(this.disabled == state){
4697             return;
4698         }
4699         
4700         this.disabled = state;
4701         
4702         if (state) {
4703             this.el.addClass('disabled');
4704             return;
4705         }
4706         
4707         this.el.removeClass('disabled');
4708         
4709         return;
4710     },
4711     
4712     setActive : function(state)
4713     {
4714         if(this.active == state){
4715             return;
4716         }
4717         
4718         this.active = state;
4719         
4720         if (state) {
4721             this.el.addClass('active');
4722             return;
4723         }
4724         
4725         this.el.removeClass('active');
4726         
4727         return;
4728     },
4729     
4730     isActive: function () 
4731     {
4732         return this.active;
4733     },
4734     
4735     setBadge : function(str)
4736     {
4737         if(!this.badgeEl){
4738             return;
4739         }
4740         
4741         this.badgeEl.dom.innerHTML = str;
4742     }
4743     
4744    
4745      
4746  
4747 });
4748  
4749
4750  /*
4751  * - LGPL
4752  *
4753  * row
4754  * 
4755  */
4756
4757 /**
4758  * @class Roo.bootstrap.Row
4759  * @extends Roo.bootstrap.Component
4760  * Bootstrap Row class (contains columns...)
4761  * 
4762  * @constructor
4763  * Create a new Row
4764  * @param {Object} config The config object
4765  */
4766
4767 Roo.bootstrap.Row = function(config){
4768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4769 };
4770
4771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4772     
4773     getAutoCreate : function(){
4774        return {
4775             cls: 'row clearfix'
4776        };
4777     }
4778     
4779     
4780 });
4781
4782  
4783
4784  /*
4785  * - LGPL
4786  *
4787  * element
4788  * 
4789  */
4790
4791 /**
4792  * @class Roo.bootstrap.Element
4793  * @extends Roo.bootstrap.Component
4794  * Bootstrap Element class
4795  * @cfg {String} html contents of the element
4796  * @cfg {String} tag tag of the element
4797  * @cfg {String} cls class of the element
4798  * @cfg {Boolean} preventDefault (true|false) default false
4799  * @cfg {Boolean} clickable (true|false) default false
4800  * 
4801  * @constructor
4802  * Create a new Element
4803  * @param {Object} config The config object
4804  */
4805
4806 Roo.bootstrap.Element = function(config){
4807     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4808     
4809     this.addEvents({
4810         // raw events
4811         /**
4812          * @event click
4813          * When a element is chick
4814          * @param {Roo.bootstrap.Element} this
4815          * @param {Roo.EventObject} e
4816          */
4817         "click" : true
4818     });
4819 };
4820
4821 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4822     
4823     tag: 'div',
4824     cls: '',
4825     html: '',
4826     preventDefault: false, 
4827     clickable: false,
4828     
4829     getAutoCreate : function(){
4830         
4831         var cfg = {
4832             tag: this.tag,
4833             cls: this.cls,
4834             html: this.html
4835         };
4836         
4837         return cfg;
4838     },
4839     
4840     initEvents: function() 
4841     {
4842         Roo.bootstrap.Element.superclass.initEvents.call(this);
4843         
4844         if(this.clickable){
4845             this.el.on('click', this.onClick, this);
4846         }
4847         
4848     },
4849     
4850     onClick : function(e)
4851     {
4852         if(this.preventDefault){
4853             e.preventDefault();
4854         }
4855         
4856         this.fireEvent('click', this, e);
4857     },
4858     
4859     getValue : function()
4860     {
4861         return this.el.dom.innerHTML;
4862     },
4863     
4864     setValue : function(value)
4865     {
4866         this.el.dom.innerHTML = value;
4867     }
4868    
4869 });
4870
4871  
4872
4873  /*
4874  * - LGPL
4875  *
4876  * pagination
4877  * 
4878  */
4879
4880 /**
4881  * @class Roo.bootstrap.Pagination
4882  * @extends Roo.bootstrap.Component
4883  * Bootstrap Pagination class
4884  * @cfg {String} size xs | sm | md | lg
4885  * @cfg {Boolean} inverse false | true
4886  * 
4887  * @constructor
4888  * Create a new Pagination
4889  * @param {Object} config The config object
4890  */
4891
4892 Roo.bootstrap.Pagination = function(config){
4893     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4894 };
4895
4896 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4897     
4898     cls: false,
4899     size: false,
4900     inverse: false,
4901     
4902     getAutoCreate : function(){
4903         var cfg = {
4904             tag: 'ul',
4905                 cls: 'pagination'
4906         };
4907         if (this.inverse) {
4908             cfg.cls += ' inverse';
4909         }
4910         if (this.html) {
4911             cfg.html=this.html;
4912         }
4913         if (this.cls) {
4914             cfg.cls += " " + this.cls;
4915         }
4916         return cfg;
4917     }
4918    
4919 });
4920
4921  
4922
4923  /*
4924  * - LGPL
4925  *
4926  * Pagination item
4927  * 
4928  */
4929
4930
4931 /**
4932  * @class Roo.bootstrap.PaginationItem
4933  * @extends Roo.bootstrap.Component
4934  * Bootstrap PaginationItem class
4935  * @cfg {String} html text
4936  * @cfg {String} href the link
4937  * @cfg {Boolean} preventDefault (true | false) default true
4938  * @cfg {Boolean} active (true | false) default false
4939  * @cfg {Boolean} disabled default false
4940  * 
4941  * 
4942  * @constructor
4943  * Create a new PaginationItem
4944  * @param {Object} config The config object
4945  */
4946
4947
4948 Roo.bootstrap.PaginationItem = function(config){
4949     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4950     this.addEvents({
4951         // raw events
4952         /**
4953          * @event click
4954          * The raw click event for the entire grid.
4955          * @param {Roo.EventObject} e
4956          */
4957         "click" : true
4958     });
4959 };
4960
4961 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4962     
4963     href : false,
4964     html : false,
4965     preventDefault: true,
4966     active : false,
4967     cls : false,
4968     disabled: false,
4969     
4970     getAutoCreate : function(){
4971         var cfg= {
4972             tag: 'li',
4973             cn: [
4974                 {
4975                     tag : 'a',
4976                     href : this.href ? this.href : '#',
4977                     html : this.html ? this.html : ''
4978                 }
4979             ]
4980         };
4981         
4982         if(this.cls){
4983             cfg.cls = this.cls;
4984         }
4985         
4986         if(this.disabled){
4987             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4988         }
4989         
4990         if(this.active){
4991             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4992         }
4993         
4994         return cfg;
4995     },
4996     
4997     initEvents: function() {
4998         
4999         this.el.on('click', this.onClick, this);
5000         
5001     },
5002     onClick : function(e)
5003     {
5004         Roo.log('PaginationItem on click ');
5005         if(this.preventDefault){
5006             e.preventDefault();
5007         }
5008         
5009         if(this.disabled){
5010             return;
5011         }
5012         
5013         this.fireEvent('click', this, e);
5014     }
5015    
5016 });
5017
5018  
5019
5020  /*
5021  * - LGPL
5022  *
5023  * slider
5024  * 
5025  */
5026
5027
5028 /**
5029  * @class Roo.bootstrap.Slider
5030  * @extends Roo.bootstrap.Component
5031  * Bootstrap Slider class
5032  *    
5033  * @constructor
5034  * Create a new Slider
5035  * @param {Object} config The config object
5036  */
5037
5038 Roo.bootstrap.Slider = function(config){
5039     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5040 };
5041
5042 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5043     
5044     getAutoCreate : function(){
5045         
5046         var cfg = {
5047             tag: 'div',
5048             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5049             cn: [
5050                 {
5051                     tag: 'a',
5052                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5053                 }
5054             ]
5055         };
5056         
5057         return cfg;
5058     }
5059    
5060 });
5061
5062  /*
5063  * Based on:
5064  * Ext JS Library 1.1.1
5065  * Copyright(c) 2006-2007, Ext JS, LLC.
5066  *
5067  * Originally Released Under LGPL - original licence link has changed is not relivant.
5068  *
5069  * Fork - LGPL
5070  * <script type="text/javascript">
5071  */
5072  
5073
5074 /**
5075  * @class Roo.grid.ColumnModel
5076  * @extends Roo.util.Observable
5077  * This is the default implementation of a ColumnModel used by the Grid. It defines
5078  * the columns in the grid.
5079  * <br>Usage:<br>
5080  <pre><code>
5081  var colModel = new Roo.grid.ColumnModel([
5082         {header: "Ticker", width: 60, sortable: true, locked: true},
5083         {header: "Company Name", width: 150, sortable: true},
5084         {header: "Market Cap.", width: 100, sortable: true},
5085         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5086         {header: "Employees", width: 100, sortable: true, resizable: false}
5087  ]);
5088  </code></pre>
5089  * <p>
5090  
5091  * The config options listed for this class are options which may appear in each
5092  * individual column definition.
5093  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5094  * @constructor
5095  * @param {Object} config An Array of column config objects. See this class's
5096  * config objects for details.
5097 */
5098 Roo.grid.ColumnModel = function(config){
5099         /**
5100      * The config passed into the constructor
5101      */
5102     this.config = config;
5103     this.lookup = {};
5104
5105     // if no id, create one
5106     // if the column does not have a dataIndex mapping,
5107     // map it to the order it is in the config
5108     for(var i = 0, len = config.length; i < len; i++){
5109         var c = config[i];
5110         if(typeof c.dataIndex == "undefined"){
5111             c.dataIndex = i;
5112         }
5113         if(typeof c.renderer == "string"){
5114             c.renderer = Roo.util.Format[c.renderer];
5115         }
5116         if(typeof c.id == "undefined"){
5117             c.id = Roo.id();
5118         }
5119         if(c.editor && c.editor.xtype){
5120             c.editor  = Roo.factory(c.editor, Roo.grid);
5121         }
5122         if(c.editor && c.editor.isFormField){
5123             c.editor = new Roo.grid.GridEditor(c.editor);
5124         }
5125         this.lookup[c.id] = c;
5126     }
5127
5128     /**
5129      * The width of columns which have no width specified (defaults to 100)
5130      * @type Number
5131      */
5132     this.defaultWidth = 100;
5133
5134     /**
5135      * Default sortable of columns which have no sortable specified (defaults to false)
5136      * @type Boolean
5137      */
5138     this.defaultSortable = false;
5139
5140     this.addEvents({
5141         /**
5142              * @event widthchange
5143              * Fires when the width of a column changes.
5144              * @param {ColumnModel} this
5145              * @param {Number} columnIndex The column index
5146              * @param {Number} newWidth The new width
5147              */
5148             "widthchange": true,
5149         /**
5150              * @event headerchange
5151              * Fires when the text of a header changes.
5152              * @param {ColumnModel} this
5153              * @param {Number} columnIndex The column index
5154              * @param {Number} newText The new header text
5155              */
5156             "headerchange": true,
5157         /**
5158              * @event hiddenchange
5159              * Fires when a column is hidden or "unhidden".
5160              * @param {ColumnModel} this
5161              * @param {Number} columnIndex The column index
5162              * @param {Boolean} hidden true if hidden, false otherwise
5163              */
5164             "hiddenchange": true,
5165             /**
5166          * @event columnmoved
5167          * Fires when a column is moved.
5168          * @param {ColumnModel} this
5169          * @param {Number} oldIndex
5170          * @param {Number} newIndex
5171          */
5172         "columnmoved" : true,
5173         /**
5174          * @event columlockchange
5175          * Fires when a column's locked state is changed
5176          * @param {ColumnModel} this
5177          * @param {Number} colIndex
5178          * @param {Boolean} locked true if locked
5179          */
5180         "columnlockchange" : true
5181     });
5182     Roo.grid.ColumnModel.superclass.constructor.call(this);
5183 };
5184 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5185     /**
5186      * @cfg {String} header The header text to display in the Grid view.
5187      */
5188     /**
5189      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5190      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5191      * specified, the column's index is used as an index into the Record's data Array.
5192      */
5193     /**
5194      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5195      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5196      */
5197     /**
5198      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5199      * Defaults to the value of the {@link #defaultSortable} property.
5200      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5201      */
5202     /**
5203      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5204      */
5205     /**
5206      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5207      */
5208     /**
5209      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5210      */
5211     /**
5212      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5213      */
5214     /**
5215      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5216      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5217      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5218      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5219      */
5220        /**
5221      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5222      */
5223     /**
5224      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5225      */
5226     /**
5227      * @cfg {String} cursor (Optional)
5228      */
5229     /**
5230      * @cfg {String} tooltip (Optional)
5231      */
5232     /**
5233      * @cfg {Number} xs (Optional)
5234      */
5235     /**
5236      * @cfg {Number} sm (Optional)
5237      */
5238     /**
5239      * @cfg {Number} md (Optional)
5240      */
5241     /**
5242      * @cfg {Number} lg (Optional)
5243      */
5244     /**
5245      * Returns the id of the column at the specified index.
5246      * @param {Number} index The column index
5247      * @return {String} the id
5248      */
5249     getColumnId : function(index){
5250         return this.config[index].id;
5251     },
5252
5253     /**
5254      * Returns the column for a specified id.
5255      * @param {String} id The column id
5256      * @return {Object} the column
5257      */
5258     getColumnById : function(id){
5259         return this.lookup[id];
5260     },
5261
5262     
5263     /**
5264      * Returns the column for a specified dataIndex.
5265      * @param {String} dataIndex The column dataIndex
5266      * @return {Object|Boolean} the column or false if not found
5267      */
5268     getColumnByDataIndex: function(dataIndex){
5269         var index = this.findColumnIndex(dataIndex);
5270         return index > -1 ? this.config[index] : false;
5271     },
5272     
5273     /**
5274      * Returns the index for a specified column id.
5275      * @param {String} id The column id
5276      * @return {Number} the index, or -1 if not found
5277      */
5278     getIndexById : function(id){
5279         for(var i = 0, len = this.config.length; i < len; i++){
5280             if(this.config[i].id == id){
5281                 return i;
5282             }
5283         }
5284         return -1;
5285     },
5286     
5287     /**
5288      * Returns the index for a specified column dataIndex.
5289      * @param {String} dataIndex The column dataIndex
5290      * @return {Number} the index, or -1 if not found
5291      */
5292     
5293     findColumnIndex : function(dataIndex){
5294         for(var i = 0, len = this.config.length; i < len; i++){
5295             if(this.config[i].dataIndex == dataIndex){
5296                 return i;
5297             }
5298         }
5299         return -1;
5300     },
5301     
5302     
5303     moveColumn : function(oldIndex, newIndex){
5304         var c = this.config[oldIndex];
5305         this.config.splice(oldIndex, 1);
5306         this.config.splice(newIndex, 0, c);
5307         this.dataMap = null;
5308         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5309     },
5310
5311     isLocked : function(colIndex){
5312         return this.config[colIndex].locked === true;
5313     },
5314
5315     setLocked : function(colIndex, value, suppressEvent){
5316         if(this.isLocked(colIndex) == value){
5317             return;
5318         }
5319         this.config[colIndex].locked = value;
5320         if(!suppressEvent){
5321             this.fireEvent("columnlockchange", this, colIndex, value);
5322         }
5323     },
5324
5325     getTotalLockedWidth : function(){
5326         var totalWidth = 0;
5327         for(var i = 0; i < this.config.length; i++){
5328             if(this.isLocked(i) && !this.isHidden(i)){
5329                 this.totalWidth += this.getColumnWidth(i);
5330             }
5331         }
5332         return totalWidth;
5333     },
5334
5335     getLockedCount : function(){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(!this.isLocked(i)){
5338                 return i;
5339             }
5340         }
5341         
5342         return this.config.length;
5343     },
5344
5345     /**
5346      * Returns the number of columns.
5347      * @return {Number}
5348      */
5349     getColumnCount : function(visibleOnly){
5350         if(visibleOnly === true){
5351             var c = 0;
5352             for(var i = 0, len = this.config.length; i < len; i++){
5353                 if(!this.isHidden(i)){
5354                     c++;
5355                 }
5356             }
5357             return c;
5358         }
5359         return this.config.length;
5360     },
5361
5362     /**
5363      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5364      * @param {Function} fn
5365      * @param {Object} scope (optional)
5366      * @return {Array} result
5367      */
5368     getColumnsBy : function(fn, scope){
5369         var r = [];
5370         for(var i = 0, len = this.config.length; i < len; i++){
5371             var c = this.config[i];
5372             if(fn.call(scope||this, c, i) === true){
5373                 r[r.length] = c;
5374             }
5375         }
5376         return r;
5377     },
5378
5379     /**
5380      * Returns true if the specified column is sortable.
5381      * @param {Number} col The column index
5382      * @return {Boolean}
5383      */
5384     isSortable : function(col){
5385         if(typeof this.config[col].sortable == "undefined"){
5386             return this.defaultSortable;
5387         }
5388         return this.config[col].sortable;
5389     },
5390
5391     /**
5392      * Returns the rendering (formatting) function defined for the column.
5393      * @param {Number} col The column index.
5394      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5395      */
5396     getRenderer : function(col){
5397         if(!this.config[col].renderer){
5398             return Roo.grid.ColumnModel.defaultRenderer;
5399         }
5400         return this.config[col].renderer;
5401     },
5402
5403     /**
5404      * Sets the rendering (formatting) function for a column.
5405      * @param {Number} col The column index
5406      * @param {Function} fn The function to use to process the cell's raw data
5407      * to return HTML markup for the grid view. The render function is called with
5408      * the following parameters:<ul>
5409      * <li>Data value.</li>
5410      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5411      * <li>css A CSS style string to apply to the table cell.</li>
5412      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5413      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5414      * <li>Row index</li>
5415      * <li>Column index</li>
5416      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5417      */
5418     setRenderer : function(col, fn){
5419         this.config[col].renderer = fn;
5420     },
5421
5422     /**
5423      * Returns the width for the specified column.
5424      * @param {Number} col The column index
5425      * @return {Number}
5426      */
5427     getColumnWidth : function(col){
5428         return this.config[col].width * 1 || this.defaultWidth;
5429     },
5430
5431     /**
5432      * Sets the width for a column.
5433      * @param {Number} col The column index
5434      * @param {Number} width The new width
5435      */
5436     setColumnWidth : function(col, width, suppressEvent){
5437         this.config[col].width = width;
5438         this.totalWidth = null;
5439         if(!suppressEvent){
5440              this.fireEvent("widthchange", this, col, width);
5441         }
5442     },
5443
5444     /**
5445      * Returns the total width of all columns.
5446      * @param {Boolean} includeHidden True to include hidden column widths
5447      * @return {Number}
5448      */
5449     getTotalWidth : function(includeHidden){
5450         if(!this.totalWidth){
5451             this.totalWidth = 0;
5452             for(var i = 0, len = this.config.length; i < len; i++){
5453                 if(includeHidden || !this.isHidden(i)){
5454                     this.totalWidth += this.getColumnWidth(i);
5455                 }
5456             }
5457         }
5458         return this.totalWidth;
5459     },
5460
5461     /**
5462      * Returns the header for the specified column.
5463      * @param {Number} col The column index
5464      * @return {String}
5465      */
5466     getColumnHeader : function(col){
5467         return this.config[col].header;
5468     },
5469
5470     /**
5471      * Sets the header for a column.
5472      * @param {Number} col The column index
5473      * @param {String} header The new header
5474      */
5475     setColumnHeader : function(col, header){
5476         this.config[col].header = header;
5477         this.fireEvent("headerchange", this, col, header);
5478     },
5479
5480     /**
5481      * Returns the tooltip for the specified column.
5482      * @param {Number} col The column index
5483      * @return {String}
5484      */
5485     getColumnTooltip : function(col){
5486             return this.config[col].tooltip;
5487     },
5488     /**
5489      * Sets the tooltip for a column.
5490      * @param {Number} col The column index
5491      * @param {String} tooltip The new tooltip
5492      */
5493     setColumnTooltip : function(col, tooltip){
5494             this.config[col].tooltip = tooltip;
5495     },
5496
5497     /**
5498      * Returns the dataIndex for the specified column.
5499      * @param {Number} col The column index
5500      * @return {Number}
5501      */
5502     getDataIndex : function(col){
5503         return this.config[col].dataIndex;
5504     },
5505
5506     /**
5507      * Sets the dataIndex for a column.
5508      * @param {Number} col The column index
5509      * @param {Number} dataIndex The new dataIndex
5510      */
5511     setDataIndex : function(col, dataIndex){
5512         this.config[col].dataIndex = dataIndex;
5513     },
5514
5515     
5516     
5517     /**
5518      * Returns true if the cell is editable.
5519      * @param {Number} colIndex The column index
5520      * @param {Number} rowIndex The row index - this is nto actually used..?
5521      * @return {Boolean}
5522      */
5523     isCellEditable : function(colIndex, rowIndex){
5524         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5525     },
5526
5527     /**
5528      * Returns the editor defined for the cell/column.
5529      * return false or null to disable editing.
5530      * @param {Number} colIndex The column index
5531      * @param {Number} rowIndex The row index
5532      * @return {Object}
5533      */
5534     getCellEditor : function(colIndex, rowIndex){
5535         return this.config[colIndex].editor;
5536     },
5537
5538     /**
5539      * Sets if a column is editable.
5540      * @param {Number} col The column index
5541      * @param {Boolean} editable True if the column is editable
5542      */
5543     setEditable : function(col, editable){
5544         this.config[col].editable = editable;
5545     },
5546
5547
5548     /**
5549      * Returns true if the column is hidden.
5550      * @param {Number} colIndex The column index
5551      * @return {Boolean}
5552      */
5553     isHidden : function(colIndex){
5554         return this.config[colIndex].hidden;
5555     },
5556
5557
5558     /**
5559      * Returns true if the column width cannot be changed
5560      */
5561     isFixed : function(colIndex){
5562         return this.config[colIndex].fixed;
5563     },
5564
5565     /**
5566      * Returns true if the column can be resized
5567      * @return {Boolean}
5568      */
5569     isResizable : function(colIndex){
5570         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5571     },
5572     /**
5573      * Sets if a column is hidden.
5574      * @param {Number} colIndex The column index
5575      * @param {Boolean} hidden True if the column is hidden
5576      */
5577     setHidden : function(colIndex, hidden){
5578         this.config[colIndex].hidden = hidden;
5579         this.totalWidth = null;
5580         this.fireEvent("hiddenchange", this, colIndex, hidden);
5581     },
5582
5583     /**
5584      * Sets the editor for a column.
5585      * @param {Number} col The column index
5586      * @param {Object} editor The editor object
5587      */
5588     setEditor : function(col, editor){
5589         this.config[col].editor = editor;
5590     }
5591 });
5592
5593 Roo.grid.ColumnModel.defaultRenderer = function(value)
5594 {
5595     if(typeof value == "object") {
5596         return value;
5597     }
5598         if(typeof value == "string" && value.length < 1){
5599             return "&#160;";
5600         }
5601     
5602         return String.format("{0}", value);
5603 };
5604
5605 // Alias for backwards compatibility
5606 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5607 /*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 /**
5619  * @class Roo.LoadMask
5620  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5621  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5622  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5623  * element's UpdateManager load indicator and will be destroyed after the initial load.
5624  * @constructor
5625  * Create a new LoadMask
5626  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5627  * @param {Object} config The config object
5628  */
5629 Roo.LoadMask = function(el, config){
5630     this.el = Roo.get(el);
5631     Roo.apply(this, config);
5632     if(this.store){
5633         this.store.on('beforeload', this.onBeforeLoad, this);
5634         this.store.on('load', this.onLoad, this);
5635         this.store.on('loadexception', this.onLoadException, this);
5636         this.removeMask = false;
5637     }else{
5638         var um = this.el.getUpdateManager();
5639         um.showLoadIndicator = false; // disable the default indicator
5640         um.on('beforeupdate', this.onBeforeLoad, this);
5641         um.on('update', this.onLoad, this);
5642         um.on('failure', this.onLoad, this);
5643         this.removeMask = true;
5644     }
5645 };
5646
5647 Roo.LoadMask.prototype = {
5648     /**
5649      * @cfg {Boolean} removeMask
5650      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5651      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5652      */
5653     /**
5654      * @cfg {String} msg
5655      * The text to display in a centered loading message box (defaults to 'Loading...')
5656      */
5657     msg : 'Loading...',
5658     /**
5659      * @cfg {String} msgCls
5660      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5661      */
5662     msgCls : 'x-mask-loading',
5663
5664     /**
5665      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5666      * @type Boolean
5667      */
5668     disabled: false,
5669
5670     /**
5671      * Disables the mask to prevent it from being displayed
5672      */
5673     disable : function(){
5674        this.disabled = true;
5675     },
5676
5677     /**
5678      * Enables the mask so that it can be displayed
5679      */
5680     enable : function(){
5681         this.disabled = false;
5682     },
5683     
5684     onLoadException : function()
5685     {
5686         Roo.log(arguments);
5687         
5688         if (typeof(arguments[3]) != 'undefined') {
5689             Roo.MessageBox.alert("Error loading",arguments[3]);
5690         } 
5691         /*
5692         try {
5693             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5694                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5695             }   
5696         } catch(e) {
5697             
5698         }
5699         */
5700     
5701         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5702     },
5703     // private
5704     onLoad : function()
5705     {
5706         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5707     },
5708
5709     // private
5710     onBeforeLoad : function(){
5711         if(!this.disabled){
5712             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5713         }
5714     },
5715
5716     // private
5717     destroy : function(){
5718         if(this.store){
5719             this.store.un('beforeload', this.onBeforeLoad, this);
5720             this.store.un('load', this.onLoad, this);
5721             this.store.un('loadexception', this.onLoadException, this);
5722         }else{
5723             var um = this.el.getUpdateManager();
5724             um.un('beforeupdate', this.onBeforeLoad, this);
5725             um.un('update', this.onLoad, this);
5726             um.un('failure', this.onLoad, this);
5727         }
5728     }
5729 };/*
5730  * - LGPL
5731  *
5732  * table
5733  * 
5734  */
5735
5736 /**
5737  * @class Roo.bootstrap.Table
5738  * @extends Roo.bootstrap.Component
5739  * Bootstrap Table class
5740  * @cfg {String} cls table class
5741  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5742  * @cfg {String} bgcolor Specifies the background color for a table
5743  * @cfg {Number} border Specifies whether the table cells should have borders or not
5744  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5745  * @cfg {Number} cellspacing Specifies the space between cells
5746  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5747  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5748  * @cfg {String} sortable Specifies that the table should be sortable
5749  * @cfg {String} summary Specifies a summary of the content of a table
5750  * @cfg {Number} width Specifies the width of a table
5751  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5752  * 
5753  * @cfg {boolean} striped Should the rows be alternative striped
5754  * @cfg {boolean} bordered Add borders to the table
5755  * @cfg {boolean} hover Add hover highlighting
5756  * @cfg {boolean} condensed Format condensed
5757  * @cfg {boolean} responsive Format condensed
5758  * @cfg {Boolean} loadMask (true|false) default false
5759  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5760  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5761  * @cfg {Boolean} rowSelection (true|false) default false
5762  * @cfg {Boolean} cellSelection (true|false) default false
5763  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5764  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5765  
5766  * 
5767  * @constructor
5768  * Create a new Table
5769  * @param {Object} config The config object
5770  */
5771
5772 Roo.bootstrap.Table = function(config){
5773     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5774     
5775   
5776     
5777     // BC...
5778     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5779     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5780     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5781     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5782     
5783     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5784     if (this.sm) {
5785         this.sm.grid = this;
5786         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5787         this.sm = this.selModel;
5788         this.sm.xmodule = this.xmodule || false;
5789     }
5790     
5791     if (this.cm && typeof(this.cm.config) == 'undefined') {
5792         this.colModel = new Roo.grid.ColumnModel(this.cm);
5793         this.cm = this.colModel;
5794         this.cm.xmodule = this.xmodule || false;
5795     }
5796     if (this.store) {
5797         this.store= Roo.factory(this.store, Roo.data);
5798         this.ds = this.store;
5799         this.ds.xmodule = this.xmodule || false;
5800          
5801     }
5802     if (this.footer && this.store) {
5803         this.footer.dataSource = this.ds;
5804         this.footer = Roo.factory(this.footer);
5805     }
5806     
5807     /** @private */
5808     this.addEvents({
5809         /**
5810          * @event cellclick
5811          * Fires when a cell is clicked
5812          * @param {Roo.bootstrap.Table} this
5813          * @param {Roo.Element} el
5814          * @param {Number} rowIndex
5815          * @param {Number} columnIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "cellclick" : true,
5819         /**
5820          * @event celldblclick
5821          * Fires when a cell is double clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "celldblclick" : true,
5829         /**
5830          * @event rowclick
5831          * Fires when a row is clicked
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Roo.EventObject} e
5836          */
5837         "rowclick" : true,
5838         /**
5839          * @event rowdblclick
5840          * Fires when a row is double clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Roo.EventObject} e
5845          */
5846         "rowdblclick" : true,
5847         /**
5848          * @event mouseover
5849          * Fires when a mouseover occur
5850          * @param {Roo.bootstrap.Table} this
5851          * @param {Roo.Element} el
5852          * @param {Number} rowIndex
5853          * @param {Number} columnIndex
5854          * @param {Roo.EventObject} e
5855          */
5856         "mouseover" : true,
5857         /**
5858          * @event mouseout
5859          * Fires when a mouseout occur
5860          * @param {Roo.bootstrap.Table} this
5861          * @param {Roo.Element} el
5862          * @param {Number} rowIndex
5863          * @param {Number} columnIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "mouseout" : true,
5867         /**
5868          * @event rowclass
5869          * Fires when a row is rendered, so you can change add a style to it.
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5872          */
5873         'rowclass' : true,
5874           /**
5875          * @event rowsrendered
5876          * Fires when all the  rows have been rendered
5877          * @param {Roo.bootstrap.Table} this
5878          */
5879         'rowsrendered' : true,
5880         /**
5881          * @event contextmenu
5882          * The raw contextmenu event for the entire grid.
5883          * @param {Roo.EventObject} e
5884          */
5885         "contextmenu" : true,
5886         /**
5887          * @event rowcontextmenu
5888          * Fires when a row is right clicked
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Number} rowIndex
5891          * @param {Roo.EventObject} e
5892          */
5893         "rowcontextmenu" : true,
5894         /**
5895          * @event cellcontextmenu
5896          * Fires when a cell is right clicked
5897          * @param {Roo.bootstrap.Table} this
5898          * @param {Number} rowIndex
5899          * @param {Number} cellIndex
5900          * @param {Roo.EventObject} e
5901          */
5902          "cellcontextmenu" : true,
5903          /**
5904          * @event headercontextmenu
5905          * Fires when a header is right clicked
5906          * @param {Roo.bootstrap.Table} this
5907          * @param {Number} columnIndex
5908          * @param {Roo.EventObject} e
5909          */
5910         "headercontextmenu" : true
5911     });
5912 };
5913
5914 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5915     
5916     cls: false,
5917     align: false,
5918     bgcolor: false,
5919     border: false,
5920     cellpadding: false,
5921     cellspacing: false,
5922     frame: false,
5923     rules: false,
5924     sortable: false,
5925     summary: false,
5926     width: false,
5927     striped : false,
5928     scrollBody : false,
5929     bordered: false,
5930     hover:  false,
5931     condensed : false,
5932     responsive : false,
5933     sm : false,
5934     cm : false,
5935     store : false,
5936     loadMask : false,
5937     footerShow : true,
5938     headerShow : true,
5939   
5940     rowSelection : false,
5941     cellSelection : false,
5942     layout : false,
5943     
5944     // Roo.Element - the tbody
5945     mainBody: false,
5946     // Roo.Element - thead element
5947     mainHead: false,
5948     
5949     container: false, // used by gridpanel...
5950     
5951     getAutoCreate : function()
5952     {
5953         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5954         
5955         cfg = {
5956             tag: 'table',
5957             cls : 'table',
5958             cn : []
5959         };
5960         if (this.scrollBody) {
5961             cfg.cls += ' table-body-fixed';
5962         }    
5963         if (this.striped) {
5964             cfg.cls += ' table-striped';
5965         }
5966         
5967         if (this.hover) {
5968             cfg.cls += ' table-hover';
5969         }
5970         if (this.bordered) {
5971             cfg.cls += ' table-bordered';
5972         }
5973         if (this.condensed) {
5974             cfg.cls += ' table-condensed';
5975         }
5976         if (this.responsive) {
5977             cfg.cls += ' table-responsive';
5978         }
5979         
5980         if (this.cls) {
5981             cfg.cls+=  ' ' +this.cls;
5982         }
5983         
5984         // this lot should be simplifed...
5985         
5986         if (this.align) {
5987             cfg.align=this.align;
5988         }
5989         if (this.bgcolor) {
5990             cfg.bgcolor=this.bgcolor;
5991         }
5992         if (this.border) {
5993             cfg.border=this.border;
5994         }
5995         if (this.cellpadding) {
5996             cfg.cellpadding=this.cellpadding;
5997         }
5998         if (this.cellspacing) {
5999             cfg.cellspacing=this.cellspacing;
6000         }
6001         if (this.frame) {
6002             cfg.frame=this.frame;
6003         }
6004         if (this.rules) {
6005             cfg.rules=this.rules;
6006         }
6007         if (this.sortable) {
6008             cfg.sortable=this.sortable;
6009         }
6010         if (this.summary) {
6011             cfg.summary=this.summary;
6012         }
6013         if (this.width) {
6014             cfg.width=this.width;
6015         }
6016         if (this.layout) {
6017             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6018         }
6019         
6020         if(this.store || this.cm){
6021             if(this.headerShow){
6022                 cfg.cn.push(this.renderHeader());
6023             }
6024             
6025             cfg.cn.push(this.renderBody());
6026             
6027             if(this.footerShow){
6028                 cfg.cn.push(this.renderFooter());
6029             }
6030             // where does this come from?
6031             //cfg.cls+=  ' TableGrid';
6032         }
6033         
6034         return { cn : [ cfg ] };
6035     },
6036     
6037     initEvents : function()
6038     {   
6039         if(!this.store || !this.cm){
6040             return;
6041         }
6042         if (this.selModel) {
6043             this.selModel.initEvents();
6044         }
6045         
6046         
6047         //Roo.log('initEvents with ds!!!!');
6048         
6049         this.mainBody = this.el.select('tbody', true).first();
6050         this.mainHead = this.el.select('thead', true).first();
6051         
6052         
6053         
6054         
6055         var _this = this;
6056         
6057         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6058             e.on('click', _this.sort, _this);
6059         });
6060         
6061         this.mainBody.on("click", this.onClick, this);
6062         this.mainBody.on("dblclick", this.onDblClick, this);
6063         
6064         // why is this done????? = it breaks dialogs??
6065         //this.parent().el.setStyle('position', 'relative');
6066         
6067         
6068         if (this.footer) {
6069             this.footer.parentId = this.id;
6070             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6071         } 
6072         
6073         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6074         
6075         this.store.on('load', this.onLoad, this);
6076         this.store.on('beforeload', this.onBeforeLoad, this);
6077         this.store.on('update', this.onUpdate, this);
6078         this.store.on('add', this.onAdd, this);
6079         this.store.on("clear", this.clear, this);
6080         
6081         this.el.on("contextmenu", this.onContextMenu, this);
6082         
6083         this.mainBody.on('scroll', this.onBodyScroll, this);
6084         
6085         
6086     },
6087     
6088     onContextMenu : function(e, t)
6089     {
6090         this.processEvent("contextmenu", e);
6091     },
6092     
6093     processEvent : function(name, e)
6094     {
6095         if (name != 'touchstart' ) {
6096             this.fireEvent(name, e);    
6097         }
6098         
6099         var t = e.getTarget();
6100         
6101         var cell = Roo.get(t);
6102         
6103         if(!cell){
6104             return;
6105         }
6106         
6107         if(cell.findParent('tfoot', false, true)){
6108             return;
6109         }
6110         
6111         if(cell.findParent('thead', false, true)){
6112             
6113             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6114                 cell = Roo.get(t).findParent('th', false, true);
6115                 if (!cell) {
6116                     Roo.log("failed to find th in thead?");
6117                     Roo.log(e.getTarget());
6118                     return;
6119                 }
6120             }
6121             
6122             var cellIndex = cell.dom.cellIndex;
6123             
6124             var ename = name == 'touchstart' ? 'click' : name;
6125             this.fireEvent("header" + ename, this, cellIndex, e);
6126             
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = Roo.get(t).findParent('td', false, true);
6132             if (!cell) {
6133                 Roo.log("failed to find th in tbody?");
6134                 Roo.log(e.getTarget());
6135                 return;
6136             }
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1;
6142         
6143         if(row !== false){
6144             
6145             this.fireEvent("row" + name, this, rowIndex, e);
6146             
6147             if(cell !== false){
6148             
6149                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6150             }
6151         }
6152         
6153     },
6154     
6155     onMouseover : function(e, el)
6156     {
6157         var cell = Roo.get(el);
6158         
6159         if(!cell){
6160             return;
6161         }
6162         
6163         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6164             cell = cell.findParent('td', false, true);
6165         }
6166         
6167         var row = cell.findParent('tr', false, true);
6168         var cellIndex = cell.dom.cellIndex;
6169         var rowIndex = row.dom.rowIndex - 1; // start from 0
6170         
6171         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6172         
6173     },
6174     
6175     onMouseout : function(e, el)
6176     {
6177         var cell = Roo.get(el);
6178         
6179         if(!cell){
6180             return;
6181         }
6182         
6183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6184             cell = cell.findParent('td', false, true);
6185         }
6186         
6187         var row = cell.findParent('tr', false, true);
6188         var cellIndex = cell.dom.cellIndex;
6189         var rowIndex = row.dom.rowIndex - 1; // start from 0
6190         
6191         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6192         
6193     },
6194     
6195     onClick : function(e, el)
6196     {
6197         var cell = Roo.get(el);
6198         
6199         if(!cell || (!this.cellSelection && !this.rowSelection)){
6200             return;
6201         }
6202         
6203         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6204             cell = cell.findParent('td', false, true);
6205         }
6206         
6207         if(!cell || typeof(cell) == 'undefined'){
6208             return;
6209         }
6210         
6211         var row = cell.findParent('tr', false, true);
6212         
6213         if(!row || typeof(row) == 'undefined'){
6214             return;
6215         }
6216         
6217         var cellIndex = cell.dom.cellIndex;
6218         var rowIndex = this.getRowIndex(row);
6219         
6220         // why??? - should these not be based on SelectionModel?
6221         if(this.cellSelection){
6222             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6223         }
6224         
6225         if(this.rowSelection){
6226             this.fireEvent('rowclick', this, row, rowIndex, e);
6227         }
6228         
6229         
6230     },
6231         
6232     onDblClick : function(e,el)
6233     {
6234         var cell = Roo.get(el);
6235         
6236         if(!cell || (!this.cellSelection && !this.rowSelection)){
6237             return;
6238         }
6239         
6240         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6241             cell = cell.findParent('td', false, true);
6242         }
6243         
6244         if(!cell || typeof(cell) == 'undefined'){
6245             return;
6246         }
6247         
6248         var row = cell.findParent('tr', false, true);
6249         
6250         if(!row || typeof(row) == 'undefined'){
6251             return;
6252         }
6253         
6254         var cellIndex = cell.dom.cellIndex;
6255         var rowIndex = this.getRowIndex(row);
6256         
6257         if(this.cellSelection){
6258             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6259         }
6260         
6261         if(this.rowSelection){
6262             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6263         }
6264     },
6265     
6266     sort : function(e,el)
6267     {
6268         var col = Roo.get(el);
6269         
6270         if(!col.hasClass('sortable')){
6271             return;
6272         }
6273         
6274         var sort = col.attr('sort');
6275         var dir = 'ASC';
6276         
6277         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6278             dir = 'DESC';
6279         }
6280         
6281         this.store.sortInfo = {field : sort, direction : dir};
6282         
6283         if (this.footer) {
6284             Roo.log("calling footer first");
6285             this.footer.onClick('first');
6286         } else {
6287         
6288             this.store.load({ params : { start : 0 } });
6289         }
6290     },
6291     
6292     renderHeader : function()
6293     {
6294         var header = {
6295             tag: 'thead',
6296             cn : []
6297         };
6298         
6299         var cm = this.cm;
6300         this.totalWidth = 0;
6301         
6302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6303             
6304             var config = cm.config[i];
6305             
6306             var c = {
6307                 tag: 'th',
6308                 style : '',
6309                 html: cm.getColumnHeader(i)
6310             };
6311             
6312             var hh = '';
6313             
6314             if(typeof(config.sortable) != 'undefined' && config.sortable){
6315                 c.cls = 'sortable';
6316                 c.html = '<i class="glyphicon"></i>' + c.html;
6317             }
6318             
6319             if(typeof(config.lgHeader) != 'undefined'){
6320                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6321             }
6322             
6323             if(typeof(config.mdHeader) != 'undefined'){
6324                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6325             }
6326             
6327             if(typeof(config.smHeader) != 'undefined'){
6328                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6329             }
6330             
6331             if(typeof(config.xsHeader) != 'undefined'){
6332                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6333             }
6334             
6335             if(hh.length){
6336                 c.html = hh;
6337             }
6338             
6339             if(typeof(config.tooltip) != 'undefined'){
6340                 c.tooltip = config.tooltip;
6341             }
6342             
6343             if(typeof(config.colspan) != 'undefined'){
6344                 c.colspan = config.colspan;
6345             }
6346             
6347             if(typeof(config.hidden) != 'undefined' && config.hidden){
6348                 c.style += ' display:none;';
6349             }
6350             
6351             if(typeof(config.dataIndex) != 'undefined'){
6352                 c.sort = config.dataIndex;
6353             }
6354             
6355            
6356             
6357             if(typeof(config.align) != 'undefined' && config.align.length){
6358                 c.style += ' text-align:' + config.align + ';';
6359             }
6360             
6361             if(typeof(config.width) != 'undefined'){
6362                 c.style += ' width:' + config.width + 'px;';
6363                 this.totalWidth += config.width;
6364             } else {
6365                 this.totalWidth += 100; // assume minimum of 100 per column?
6366             }
6367             
6368             if(typeof(config.cls) != 'undefined'){
6369                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6370             }
6371             
6372             ['xs','sm','md','lg'].map(function(size){
6373                 
6374                 if(typeof(config[size]) == 'undefined'){
6375                     return;
6376                 }
6377                 
6378                 if (!config[size]) { // 0 = hidden
6379                     c.cls += ' hidden-' + size;
6380                     return;
6381                 }
6382                 
6383                 c.cls += ' col-' + size + '-' + config[size];
6384
6385             });
6386             
6387             header.cn.push(c)
6388         }
6389         
6390         return header;
6391     },
6392     
6393     renderBody : function()
6394     {
6395         var body = {
6396             tag: 'tbody',
6397             cn : [
6398                 {
6399                     tag: 'tr',
6400                     cn : [
6401                         {
6402                             tag : 'td',
6403                             colspan :  this.cm.getColumnCount()
6404                         }
6405                     ]
6406                 }
6407             ]
6408         };
6409         
6410         return body;
6411     },
6412     
6413     renderFooter : function()
6414     {
6415         var footer = {
6416             tag: 'tfoot',
6417             cn : [
6418                 {
6419                     tag: 'tr',
6420                     cn : [
6421                         {
6422                             tag : 'td',
6423                             colspan :  this.cm.getColumnCount()
6424                         }
6425                     ]
6426                 }
6427             ]
6428         };
6429         
6430         return footer;
6431     },
6432     
6433     
6434     
6435     onLoad : function()
6436     {
6437 //        Roo.log('ds onload');
6438         this.clear();
6439         
6440         var _this = this;
6441         var cm = this.cm;
6442         var ds = this.store;
6443         
6444         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6445             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6446             if (_this.store.sortInfo) {
6447                     
6448                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6449                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6450                 }
6451                 
6452                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6453                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6454                 }
6455             }
6456         });
6457         
6458         var tbody =  this.mainBody;
6459               
6460         if(ds.getCount() > 0){
6461             ds.data.each(function(d,rowIndex){
6462                 var row =  this.renderRow(cm, ds, rowIndex);
6463                 
6464                 tbody.createChild(row);
6465                 
6466                 var _this = this;
6467                 
6468                 if(row.cellObjects.length){
6469                     Roo.each(row.cellObjects, function(r){
6470                         _this.renderCellObject(r);
6471                     })
6472                 }
6473                 
6474             }, this);
6475         }
6476         
6477         Roo.each(this.el.select('tbody td', true).elements, function(e){
6478             e.on('mouseover', _this.onMouseover, _this);
6479         });
6480         
6481         Roo.each(this.el.select('tbody td', true).elements, function(e){
6482             e.on('mouseout', _this.onMouseout, _this);
6483         });
6484         this.fireEvent('rowsrendered', this);
6485         //if(this.loadMask){
6486         //    this.maskEl.hide();
6487         //}
6488         
6489         this.autoSize();
6490     },
6491     
6492     
6493     onUpdate : function(ds,record)
6494     {
6495         this.refreshRow(record);
6496         this.autoSize();
6497     },
6498     
6499     onRemove : function(ds, record, index, isUpdate){
6500         if(isUpdate !== true){
6501             this.fireEvent("beforerowremoved", this, index, record);
6502         }
6503         var bt = this.mainBody.dom;
6504         
6505         var rows = this.el.select('tbody > tr', true).elements;
6506         
6507         if(typeof(rows[index]) != 'undefined'){
6508             bt.removeChild(rows[index].dom);
6509         }
6510         
6511 //        if(bt.rows[index]){
6512 //            bt.removeChild(bt.rows[index]);
6513 //        }
6514         
6515         if(isUpdate !== true){
6516             //this.stripeRows(index);
6517             //this.syncRowHeights(index, index);
6518             //this.layout();
6519             this.fireEvent("rowremoved", this, index, record);
6520         }
6521     },
6522     
6523     onAdd : function(ds, records, rowIndex)
6524     {
6525         //Roo.log('on Add called');
6526         // - note this does not handle multiple adding very well..
6527         var bt = this.mainBody.dom;
6528         for (var i =0 ; i < records.length;i++) {
6529             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6530             //Roo.log(records[i]);
6531             //Roo.log(this.store.getAt(rowIndex+i));
6532             this.insertRow(this.store, rowIndex + i, false);
6533             return;
6534         }
6535         
6536     },
6537     
6538     
6539     refreshRow : function(record){
6540         var ds = this.store, index;
6541         if(typeof record == 'number'){
6542             index = record;
6543             record = ds.getAt(index);
6544         }else{
6545             index = ds.indexOf(record);
6546         }
6547         this.insertRow(ds, index, true);
6548         this.autoSize();
6549         this.onRemove(ds, record, index+1, true);
6550         this.autoSize();
6551         //this.syncRowHeights(index, index);
6552         //this.layout();
6553         this.fireEvent("rowupdated", this, index, record);
6554     },
6555     
6556     insertRow : function(dm, rowIndex, isUpdate){
6557         
6558         if(!isUpdate){
6559             this.fireEvent("beforerowsinserted", this, rowIndex);
6560         }
6561             //var s = this.getScrollState();
6562         var row = this.renderRow(this.cm, this.store, rowIndex);
6563         // insert before rowIndex..
6564         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6565         
6566         var _this = this;
6567                 
6568         if(row.cellObjects.length){
6569             Roo.each(row.cellObjects, function(r){
6570                 _this.renderCellObject(r);
6571             })
6572         }
6573             
6574         if(!isUpdate){
6575             this.fireEvent("rowsinserted", this, rowIndex);
6576             //this.syncRowHeights(firstRow, lastRow);
6577             //this.stripeRows(firstRow);
6578             //this.layout();
6579         }
6580         
6581     },
6582     
6583     
6584     getRowDom : function(rowIndex)
6585     {
6586         var rows = this.el.select('tbody > tr', true).elements;
6587         
6588         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6589         
6590     },
6591     // returns the object tree for a tr..
6592   
6593     
6594     renderRow : function(cm, ds, rowIndex) 
6595     {
6596         
6597         var d = ds.getAt(rowIndex);
6598         
6599         var row = {
6600             tag : 'tr',
6601             cn : []
6602         };
6603             
6604         var cellObjects = [];
6605         
6606         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6607             var config = cm.config[i];
6608             
6609             var renderer = cm.getRenderer(i);
6610             var value = '';
6611             var id = false;
6612             
6613             if(typeof(renderer) !== 'undefined'){
6614                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6615             }
6616             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6617             // and are rendered into the cells after the row is rendered - using the id for the element.
6618             
6619             if(typeof(value) === 'object'){
6620                 id = Roo.id();
6621                 cellObjects.push({
6622                     container : id,
6623                     cfg : value 
6624                 })
6625             }
6626             
6627             var rowcfg = {
6628                 record: d,
6629                 rowIndex : rowIndex,
6630                 colIndex : i,
6631                 rowClass : ''
6632             };
6633
6634             this.fireEvent('rowclass', this, rowcfg);
6635             
6636             var td = {
6637                 tag: 'td',
6638                 cls : rowcfg.rowClass,
6639                 style: '',
6640                 html: (typeof(value) === 'object') ? '' : value
6641             };
6642             
6643             if (id) {
6644                 td.id = id;
6645             }
6646             
6647             if(typeof(config.colspan) != 'undefined'){
6648                 td.colspan = config.colspan;
6649             }
6650             
6651             if(typeof(config.hidden) != 'undefined' && config.hidden){
6652                 td.style += ' display:none;';
6653             }
6654             
6655             if(typeof(config.align) != 'undefined' && config.align.length){
6656                 td.style += ' text-align:' + config.align + ';';
6657             }
6658             
6659             if(typeof(config.width) != 'undefined'){
6660                 td.style += ' width:' +  config.width + 'px;';
6661             }
6662             
6663             if(typeof(config.cursor) != 'undefined'){
6664                 td.style += ' cursor:' +  config.cursor + ';';
6665             }
6666             
6667             if(typeof(config.cls) != 'undefined'){
6668                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6669             }
6670             
6671             ['xs','sm','md','lg'].map(function(size){
6672                 
6673                 if(typeof(config[size]) == 'undefined'){
6674                     return;
6675                 }
6676                 
6677                 if (!config[size]) { // 0 = hidden
6678                     td.cls += ' hidden-' + size;
6679                     return;
6680                 }
6681                 
6682                 td.cls += ' col-' + size + '-' + config[size];
6683
6684             });
6685              
6686             row.cn.push(td);
6687            
6688         }
6689         
6690         row.cellObjects = cellObjects;
6691         
6692         return row;
6693           
6694     },
6695     
6696     
6697     
6698     onBeforeLoad : function()
6699     {
6700         //Roo.log('ds onBeforeLoad');
6701         
6702         //this.clear();
6703         
6704         //if(this.loadMask){
6705         //    this.maskEl.show();
6706         //}
6707     },
6708      /**
6709      * Remove all rows
6710      */
6711     clear : function()
6712     {
6713         this.el.select('tbody', true).first().dom.innerHTML = '';
6714     },
6715     /**
6716      * Show or hide a row.
6717      * @param {Number} rowIndex to show or hide
6718      * @param {Boolean} state hide
6719      */
6720     setRowVisibility : function(rowIndex, state)
6721     {
6722         var bt = this.mainBody.dom;
6723         
6724         var rows = this.el.select('tbody > tr', true).elements;
6725         
6726         if(typeof(rows[rowIndex]) == 'undefined'){
6727             return;
6728         }
6729         rows[rowIndex].dom.style.display = state ? '' : 'none';
6730     },
6731     
6732     
6733     getSelectionModel : function(){
6734         if(!this.selModel){
6735             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6736         }
6737         return this.selModel;
6738     },
6739     /*
6740      * Render the Roo.bootstrap object from renderder
6741      */
6742     renderCellObject : function(r)
6743     {
6744         var _this = this;
6745         
6746         var t = r.cfg.render(r.container);
6747         
6748         if(r.cfg.cn){
6749             Roo.each(r.cfg.cn, function(c){
6750                 var child = {
6751                     container: t.getChildContainer(),
6752                     cfg: c
6753                 };
6754                 _this.renderCellObject(child);
6755             })
6756         }
6757     },
6758     
6759     getRowIndex : function(row)
6760     {
6761         var rowIndex = -1;
6762         
6763         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6764             if(el != row){
6765                 return;
6766             }
6767             
6768             rowIndex = index;
6769         });
6770         
6771         return rowIndex;
6772     },
6773      /**
6774      * Returns the grid's underlying element = used by panel.Grid
6775      * @return {Element} The element
6776      */
6777     getGridEl : function(){
6778         return this.el;
6779     },
6780      /**
6781      * Forces a resize - used by panel.Grid
6782      * @return {Element} The element
6783      */
6784     autoSize : function()
6785     {
6786         //var ctr = Roo.get(this.container.dom.parentElement);
6787         var ctr = Roo.get(this.el.dom);
6788         
6789         var thd = this.getGridEl().select('thead',true).first();
6790         var tbd = this.getGridEl().select('tbody', true).first();
6791         var tfd = this.getGridEl().select('tfoot', true).first();
6792         
6793         var cw = ctr.getWidth();
6794         
6795         if (tbd) {
6796             
6797             tbd.setSize(ctr.getWidth(),
6798                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6799             );
6800             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6801             cw -= barsize;
6802         }
6803         cw = Math.max(cw, this.totalWidth);
6804         this.getGridEl().select('tr',true).setWidth(cw);
6805         // resize 'expandable coloumn?
6806         
6807         return; // we doe not have a view in this design..
6808         
6809     },
6810     onBodyScroll: function()
6811     {
6812         
6813         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6814         this.mainHead.setStyle({
6815                     'position' : 'relative',
6816                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6817         });
6818         
6819         
6820     }
6821 });
6822
6823  
6824
6825  /*
6826  * - LGPL
6827  *
6828  * table cell
6829  * 
6830  */
6831
6832 /**
6833  * @class Roo.bootstrap.TableCell
6834  * @extends Roo.bootstrap.Component
6835  * Bootstrap TableCell class
6836  * @cfg {String} html cell contain text
6837  * @cfg {String} cls cell class
6838  * @cfg {String} tag cell tag (td|th) default td
6839  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6840  * @cfg {String} align Aligns the content in a cell
6841  * @cfg {String} axis Categorizes cells
6842  * @cfg {String} bgcolor Specifies the background color of a cell
6843  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6844  * @cfg {Number} colspan Specifies the number of columns a cell should span
6845  * @cfg {String} headers Specifies one or more header cells a cell is related to
6846  * @cfg {Number} height Sets the height of a cell
6847  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6848  * @cfg {Number} rowspan Sets the number of rows a cell should span
6849  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6850  * @cfg {String} valign Vertical aligns the content in a cell
6851  * @cfg {Number} width Specifies the width of a cell
6852  * 
6853  * @constructor
6854  * Create a new TableCell
6855  * @param {Object} config The config object
6856  */
6857
6858 Roo.bootstrap.TableCell = function(config){
6859     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6860 };
6861
6862 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6863     
6864     html: false,
6865     cls: false,
6866     tag: false,
6867     abbr: false,
6868     align: false,
6869     axis: false,
6870     bgcolor: false,
6871     charoff: false,
6872     colspan: false,
6873     headers: false,
6874     height: false,
6875     nowrap: false,
6876     rowspan: false,
6877     scope: false,
6878     valign: false,
6879     width: false,
6880     
6881     
6882     getAutoCreate : function(){
6883         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6884         
6885         cfg = {
6886             tag: 'td'
6887         };
6888         
6889         if(this.tag){
6890             cfg.tag = this.tag;
6891         }
6892         
6893         if (this.html) {
6894             cfg.html=this.html
6895         }
6896         if (this.cls) {
6897             cfg.cls=this.cls
6898         }
6899         if (this.abbr) {
6900             cfg.abbr=this.abbr
6901         }
6902         if (this.align) {
6903             cfg.align=this.align
6904         }
6905         if (this.axis) {
6906             cfg.axis=this.axis
6907         }
6908         if (this.bgcolor) {
6909             cfg.bgcolor=this.bgcolor
6910         }
6911         if (this.charoff) {
6912             cfg.charoff=this.charoff
6913         }
6914         if (this.colspan) {
6915             cfg.colspan=this.colspan
6916         }
6917         if (this.headers) {
6918             cfg.headers=this.headers
6919         }
6920         if (this.height) {
6921             cfg.height=this.height
6922         }
6923         if (this.nowrap) {
6924             cfg.nowrap=this.nowrap
6925         }
6926         if (this.rowspan) {
6927             cfg.rowspan=this.rowspan
6928         }
6929         if (this.scope) {
6930             cfg.scope=this.scope
6931         }
6932         if (this.valign) {
6933             cfg.valign=this.valign
6934         }
6935         if (this.width) {
6936             cfg.width=this.width
6937         }
6938         
6939         
6940         return cfg;
6941     }
6942    
6943 });
6944
6945  
6946
6947  /*
6948  * - LGPL
6949  *
6950  * table row
6951  * 
6952  */
6953
6954 /**
6955  * @class Roo.bootstrap.TableRow
6956  * @extends Roo.bootstrap.Component
6957  * Bootstrap TableRow class
6958  * @cfg {String} cls row class
6959  * @cfg {String} align Aligns the content in a table row
6960  * @cfg {String} bgcolor Specifies a background color for a table row
6961  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6962  * @cfg {String} valign Vertical aligns the content in a table row
6963  * 
6964  * @constructor
6965  * Create a new TableRow
6966  * @param {Object} config The config object
6967  */
6968
6969 Roo.bootstrap.TableRow = function(config){
6970     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6971 };
6972
6973 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6974     
6975     cls: false,
6976     align: false,
6977     bgcolor: false,
6978     charoff: false,
6979     valign: false,
6980     
6981     getAutoCreate : function(){
6982         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6983         
6984         cfg = {
6985             tag: 'tr'
6986         };
6987             
6988         if(this.cls){
6989             cfg.cls = this.cls;
6990         }
6991         if(this.align){
6992             cfg.align = this.align;
6993         }
6994         if(this.bgcolor){
6995             cfg.bgcolor = this.bgcolor;
6996         }
6997         if(this.charoff){
6998             cfg.charoff = this.charoff;
6999         }
7000         if(this.valign){
7001             cfg.valign = this.valign;
7002         }
7003         
7004         return cfg;
7005     }
7006    
7007 });
7008
7009  
7010
7011  /*
7012  * - LGPL
7013  *
7014  * table body
7015  * 
7016  */
7017
7018 /**
7019  * @class Roo.bootstrap.TableBody
7020  * @extends Roo.bootstrap.Component
7021  * Bootstrap TableBody class
7022  * @cfg {String} cls element class
7023  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7024  * @cfg {String} align Aligns the content inside the element
7025  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7026  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7027  * 
7028  * @constructor
7029  * Create a new TableBody
7030  * @param {Object} config The config object
7031  */
7032
7033 Roo.bootstrap.TableBody = function(config){
7034     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7035 };
7036
7037 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7038     
7039     cls: false,
7040     tag: false,
7041     align: false,
7042     charoff: false,
7043     valign: false,
7044     
7045     getAutoCreate : function(){
7046         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7047         
7048         cfg = {
7049             tag: 'tbody'
7050         };
7051             
7052         if (this.cls) {
7053             cfg.cls=this.cls
7054         }
7055         if(this.tag){
7056             cfg.tag = this.tag;
7057         }
7058         
7059         if(this.align){
7060             cfg.align = this.align;
7061         }
7062         if(this.charoff){
7063             cfg.charoff = this.charoff;
7064         }
7065         if(this.valign){
7066             cfg.valign = this.valign;
7067         }
7068         
7069         return cfg;
7070     }
7071     
7072     
7073 //    initEvents : function()
7074 //    {
7075 //        
7076 //        if(!this.store){
7077 //            return;
7078 //        }
7079 //        
7080 //        this.store = Roo.factory(this.store, Roo.data);
7081 //        this.store.on('load', this.onLoad, this);
7082 //        
7083 //        this.store.load();
7084 //        
7085 //    },
7086 //    
7087 //    onLoad: function () 
7088 //    {   
7089 //        this.fireEvent('load', this);
7090 //    }
7091 //    
7092 //   
7093 });
7094
7095  
7096
7097  /*
7098  * Based on:
7099  * Ext JS Library 1.1.1
7100  * Copyright(c) 2006-2007, Ext JS, LLC.
7101  *
7102  * Originally Released Under LGPL - original licence link has changed is not relivant.
7103  *
7104  * Fork - LGPL
7105  * <script type="text/javascript">
7106  */
7107
7108 // as we use this in bootstrap.
7109 Roo.namespace('Roo.form');
7110  /**
7111  * @class Roo.form.Action
7112  * Internal Class used to handle form actions
7113  * @constructor
7114  * @param {Roo.form.BasicForm} el The form element or its id
7115  * @param {Object} config Configuration options
7116  */
7117
7118  
7119  
7120 // define the action interface
7121 Roo.form.Action = function(form, options){
7122     this.form = form;
7123     this.options = options || {};
7124 };
7125 /**
7126  * Client Validation Failed
7127  * @const 
7128  */
7129 Roo.form.Action.CLIENT_INVALID = 'client';
7130 /**
7131  * Server Validation Failed
7132  * @const 
7133  */
7134 Roo.form.Action.SERVER_INVALID = 'server';
7135  /**
7136  * Connect to Server Failed
7137  * @const 
7138  */
7139 Roo.form.Action.CONNECT_FAILURE = 'connect';
7140 /**
7141  * Reading Data from Server Failed
7142  * @const 
7143  */
7144 Roo.form.Action.LOAD_FAILURE = 'load';
7145
7146 Roo.form.Action.prototype = {
7147     type : 'default',
7148     failureType : undefined,
7149     response : undefined,
7150     result : undefined,
7151
7152     // interface method
7153     run : function(options){
7154
7155     },
7156
7157     // interface method
7158     success : function(response){
7159
7160     },
7161
7162     // interface method
7163     handleResponse : function(response){
7164
7165     },
7166
7167     // default connection failure
7168     failure : function(response){
7169         
7170         this.response = response;
7171         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7172         this.form.afterAction(this, false);
7173     },
7174
7175     processResponse : function(response){
7176         this.response = response;
7177         if(!response.responseText){
7178             return true;
7179         }
7180         this.result = this.handleResponse(response);
7181         return this.result;
7182     },
7183
7184     // utility functions used internally
7185     getUrl : function(appendParams){
7186         var url = this.options.url || this.form.url || this.form.el.dom.action;
7187         if(appendParams){
7188             var p = this.getParams();
7189             if(p){
7190                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7191             }
7192         }
7193         return url;
7194     },
7195
7196     getMethod : function(){
7197         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7198     },
7199
7200     getParams : function(){
7201         var bp = this.form.baseParams;
7202         var p = this.options.params;
7203         if(p){
7204             if(typeof p == "object"){
7205                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7206             }else if(typeof p == 'string' && bp){
7207                 p += '&' + Roo.urlEncode(bp);
7208             }
7209         }else if(bp){
7210             p = Roo.urlEncode(bp);
7211         }
7212         return p;
7213     },
7214
7215     createCallback : function(){
7216         return {
7217             success: this.success,
7218             failure: this.failure,
7219             scope: this,
7220             timeout: (this.form.timeout*1000),
7221             upload: this.form.fileUpload ? this.success : undefined
7222         };
7223     }
7224 };
7225
7226 Roo.form.Action.Submit = function(form, options){
7227     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7228 };
7229
7230 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7231     type : 'submit',
7232
7233     haveProgress : false,
7234     uploadComplete : false,
7235     
7236     // uploadProgress indicator.
7237     uploadProgress : function()
7238     {
7239         if (!this.form.progressUrl) {
7240             return;
7241         }
7242         
7243         if (!this.haveProgress) {
7244             Roo.MessageBox.progress("Uploading", "Uploading");
7245         }
7246         if (this.uploadComplete) {
7247            Roo.MessageBox.hide();
7248            return;
7249         }
7250         
7251         this.haveProgress = true;
7252    
7253         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7254         
7255         var c = new Roo.data.Connection();
7256         c.request({
7257             url : this.form.progressUrl,
7258             params: {
7259                 id : uid
7260             },
7261             method: 'GET',
7262             success : function(req){
7263                //console.log(data);
7264                 var rdata = false;
7265                 var edata;
7266                 try  {
7267                    rdata = Roo.decode(req.responseText)
7268                 } catch (e) {
7269                     Roo.log("Invalid data from server..");
7270                     Roo.log(edata);
7271                     return;
7272                 }
7273                 if (!rdata || !rdata.success) {
7274                     Roo.log(rdata);
7275                     Roo.MessageBox.alert(Roo.encode(rdata));
7276                     return;
7277                 }
7278                 var data = rdata.data;
7279                 
7280                 if (this.uploadComplete) {
7281                    Roo.MessageBox.hide();
7282                    return;
7283                 }
7284                    
7285                 if (data){
7286                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7287                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7288                     );
7289                 }
7290                 this.uploadProgress.defer(2000,this);
7291             },
7292        
7293             failure: function(data) {
7294                 Roo.log('progress url failed ');
7295                 Roo.log(data);
7296             },
7297             scope : this
7298         });
7299            
7300     },
7301     
7302     
7303     run : function()
7304     {
7305         // run get Values on the form, so it syncs any secondary forms.
7306         this.form.getValues();
7307         
7308         var o = this.options;
7309         var method = this.getMethod();
7310         var isPost = method == 'POST';
7311         if(o.clientValidation === false || this.form.isValid()){
7312             
7313             if (this.form.progressUrl) {
7314                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7315                     (new Date() * 1) + '' + Math.random());
7316                     
7317             } 
7318             
7319             
7320             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7321                 form:this.form.el.dom,
7322                 url:this.getUrl(!isPost),
7323                 method: method,
7324                 params:isPost ? this.getParams() : null,
7325                 isUpload: this.form.fileUpload
7326             }));
7327             
7328             this.uploadProgress();
7329
7330         }else if (o.clientValidation !== false){ // client validation failed
7331             this.failureType = Roo.form.Action.CLIENT_INVALID;
7332             this.form.afterAction(this, false);
7333         }
7334     },
7335
7336     success : function(response)
7337     {
7338         this.uploadComplete= true;
7339         if (this.haveProgress) {
7340             Roo.MessageBox.hide();
7341         }
7342         
7343         
7344         var result = this.processResponse(response);
7345         if(result === true || result.success){
7346             this.form.afterAction(this, true);
7347             return;
7348         }
7349         if(result.errors){
7350             this.form.markInvalid(result.errors);
7351             this.failureType = Roo.form.Action.SERVER_INVALID;
7352         }
7353         this.form.afterAction(this, false);
7354     },
7355     failure : function(response)
7356     {
7357         this.uploadComplete= true;
7358         if (this.haveProgress) {
7359             Roo.MessageBox.hide();
7360         }
7361         
7362         this.response = response;
7363         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7364         this.form.afterAction(this, false);
7365     },
7366     
7367     handleResponse : function(response){
7368         if(this.form.errorReader){
7369             var rs = this.form.errorReader.read(response);
7370             var errors = [];
7371             if(rs.records){
7372                 for(var i = 0, len = rs.records.length; i < len; i++) {
7373                     var r = rs.records[i];
7374                     errors[i] = r.data;
7375                 }
7376             }
7377             if(errors.length < 1){
7378                 errors = null;
7379             }
7380             return {
7381                 success : rs.success,
7382                 errors : errors
7383             };
7384         }
7385         var ret = false;
7386         try {
7387             ret = Roo.decode(response.responseText);
7388         } catch (e) {
7389             ret = {
7390                 success: false,
7391                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7392                 errors : []
7393             };
7394         }
7395         return ret;
7396         
7397     }
7398 });
7399
7400
7401 Roo.form.Action.Load = function(form, options){
7402     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7403     this.reader = this.form.reader;
7404 };
7405
7406 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7407     type : 'load',
7408
7409     run : function(){
7410         
7411         Roo.Ajax.request(Roo.apply(
7412                 this.createCallback(), {
7413                     method:this.getMethod(),
7414                     url:this.getUrl(false),
7415                     params:this.getParams()
7416         }));
7417     },
7418
7419     success : function(response){
7420         
7421         var result = this.processResponse(response);
7422         if(result === true || !result.success || !result.data){
7423             this.failureType = Roo.form.Action.LOAD_FAILURE;
7424             this.form.afterAction(this, false);
7425             return;
7426         }
7427         this.form.clearInvalid();
7428         this.form.setValues(result.data);
7429         this.form.afterAction(this, true);
7430     },
7431
7432     handleResponse : function(response){
7433         if(this.form.reader){
7434             var rs = this.form.reader.read(response);
7435             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7436             return {
7437                 success : rs.success,
7438                 data : data
7439             };
7440         }
7441         return Roo.decode(response.responseText);
7442     }
7443 });
7444
7445 Roo.form.Action.ACTION_TYPES = {
7446     'load' : Roo.form.Action.Load,
7447     'submit' : Roo.form.Action.Submit
7448 };/*
7449  * - LGPL
7450  *
7451  * form
7452  *
7453  */
7454
7455 /**
7456  * @class Roo.bootstrap.Form
7457  * @extends Roo.bootstrap.Component
7458  * Bootstrap Form class
7459  * @cfg {String} method  GET | POST (default POST)
7460  * @cfg {String} labelAlign top | left (default top)
7461  * @cfg {String} align left  | right - for navbars
7462  * @cfg {Boolean} loadMask load mask when submit (default true)
7463
7464  *
7465  * @constructor
7466  * Create a new Form
7467  * @param {Object} config The config object
7468  */
7469
7470
7471 Roo.bootstrap.Form = function(config){
7472     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7473     
7474     Roo.bootstrap.Form.popover.apply();
7475     
7476     this.addEvents({
7477         /**
7478          * @event clientvalidation
7479          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7480          * @param {Form} this
7481          * @param {Boolean} valid true if the form has passed client-side validation
7482          */
7483         clientvalidation: true,
7484         /**
7485          * @event beforeaction
7486          * Fires before any action is performed. Return false to cancel the action.
7487          * @param {Form} this
7488          * @param {Action} action The action to be performed
7489          */
7490         beforeaction: true,
7491         /**
7492          * @event actionfailed
7493          * Fires when an action fails.
7494          * @param {Form} this
7495          * @param {Action} action The action that failed
7496          */
7497         actionfailed : true,
7498         /**
7499          * @event actioncomplete
7500          * Fires when an action is completed.
7501          * @param {Form} this
7502          * @param {Action} action The action that completed
7503          */
7504         actioncomplete : true
7505     });
7506
7507 };
7508
7509 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7510
7511      /**
7512      * @cfg {String} method
7513      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7514      */
7515     method : 'POST',
7516     /**
7517      * @cfg {String} url
7518      * The URL to use for form actions if one isn't supplied in the action options.
7519      */
7520     /**
7521      * @cfg {Boolean} fileUpload
7522      * Set to true if this form is a file upload.
7523      */
7524
7525     /**
7526      * @cfg {Object} baseParams
7527      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7528      */
7529
7530     /**
7531      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7532      */
7533     timeout: 30,
7534     /**
7535      * @cfg {Sting} align (left|right) for navbar forms
7536      */
7537     align : 'left',
7538
7539     // private
7540     activeAction : null,
7541
7542     /**
7543      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7544      * element by passing it or its id or mask the form itself by passing in true.
7545      * @type Mixed
7546      */
7547     waitMsgTarget : false,
7548
7549     loadMask : true,
7550     
7551     /**
7552      * @cfg {Boolean} errorMask (true|false) default false
7553      */
7554     errorMask : false,
7555
7556     getAutoCreate : function(){
7557
7558         var cfg = {
7559             tag: 'form',
7560             method : this.method || 'POST',
7561             id : this.id || Roo.id(),
7562             cls : ''
7563         };
7564         if (this.parent().xtype.match(/^Nav/)) {
7565             cfg.cls = 'navbar-form navbar-' + this.align;
7566
7567         }
7568
7569         if (this.labelAlign == 'left' ) {
7570             cfg.cls += ' form-horizontal';
7571         }
7572
7573
7574         return cfg;
7575     },
7576     initEvents : function()
7577     {
7578         this.el.on('submit', this.onSubmit, this);
7579         // this was added as random key presses on the form where triggering form submit.
7580         this.el.on('keypress', function(e) {
7581             if (e.getCharCode() != 13) {
7582                 return true;
7583             }
7584             // we might need to allow it for textareas.. and some other items.
7585             // check e.getTarget().
7586
7587             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7588                 return true;
7589             }
7590
7591             Roo.log("keypress blocked");
7592
7593             e.preventDefault();
7594             return false;
7595         });
7596         
7597     },
7598     // private
7599     onSubmit : function(e){
7600         e.stopEvent();
7601     },
7602
7603      /**
7604      * Returns true if client-side validation on the form is successful.
7605      * @return Boolean
7606      */
7607     isValid : function(){
7608         var items = this.getItems();
7609         var valid = true;
7610         var target = false;
7611         
7612         items.each(function(f){
7613             
7614             if(f.validate()){
7615                 return;
7616             }
7617             valid = false;
7618
7619             if(!target && f.el.isVisible(true)){
7620                 target = f;
7621             }
7622            
7623         });
7624         
7625         if(this.errorMask && !valid){
7626             Roo.bootstrap.Form.popover.mask(this, target);
7627         }
7628         
7629         return valid;
7630     },
7631     
7632     /**
7633      * Returns true if any fields in this form have changed since their original load.
7634      * @return Boolean
7635      */
7636     isDirty : function(){
7637         var dirty = false;
7638         var items = this.getItems();
7639         items.each(function(f){
7640            if(f.isDirty()){
7641                dirty = true;
7642                return false;
7643            }
7644            return true;
7645         });
7646         return dirty;
7647     },
7648      /**
7649      * Performs a predefined action (submit or load) or custom actions you define on this form.
7650      * @param {String} actionName The name of the action type
7651      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7652      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7653      * accept other config options):
7654      * <pre>
7655 Property          Type             Description
7656 ----------------  ---------------  ----------------------------------------------------------------------------------
7657 url               String           The url for the action (defaults to the form's url)
7658 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7659 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7660 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7661                                    validate the form on the client (defaults to false)
7662      * </pre>
7663      * @return {BasicForm} this
7664      */
7665     doAction : function(action, options){
7666         if(typeof action == 'string'){
7667             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7668         }
7669         if(this.fireEvent('beforeaction', this, action) !== false){
7670             this.beforeAction(action);
7671             action.run.defer(100, action);
7672         }
7673         return this;
7674     },
7675
7676     // private
7677     beforeAction : function(action){
7678         var o = action.options;
7679
7680         if(this.loadMask){
7681             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7682         }
7683         // not really supported yet.. ??
7684
7685         //if(this.waitMsgTarget === true){
7686         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7687         //}else if(this.waitMsgTarget){
7688         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7689         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7690         //}else {
7691         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7692        // }
7693
7694     },
7695
7696     // private
7697     afterAction : function(action, success){
7698         this.activeAction = null;
7699         var o = action.options;
7700
7701         //if(this.waitMsgTarget === true){
7702             this.el.unmask();
7703         //}else if(this.waitMsgTarget){
7704         //    this.waitMsgTarget.unmask();
7705         //}else{
7706         //    Roo.MessageBox.updateProgress(1);
7707         //    Roo.MessageBox.hide();
7708        // }
7709         //
7710         if(success){
7711             if(o.reset){
7712                 this.reset();
7713             }
7714             Roo.callback(o.success, o.scope, [this, action]);
7715             this.fireEvent('actioncomplete', this, action);
7716
7717         }else{
7718
7719             // failure condition..
7720             // we have a scenario where updates need confirming.
7721             // eg. if a locking scenario exists..
7722             // we look for { errors : { needs_confirm : true }} in the response.
7723             if (
7724                 (typeof(action.result) != 'undefined')  &&
7725                 (typeof(action.result.errors) != 'undefined')  &&
7726                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7727            ){
7728                 var _t = this;
7729                 Roo.log("not supported yet");
7730                  /*
7731
7732                 Roo.MessageBox.confirm(
7733                     "Change requires confirmation",
7734                     action.result.errorMsg,
7735                     function(r) {
7736                         if (r != 'yes') {
7737                             return;
7738                         }
7739                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7740                     }
7741
7742                 );
7743                 */
7744
7745
7746                 return;
7747             }
7748
7749             Roo.callback(o.failure, o.scope, [this, action]);
7750             // show an error message if no failed handler is set..
7751             if (!this.hasListener('actionfailed')) {
7752                 Roo.log("need to add dialog support");
7753                 /*
7754                 Roo.MessageBox.alert("Error",
7755                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7756                         action.result.errorMsg :
7757                         "Saving Failed, please check your entries or try again"
7758                 );
7759                 */
7760             }
7761
7762             this.fireEvent('actionfailed', this, action);
7763         }
7764
7765     },
7766     /**
7767      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7768      * @param {String} id The value to search for
7769      * @return Field
7770      */
7771     findField : function(id){
7772         var items = this.getItems();
7773         var field = items.get(id);
7774         if(!field){
7775              items.each(function(f){
7776                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7777                     field = f;
7778                     return false;
7779                 }
7780                 return true;
7781             });
7782         }
7783         return field || null;
7784     },
7785      /**
7786      * Mark fields in this form invalid in bulk.
7787      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7788      * @return {BasicForm} this
7789      */
7790     markInvalid : function(errors){
7791         if(errors instanceof Array){
7792             for(var i = 0, len = errors.length; i < len; i++){
7793                 var fieldError = errors[i];
7794                 var f = this.findField(fieldError.id);
7795                 if(f){
7796                     f.markInvalid(fieldError.msg);
7797                 }
7798             }
7799         }else{
7800             var field, id;
7801             for(id in errors){
7802                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7803                     field.markInvalid(errors[id]);
7804                 }
7805             }
7806         }
7807         //Roo.each(this.childForms || [], function (f) {
7808         //    f.markInvalid(errors);
7809         //});
7810
7811         return this;
7812     },
7813
7814     /**
7815      * Set values for fields in this form in bulk.
7816      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7817      * @return {BasicForm} this
7818      */
7819     setValues : function(values){
7820         if(values instanceof Array){ // array of objects
7821             for(var i = 0, len = values.length; i < len; i++){
7822                 var v = values[i];
7823                 var f = this.findField(v.id);
7824                 if(f){
7825                     f.setValue(v.value);
7826                     if(this.trackResetOnLoad){
7827                         f.originalValue = f.getValue();
7828                     }
7829                 }
7830             }
7831         }else{ // object hash
7832             var field, id;
7833             for(id in values){
7834                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7835
7836                     if (field.setFromData &&
7837                         field.valueField &&
7838                         field.displayField &&
7839                         // combos' with local stores can
7840                         // be queried via setValue()
7841                         // to set their value..
7842                         (field.store && !field.store.isLocal)
7843                         ) {
7844                         // it's a combo
7845                         var sd = { };
7846                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7847                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7848                         field.setFromData(sd);
7849
7850                     } else {
7851                         field.setValue(values[id]);
7852                     }
7853
7854
7855                     if(this.trackResetOnLoad){
7856                         field.originalValue = field.getValue();
7857                     }
7858                 }
7859             }
7860         }
7861
7862         //Roo.each(this.childForms || [], function (f) {
7863         //    f.setValues(values);
7864         //});
7865
7866         return this;
7867     },
7868
7869     /**
7870      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7871      * they are returned as an array.
7872      * @param {Boolean} asString
7873      * @return {Object}
7874      */
7875     getValues : function(asString){
7876         //if (this.childForms) {
7877             // copy values from the child forms
7878         //    Roo.each(this.childForms, function (f) {
7879         //        this.setValues(f.getValues());
7880         //    }, this);
7881         //}
7882
7883
7884
7885         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7886         if(asString === true){
7887             return fs;
7888         }
7889         return Roo.urlDecode(fs);
7890     },
7891
7892     /**
7893      * Returns the fields in this form as an object with key/value pairs.
7894      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7895      * @return {Object}
7896      */
7897     getFieldValues : function(with_hidden)
7898     {
7899         var items = this.getItems();
7900         var ret = {};
7901         items.each(function(f){
7902             if (!f.getName()) {
7903                 return;
7904             }
7905             var v = f.getValue();
7906             if (f.inputType =='radio') {
7907                 if (typeof(ret[f.getName()]) == 'undefined') {
7908                     ret[f.getName()] = ''; // empty..
7909                 }
7910
7911                 if (!f.el.dom.checked) {
7912                     return;
7913
7914                 }
7915                 v = f.el.dom.value;
7916
7917             }
7918
7919             // not sure if this supported any more..
7920             if ((typeof(v) == 'object') && f.getRawValue) {
7921                 v = f.getRawValue() ; // dates..
7922             }
7923             // combo boxes where name != hiddenName...
7924             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7925                 ret[f.name] = f.getRawValue();
7926             }
7927             ret[f.getName()] = v;
7928         });
7929
7930         return ret;
7931     },
7932
7933     /**
7934      * Clears all invalid messages in this form.
7935      * @return {BasicForm} this
7936      */
7937     clearInvalid : function(){
7938         var items = this.getItems();
7939
7940         items.each(function(f){
7941            f.clearInvalid();
7942         });
7943
7944
7945
7946         return this;
7947     },
7948
7949     /**
7950      * Resets this form.
7951      * @return {BasicForm} this
7952      */
7953     reset : function(){
7954         var items = this.getItems();
7955         items.each(function(f){
7956             f.reset();
7957         });
7958
7959         Roo.each(this.childForms || [], function (f) {
7960             f.reset();
7961         });
7962
7963
7964         return this;
7965     },
7966     getItems : function()
7967     {
7968         var r=new Roo.util.MixedCollection(false, function(o){
7969             return o.id || (o.id = Roo.id());
7970         });
7971         var iter = function(el) {
7972             if (el.inputEl) {
7973                 r.add(el);
7974             }
7975             if (!el.items) {
7976                 return;
7977             }
7978             Roo.each(el.items,function(e) {
7979                 iter(e);
7980             });
7981
7982
7983         };
7984
7985         iter(this);
7986         return r;
7987
7988
7989
7990
7991     }
7992
7993 });
7994
7995 Roo.apply(Roo.bootstrap.Form, {
7996     
7997     popover : {
7998         
7999         padding : 5,
8000         
8001         isApplied : false,
8002         
8003         isMasked : false,
8004         
8005         form : false,
8006         
8007         target : false,
8008         
8009         toolTip : false,
8010         
8011         intervalID : false,
8012         
8013         maskEl : false,
8014         
8015         apply : function()
8016         {
8017             if(this.isApplied){
8018                 return;
8019             }
8020             
8021             this.maskEl = {
8022                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8023                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8024                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8025                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8026             };
8027             
8028             this.maskEl.top.enableDisplayMode("block");
8029             this.maskEl.left.enableDisplayMode("block");
8030             this.maskEl.bottom.enableDisplayMode("block");
8031             this.maskEl.right.enableDisplayMode("block");
8032             
8033             this.toolTip = new Roo.bootstrap.Tooltip({
8034                 cls : 'roo-form-error-popover',
8035                 alignment : {
8036                     'left' : ['r-l', [-2,0], 'right'],
8037                     'right' : ['l-r', [2,0], 'left'],
8038                     'bottom' : ['tl-bl', [0,2], 'top'],
8039                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8040                 }
8041             });
8042             
8043             this.toolTip.render(Roo.get(document.body));
8044
8045             this.toolTip.el.enableDisplayMode("block");
8046             
8047             Roo.get(document.body).on('click', function(){
8048                 this.unmask();
8049             }, this);
8050             
8051             this.isApplied = true
8052         },
8053         
8054         mask : function(form, target)
8055         {
8056             this.form = form;
8057             
8058             this.target = target;
8059             
8060             if(!this.form.errorMask || !target.el){
8061                 return;
8062             }
8063             
8064             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8065             
8066             var ot = this.target.el.calcOffsetsTo(scrollable);
8067             
8068             var scrollTo = ot[1] - 100;
8069             
8070             var maxScroll = Roo.lib.Dom.getDocumentHeight() - Roo.lib.Dom.getViewportHeight();
8071             
8072             scrollTo = Math.min(scrollTo, maxScroll);
8073             
8074             scrollable.scrollTo('top', scrollTo);
8075             
8076             var box = this.target.el.getBox();
8077
8078             var zIndex = Roo.bootstrap.Modal.zIndex++;
8079
8080             this.maskEl.top.setStyle('position', 'fixed');
8081             this.maskEl.top.setStyle('z-index', zIndex);
8082             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8083             this.maskEl.top.setXY([0, 0]);
8084             this.maskEl.top.show();
8085
8086             this.maskEl.left.setStyle('position', 'fixed');
8087             this.maskEl.left.setStyle('z-index', zIndex);
8088             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8089             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8090             this.maskEl.left.show();
8091
8092             this.maskEl.bottom.setStyle('position', 'fixed');
8093             this.maskEl.bottom.setStyle('z-index', zIndex);
8094             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8095             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8096             this.maskEl.bottom.show();
8097
8098             this.maskEl.right.setStyle('position', 'fixed');
8099             this.maskEl.right.setStyle('z-index', zIndex);
8100             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8101             this.maskEl.right.setXY([0, box.y - this.padding]);
8102             this.maskEl.right.show();
8103
8104
8105             this.toolTip.bindEl = this.target.el;
8106
8107             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8108
8109             var tip = this.target.blankText;
8110
8111             if(this.target.getValue() !== '' && this.target.regexText.length){
8112                 tip = this.target.regexText;
8113             }
8114
8115             this.toolTip.show(tip);
8116
8117             this.intervalID = window.setInterval(function() {
8118                 Roo.bootstrap.Form.popover.unmask();
8119             }, 10000);
8120
8121             window.onwheel = function(){ return false;};
8122             
8123             (function(){ this.isMasked = true; }).defer(500, this);
8124                 
8125             
8126             
8127         },
8128         
8129         unmask : function()
8130         {
8131             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8132                 return;
8133             }
8134             
8135             this.maskEl.top.setStyle('position', 'absolute');
8136             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8137             this.maskEl.top.hide();
8138
8139             this.maskEl.left.setStyle('position', 'absolute');
8140             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8141             this.maskEl.left.hide();
8142
8143             this.maskEl.bottom.setStyle('position', 'absolute');
8144             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8145             this.maskEl.bottom.hide();
8146
8147             this.maskEl.right.setStyle('position', 'absolute');
8148             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8149             this.maskEl.right.hide();
8150             
8151             this.toolTip.hide();
8152             
8153             this.toolTip.el.hide();
8154             
8155             window.onwheel = function(){ return true;};
8156             
8157             if(this.intervalID){
8158                 window.clearInterval(this.intervalID);
8159                 this.intervalID = false;
8160             }
8161             
8162             this.isMasked = false;
8163             
8164         }
8165         
8166     }
8167     
8168 });
8169
8170 /*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180 /**
8181  * @class Roo.form.VTypes
8182  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8183  * @singleton
8184  */
8185 Roo.form.VTypes = function(){
8186     // closure these in so they are only created once.
8187     var alpha = /^[a-zA-Z_]+$/;
8188     var alphanum = /^[a-zA-Z0-9_]+$/;
8189     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8190     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8191
8192     // All these messages and functions are configurable
8193     return {
8194         /**
8195          * The function used to validate email addresses
8196          * @param {String} value The email address
8197          */
8198         'email' : function(v){
8199             return email.test(v);
8200         },
8201         /**
8202          * The error text to display when the email validation function returns false
8203          * @type String
8204          */
8205         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8206         /**
8207          * The keystroke filter mask to be applied on email input
8208          * @type RegExp
8209          */
8210         'emailMask' : /[a-z0-9_\.\-@]/i,
8211
8212         /**
8213          * The function used to validate URLs
8214          * @param {String} value The URL
8215          */
8216         'url' : function(v){
8217             return url.test(v);
8218         },
8219         /**
8220          * The error text to display when the url validation function returns false
8221          * @type String
8222          */
8223         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8224         
8225         /**
8226          * The function used to validate alpha values
8227          * @param {String} value The value
8228          */
8229         'alpha' : function(v){
8230             return alpha.test(v);
8231         },
8232         /**
8233          * The error text to display when the alpha validation function returns false
8234          * @type String
8235          */
8236         'alphaText' : 'This field should only contain letters and _',
8237         /**
8238          * The keystroke filter mask to be applied on alpha input
8239          * @type RegExp
8240          */
8241         'alphaMask' : /[a-z_]/i,
8242
8243         /**
8244          * The function used to validate alphanumeric values
8245          * @param {String} value The value
8246          */
8247         'alphanum' : function(v){
8248             return alphanum.test(v);
8249         },
8250         /**
8251          * The error text to display when the alphanumeric validation function returns false
8252          * @type String
8253          */
8254         'alphanumText' : 'This field should only contain letters, numbers and _',
8255         /**
8256          * The keystroke filter mask to be applied on alphanumeric input
8257          * @type RegExp
8258          */
8259         'alphanumMask' : /[a-z0-9_]/i
8260     };
8261 }();/*
8262  * - LGPL
8263  *
8264  * Input
8265  * 
8266  */
8267
8268 /**
8269  * @class Roo.bootstrap.Input
8270  * @extends Roo.bootstrap.Component
8271  * Bootstrap Input class
8272  * @cfg {Boolean} disabled is it disabled
8273  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8274  * @cfg {String} name name of the input
8275  * @cfg {string} fieldLabel - the label associated
8276  * @cfg {string} placeholder - placeholder to put in text.
8277  * @cfg {string}  before - input group add on before
8278  * @cfg {string} after - input group add on after
8279  * @cfg {string} size - (lg|sm) or leave empty..
8280  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8281  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8282  * @cfg {Number} md colspan out of 12 for computer-sized screens
8283  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8284  * @cfg {string} value default value of the input
8285  * @cfg {Number} labelWidth set the width of label 
8286  * @cfg {Number} labellg set the width of label (1-12)
8287  * @cfg {Number} labelmd set the width of label (1-12)
8288  * @cfg {Number} labelsm set the width of label (1-12)
8289  * @cfg {Number} labelxs set the width of label (1-12)
8290  * @cfg {String} labelAlign (top|left)
8291  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8292  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8293  * @cfg {String} indicatorpos (left|right) default left
8294
8295  * @cfg {String} align (left|center|right) Default left
8296  * @cfg {Boolean} forceFeedback (true|false) Default false
8297  * 
8298  * 
8299  * 
8300  * 
8301  * @constructor
8302  * Create a new Input
8303  * @param {Object} config The config object
8304  */
8305
8306 Roo.bootstrap.Input = function(config){
8307     
8308     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8309     
8310     this.addEvents({
8311         /**
8312          * @event focus
8313          * Fires when this field receives input focus.
8314          * @param {Roo.form.Field} this
8315          */
8316         focus : true,
8317         /**
8318          * @event blur
8319          * Fires when this field loses input focus.
8320          * @param {Roo.form.Field} this
8321          */
8322         blur : true,
8323         /**
8324          * @event specialkey
8325          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8326          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8327          * @param {Roo.form.Field} this
8328          * @param {Roo.EventObject} e The event object
8329          */
8330         specialkey : true,
8331         /**
8332          * @event change
8333          * Fires just before the field blurs if the field value has changed.
8334          * @param {Roo.form.Field} this
8335          * @param {Mixed} newValue The new value
8336          * @param {Mixed} oldValue The original value
8337          */
8338         change : true,
8339         /**
8340          * @event invalid
8341          * Fires after the field has been marked as invalid.
8342          * @param {Roo.form.Field} this
8343          * @param {String} msg The validation message
8344          */
8345         invalid : true,
8346         /**
8347          * @event valid
8348          * Fires after the field has been validated with no errors.
8349          * @param {Roo.form.Field} this
8350          */
8351         valid : true,
8352          /**
8353          * @event keyup
8354          * Fires after the key up
8355          * @param {Roo.form.Field} this
8356          * @param {Roo.EventObject}  e The event Object
8357          */
8358         keyup : true
8359     });
8360 };
8361
8362 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8363      /**
8364      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8365       automatic validation (defaults to "keyup").
8366      */
8367     validationEvent : "keyup",
8368      /**
8369      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8370      */
8371     validateOnBlur : true,
8372     /**
8373      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8374      */
8375     validationDelay : 250,
8376      /**
8377      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8378      */
8379     focusClass : "x-form-focus",  // not needed???
8380     
8381        
8382     /**
8383      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8384      */
8385     invalidClass : "has-warning",
8386     
8387     /**
8388      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8389      */
8390     validClass : "has-success",
8391     
8392     /**
8393      * @cfg {Boolean} hasFeedback (true|false) default true
8394      */
8395     hasFeedback : true,
8396     
8397     /**
8398      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8399      */
8400     invalidFeedbackClass : "glyphicon-warning-sign",
8401     
8402     /**
8403      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8404      */
8405     validFeedbackClass : "glyphicon-ok",
8406     
8407     /**
8408      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8409      */
8410     selectOnFocus : false,
8411     
8412      /**
8413      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8414      */
8415     maskRe : null,
8416        /**
8417      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8418      */
8419     vtype : null,
8420     
8421       /**
8422      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8423      */
8424     disableKeyFilter : false,
8425     
8426        /**
8427      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8428      */
8429     disabled : false,
8430      /**
8431      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8432      */
8433     allowBlank : true,
8434     /**
8435      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8436      */
8437     blankText : "Please complete this mandatory field",
8438     
8439      /**
8440      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8441      */
8442     minLength : 0,
8443     /**
8444      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8445      */
8446     maxLength : Number.MAX_VALUE,
8447     /**
8448      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8449      */
8450     minLengthText : "The minimum length for this field is {0}",
8451     /**
8452      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8453      */
8454     maxLengthText : "The maximum length for this field is {0}",
8455   
8456     
8457     /**
8458      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8459      * If available, this function will be called only after the basic validators all return true, and will be passed the
8460      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8461      */
8462     validator : null,
8463     /**
8464      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8465      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8466      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8467      */
8468     regex : null,
8469     /**
8470      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8471      */
8472     regexText : "",
8473     
8474     autocomplete: false,
8475     
8476     
8477     fieldLabel : '',
8478     inputType : 'text',
8479     
8480     name : false,
8481     placeholder: false,
8482     before : false,
8483     after : false,
8484     size : false,
8485     hasFocus : false,
8486     preventMark: false,
8487     isFormField : true,
8488     value : '',
8489     labelWidth : 2,
8490     labelAlign : false,
8491     readOnly : false,
8492     align : false,
8493     formatedValue : false,
8494     forceFeedback : false,
8495     
8496     indicatorpos : 'left',
8497     
8498     labellg : 0,
8499     labelmd : 0,
8500     labelsm : 0,
8501     labelxs : 0,
8502     
8503     parentLabelAlign : function()
8504     {
8505         var parent = this;
8506         while (parent.parent()) {
8507             parent = parent.parent();
8508             if (typeof(parent.labelAlign) !='undefined') {
8509                 return parent.labelAlign;
8510             }
8511         }
8512         return 'left';
8513         
8514     },
8515     
8516     getAutoCreate : function()
8517     {
8518         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8519         
8520         var id = Roo.id();
8521         
8522         var cfg = {};
8523         
8524         if(this.inputType != 'hidden'){
8525             cfg.cls = 'form-group' //input-group
8526         }
8527         
8528         var input =  {
8529             tag: 'input',
8530             id : id,
8531             type : this.inputType,
8532             value : this.value,
8533             cls : 'form-control',
8534             placeholder : this.placeholder || '',
8535             autocomplete : this.autocomplete || 'new-password'
8536         };
8537         
8538         if(this.align){
8539             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8540         }
8541         
8542         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8543             input.maxLength = this.maxLength;
8544         }
8545         
8546         if (this.disabled) {
8547             input.disabled=true;
8548         }
8549         
8550         if (this.readOnly) {
8551             input.readonly=true;
8552         }
8553         
8554         if (this.name) {
8555             input.name = this.name;
8556         }
8557         
8558         if (this.size) {
8559             input.cls += ' input-' + this.size;
8560         }
8561         
8562         var settings=this;
8563         ['xs','sm','md','lg'].map(function(size){
8564             if (settings[size]) {
8565                 cfg.cls += ' col-' + size + '-' + settings[size];
8566             }
8567         });
8568         
8569         var inputblock = input;
8570         
8571         var feedback = {
8572             tag: 'span',
8573             cls: 'glyphicon form-control-feedback'
8574         };
8575             
8576         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8577             
8578             inputblock = {
8579                 cls : 'has-feedback',
8580                 cn :  [
8581                     input,
8582                     feedback
8583                 ] 
8584             };  
8585         }
8586         
8587         if (this.before || this.after) {
8588             
8589             inputblock = {
8590                 cls : 'input-group',
8591                 cn :  [] 
8592             };
8593             
8594             if (this.before && typeof(this.before) == 'string') {
8595                 
8596                 inputblock.cn.push({
8597                     tag :'span',
8598                     cls : 'roo-input-before input-group-addon',
8599                     html : this.before
8600                 });
8601             }
8602             if (this.before && typeof(this.before) == 'object') {
8603                 this.before = Roo.factory(this.before);
8604                 
8605                 inputblock.cn.push({
8606                     tag :'span',
8607                     cls : 'roo-input-before input-group-' +
8608                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8609                 });
8610             }
8611             
8612             inputblock.cn.push(input);
8613             
8614             if (this.after && typeof(this.after) == 'string') {
8615                 inputblock.cn.push({
8616                     tag :'span',
8617                     cls : 'roo-input-after input-group-addon',
8618                     html : this.after
8619                 });
8620             }
8621             if (this.after && typeof(this.after) == 'object') {
8622                 this.after = Roo.factory(this.after);
8623                 
8624                 inputblock.cn.push({
8625                     tag :'span',
8626                     cls : 'roo-input-after input-group-' +
8627                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8628                 });
8629             }
8630             
8631             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8632                 inputblock.cls += ' has-feedback';
8633                 inputblock.cn.push(feedback);
8634             }
8635         };
8636         
8637         if (align ==='left' && this.fieldLabel.length) {
8638             
8639             cfg.cls += ' roo-form-group-label-left';
8640             
8641             cfg.cn = [
8642                 {
8643                     tag : 'i',
8644                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8645                     tooltip : 'This field is required'
8646                 },
8647                 {
8648                     tag: 'label',
8649                     'for' :  id,
8650                     cls : 'control-label',
8651                     html : this.fieldLabel
8652
8653                 },
8654                 {
8655                     cls : "", 
8656                     cn: [
8657                         inputblock
8658                     ]
8659                 }
8660             ];
8661             
8662             var labelCfg = cfg.cn[1];
8663             var contentCfg = cfg.cn[2];
8664             
8665             if(this.indicatorpos == 'right'){
8666                 cfg.cn = [
8667                     {
8668                         tag: 'label',
8669                         'for' :  id,
8670                         cls : 'control-label',
8671                         html : this.fieldLabel
8672
8673                     },
8674                     {
8675                         tag : 'i',
8676                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8677                         tooltip : 'This field is required'
8678                     },
8679                     {
8680                         cls : "",
8681                         cn: [
8682                             inputblock
8683                         ]
8684                     }
8685
8686                 ];
8687                 
8688                 labelCfg = cfg.cn[0];
8689                 contentCfg = cfg.cn[2];
8690             
8691             }
8692             
8693             if(this.labelWidth > 12){
8694                 labelCfg.style = "width: " + this.labelWidth + 'px';
8695             }
8696             
8697             if(this.labelWidth < 13 && this.labelmd == 0){
8698                 this.labelmd = this.labelWidth;
8699             }
8700             
8701             if(this.labellg > 0){
8702                 labelCfg.cls += ' col-lg-' + this.labellg;
8703                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8704             }
8705             
8706             if(this.labelmd > 0){
8707                 labelCfg.cls += ' col-md-' + this.labelmd;
8708                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8709             }
8710             
8711             if(this.labelsm > 0){
8712                 labelCfg.cls += ' col-sm-' + this.labelsm;
8713                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8714             }
8715             
8716             if(this.labelxs > 0){
8717                 labelCfg.cls += ' col-xs-' + this.labelxs;
8718                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8719             }
8720             
8721             
8722         } else if ( this.fieldLabel.length) {
8723                 
8724             cfg.cn = [
8725                 {
8726                     tag : 'i',
8727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8728                     tooltip : 'This field is required'
8729                 },
8730                 {
8731                     tag: 'label',
8732                    //cls : 'input-group-addon',
8733                     html : this.fieldLabel
8734
8735                 },
8736
8737                inputblock
8738
8739            ];
8740            
8741            if(this.indicatorpos == 'right'){
8742                 
8743                 cfg.cn = [
8744                     {
8745                         tag: 'label',
8746                        //cls : 'input-group-addon',
8747                         html : this.fieldLabel
8748
8749                     },
8750                     {
8751                         tag : 'i',
8752                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8753                         tooltip : 'This field is required'
8754                     },
8755
8756                    inputblock
8757
8758                ];
8759
8760             }
8761
8762         } else {
8763             
8764             cfg.cn = [
8765
8766                     inputblock
8767
8768             ];
8769                 
8770                 
8771         };
8772         
8773         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8774            cfg.cls += ' navbar-form';
8775         }
8776         
8777         if (this.parentType === 'NavGroup') {
8778            cfg.cls += ' navbar-form';
8779            cfg.tag = 'li';
8780         }
8781         
8782         return cfg;
8783         
8784     },
8785     /**
8786      * return the real input element.
8787      */
8788     inputEl: function ()
8789     {
8790         return this.el.select('input.form-control',true).first();
8791     },
8792     
8793     tooltipEl : function()
8794     {
8795         return this.inputEl();
8796     },
8797     
8798     indicatorEl : function()
8799     {
8800         var indicator = this.el.select('i.roo-required-indicator',true).first();
8801         
8802         if(!indicator){
8803             return false;
8804         }
8805         
8806         return indicator;
8807         
8808     },
8809     
8810     setDisabled : function(v)
8811     {
8812         var i  = this.inputEl().dom;
8813         if (!v) {
8814             i.removeAttribute('disabled');
8815             return;
8816             
8817         }
8818         i.setAttribute('disabled','true');
8819     },
8820     initEvents : function()
8821     {
8822           
8823         this.inputEl().on("keydown" , this.fireKey,  this);
8824         this.inputEl().on("focus", this.onFocus,  this);
8825         this.inputEl().on("blur", this.onBlur,  this);
8826         
8827         this.inputEl().relayEvent('keyup', this);
8828         
8829         this.indicator = this.indicatorEl();
8830         
8831         if(this.indicator){
8832             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8833             this.indicator.hide();
8834         }
8835  
8836         // reference to original value for reset
8837         this.originalValue = this.getValue();
8838         //Roo.form.TextField.superclass.initEvents.call(this);
8839         if(this.validationEvent == 'keyup'){
8840             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8841             this.inputEl().on('keyup', this.filterValidation, this);
8842         }
8843         else if(this.validationEvent !== false){
8844             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8845         }
8846         
8847         if(this.selectOnFocus){
8848             this.on("focus", this.preFocus, this);
8849             
8850         }
8851         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8852             this.inputEl().on("keypress", this.filterKeys, this);
8853         } else {
8854             this.inputEl().relayEvent('keypress', this);
8855         }
8856        /* if(this.grow){
8857             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8858             this.el.on("click", this.autoSize,  this);
8859         }
8860         */
8861         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8862             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8863         }
8864         
8865         if (typeof(this.before) == 'object') {
8866             this.before.render(this.el.select('.roo-input-before',true).first());
8867         }
8868         if (typeof(this.after) == 'object') {
8869             this.after.render(this.el.select('.roo-input-after',true).first());
8870         }
8871         
8872         
8873     },
8874     filterValidation : function(e){
8875         if(!e.isNavKeyPress()){
8876             this.validationTask.delay(this.validationDelay);
8877         }
8878     },
8879      /**
8880      * Validates the field value
8881      * @return {Boolean} True if the value is valid, else false
8882      */
8883     validate : function(){
8884         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8885         if(this.disabled || this.validateValue(this.getRawValue())){
8886             this.markValid();
8887             return true;
8888         }
8889         
8890         this.markInvalid();
8891         return false;
8892     },
8893     
8894     
8895     /**
8896      * Validates a value according to the field's validation rules and marks the field as invalid
8897      * if the validation fails
8898      * @param {Mixed} value The value to validate
8899      * @return {Boolean} True if the value is valid, else false
8900      */
8901     validateValue : function(value){
8902         if(value.length < 1)  { // if it's blank
8903             if(this.allowBlank){
8904                 return true;
8905             }
8906             return false;
8907         }
8908         
8909         if(value.length < this.minLength){
8910             return false;
8911         }
8912         if(value.length > this.maxLength){
8913             return false;
8914         }
8915         if(this.vtype){
8916             var vt = Roo.form.VTypes;
8917             if(!vt[this.vtype](value, this)){
8918                 return false;
8919             }
8920         }
8921         if(typeof this.validator == "function"){
8922             var msg = this.validator(value);
8923             if(msg !== true){
8924                 return false;
8925             }
8926         }
8927         
8928         if(this.regex && !this.regex.test(value)){
8929             return false;
8930         }
8931         
8932         return true;
8933     },
8934
8935     
8936     
8937      // private
8938     fireKey : function(e){
8939         //Roo.log('field ' + e.getKey());
8940         if(e.isNavKeyPress()){
8941             this.fireEvent("specialkey", this, e);
8942         }
8943     },
8944     focus : function (selectText){
8945         if(this.rendered){
8946             this.inputEl().focus();
8947             if(selectText === true){
8948                 this.inputEl().dom.select();
8949             }
8950         }
8951         return this;
8952     } ,
8953     
8954     onFocus : function(){
8955         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8956            // this.el.addClass(this.focusClass);
8957         }
8958         if(!this.hasFocus){
8959             this.hasFocus = true;
8960             this.startValue = this.getValue();
8961             this.fireEvent("focus", this);
8962         }
8963     },
8964     
8965     beforeBlur : Roo.emptyFn,
8966
8967     
8968     // private
8969     onBlur : function(){
8970         this.beforeBlur();
8971         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8972             //this.el.removeClass(this.focusClass);
8973         }
8974         this.hasFocus = false;
8975         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8976             this.validate();
8977         }
8978         var v = this.getValue();
8979         if(String(v) !== String(this.startValue)){
8980             this.fireEvent('change', this, v, this.startValue);
8981         }
8982         this.fireEvent("blur", this);
8983     },
8984     
8985     /**
8986      * Resets the current field value to the originally loaded value and clears any validation messages
8987      */
8988     reset : function(){
8989         this.setValue(this.originalValue);
8990         this.validate();
8991     },
8992      /**
8993      * Returns the name of the field
8994      * @return {Mixed} name The name field
8995      */
8996     getName: function(){
8997         return this.name;
8998     },
8999      /**
9000      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9001      * @return {Mixed} value The field value
9002      */
9003     getValue : function(){
9004         
9005         var v = this.inputEl().getValue();
9006         
9007         return v;
9008     },
9009     /**
9010      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9011      * @return {Mixed} value The field value
9012      */
9013     getRawValue : function(){
9014         var v = this.inputEl().getValue();
9015         
9016         return v;
9017     },
9018     
9019     /**
9020      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9021      * @param {Mixed} value The value to set
9022      */
9023     setRawValue : function(v){
9024         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9025     },
9026     
9027     selectText : function(start, end){
9028         var v = this.getRawValue();
9029         if(v.length > 0){
9030             start = start === undefined ? 0 : start;
9031             end = end === undefined ? v.length : end;
9032             var d = this.inputEl().dom;
9033             if(d.setSelectionRange){
9034                 d.setSelectionRange(start, end);
9035             }else if(d.createTextRange){
9036                 var range = d.createTextRange();
9037                 range.moveStart("character", start);
9038                 range.moveEnd("character", v.length-end);
9039                 range.select();
9040             }
9041         }
9042     },
9043     
9044     /**
9045      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9046      * @param {Mixed} value The value to set
9047      */
9048     setValue : function(v){
9049         this.value = v;
9050         if(this.rendered){
9051             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9052             this.validate();
9053         }
9054     },
9055     
9056     /*
9057     processValue : function(value){
9058         if(this.stripCharsRe){
9059             var newValue = value.replace(this.stripCharsRe, '');
9060             if(newValue !== value){
9061                 this.setRawValue(newValue);
9062                 return newValue;
9063             }
9064         }
9065         return value;
9066     },
9067   */
9068     preFocus : function(){
9069         
9070         if(this.selectOnFocus){
9071             this.inputEl().dom.select();
9072         }
9073     },
9074     filterKeys : function(e){
9075         var k = e.getKey();
9076         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9077             return;
9078         }
9079         var c = e.getCharCode(), cc = String.fromCharCode(c);
9080         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9081             return;
9082         }
9083         if(!this.maskRe.test(cc)){
9084             e.stopEvent();
9085         }
9086     },
9087      /**
9088      * Clear any invalid styles/messages for this field
9089      */
9090     clearInvalid : function(){
9091         
9092         if(!this.el || this.preventMark){ // not rendered
9093             return;
9094         }
9095         
9096      
9097         this.el.removeClass(this.invalidClass);
9098         
9099         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9100             
9101             var feedback = this.el.select('.form-control-feedback', true).first();
9102             
9103             if(feedback){
9104                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9105             }
9106             
9107         }
9108         
9109         this.fireEvent('valid', this);
9110     },
9111     
9112      /**
9113      * Mark this field as valid
9114      */
9115     markValid : function()
9116     {
9117         if(!this.el  || this.preventMark){ // not rendered...
9118             return;
9119         }
9120         
9121         this.el.removeClass([this.invalidClass, this.validClass]);
9122         
9123         var feedback = this.el.select('.form-control-feedback', true).first();
9124             
9125         if(feedback){
9126             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9127         }
9128
9129         if(this.disabled){
9130             return;
9131         }
9132         
9133         if(this.allowBlank && !this.getRawValue().length){
9134             return;
9135         }
9136         
9137         if(this.indicator){
9138             this.indicator.hide();
9139         }
9140         
9141         this.el.addClass(this.validClass);
9142         
9143         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9144             
9145             var feedback = this.el.select('.form-control-feedback', true).first();
9146             
9147             if(feedback){
9148                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9149                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9150             }
9151             
9152         }
9153         
9154         this.fireEvent('valid', this);
9155     },
9156     
9157      /**
9158      * Mark this field as invalid
9159      * @param {String} msg The validation message
9160      */
9161     markInvalid : function(msg)
9162     {
9163         if(!this.el  || this.preventMark){ // not rendered
9164             return;
9165         }
9166         
9167         this.el.removeClass([this.invalidClass, this.validClass]);
9168         
9169         var feedback = this.el.select('.form-control-feedback', true).first();
9170             
9171         if(feedback){
9172             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9173         }
9174
9175         if(this.disabled){
9176             return;
9177         }
9178         
9179         if(this.allowBlank && !this.getRawValue().length){
9180             return;
9181         }
9182         
9183         if(this.indicator){
9184             this.indicator.show();
9185         }
9186         
9187         this.el.addClass(this.invalidClass);
9188         
9189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9190             
9191             var feedback = this.el.select('.form-control-feedback', true).first();
9192             
9193             if(feedback){
9194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9195                 
9196                 if(this.getValue().length || this.forceFeedback){
9197                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9198                 }
9199                 
9200             }
9201             
9202         }
9203         
9204         this.fireEvent('invalid', this, msg);
9205     },
9206     // private
9207     SafariOnKeyDown : function(event)
9208     {
9209         // this is a workaround for a password hang bug on chrome/ webkit.
9210         if (this.inputEl().dom.type != 'password') {
9211             return;
9212         }
9213         
9214         var isSelectAll = false;
9215         
9216         if(this.inputEl().dom.selectionEnd > 0){
9217             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9218         }
9219         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9220             event.preventDefault();
9221             this.setValue('');
9222             return;
9223         }
9224         
9225         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9226             
9227             event.preventDefault();
9228             // this is very hacky as keydown always get's upper case.
9229             //
9230             var cc = String.fromCharCode(event.getCharCode());
9231             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9232             
9233         }
9234     },
9235     adjustWidth : function(tag, w){
9236         tag = tag.toLowerCase();
9237         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9238             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9239                 if(tag == 'input'){
9240                     return w + 2;
9241                 }
9242                 if(tag == 'textarea'){
9243                     return w-2;
9244                 }
9245             }else if(Roo.isOpera){
9246                 if(tag == 'input'){
9247                     return w + 2;
9248                 }
9249                 if(tag == 'textarea'){
9250                     return w-2;
9251                 }
9252             }
9253         }
9254         return w;
9255     }
9256     
9257 });
9258
9259  
9260 /*
9261  * - LGPL
9262  *
9263  * Input
9264  * 
9265  */
9266
9267 /**
9268  * @class Roo.bootstrap.TextArea
9269  * @extends Roo.bootstrap.Input
9270  * Bootstrap TextArea class
9271  * @cfg {Number} cols Specifies the visible width of a text area
9272  * @cfg {Number} rows Specifies the visible number of lines in a text area
9273  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9274  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9275  * @cfg {string} html text
9276  * 
9277  * @constructor
9278  * Create a new TextArea
9279  * @param {Object} config The config object
9280  */
9281
9282 Roo.bootstrap.TextArea = function(config){
9283     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9284    
9285 };
9286
9287 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9288      
9289     cols : false,
9290     rows : 5,
9291     readOnly : false,
9292     warp : 'soft',
9293     resize : false,
9294     value: false,
9295     html: false,
9296     
9297     getAutoCreate : function(){
9298         
9299         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9300         
9301         var id = Roo.id();
9302         
9303         var cfg = {};
9304         
9305         var input =  {
9306             tag: 'textarea',
9307             id : id,
9308             warp : this.warp,
9309             rows : this.rows,
9310             value : this.value || '',
9311             html: this.html || '',
9312             cls : 'form-control',
9313             placeholder : this.placeholder || '' 
9314             
9315         };
9316         
9317         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9318             input.maxLength = this.maxLength;
9319         }
9320         
9321         if(this.resize){
9322             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9323         }
9324         
9325         if(this.cols){
9326             input.cols = this.cols;
9327         }
9328         
9329         if (this.readOnly) {
9330             input.readonly = true;
9331         }
9332         
9333         if (this.name) {
9334             input.name = this.name;
9335         }
9336         
9337         if (this.size) {
9338             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9339         }
9340         
9341         var settings=this;
9342         ['xs','sm','md','lg'].map(function(size){
9343             if (settings[size]) {
9344                 cfg.cls += ' col-' + size + '-' + settings[size];
9345             }
9346         });
9347         
9348         var inputblock = input;
9349         
9350         if(this.hasFeedback && !this.allowBlank){
9351             
9352             var feedback = {
9353                 tag: 'span',
9354                 cls: 'glyphicon form-control-feedback'
9355             };
9356
9357             inputblock = {
9358                 cls : 'has-feedback',
9359                 cn :  [
9360                     input,
9361                     feedback
9362                 ] 
9363             };  
9364         }
9365         
9366         
9367         if (this.before || this.after) {
9368             
9369             inputblock = {
9370                 cls : 'input-group',
9371                 cn :  [] 
9372             };
9373             if (this.before) {
9374                 inputblock.cn.push({
9375                     tag :'span',
9376                     cls : 'input-group-addon',
9377                     html : this.before
9378                 });
9379             }
9380             
9381             inputblock.cn.push(input);
9382             
9383             if(this.hasFeedback && !this.allowBlank){
9384                 inputblock.cls += ' has-feedback';
9385                 inputblock.cn.push(feedback);
9386             }
9387             
9388             if (this.after) {
9389                 inputblock.cn.push({
9390                     tag :'span',
9391                     cls : 'input-group-addon',
9392                     html : this.after
9393                 });
9394             }
9395             
9396         }
9397         
9398         if (align ==='left' && this.fieldLabel.length) {
9399             cfg.cn = [
9400                 {
9401                     tag: 'label',
9402                     'for' :  id,
9403                     cls : 'control-label',
9404                     html : this.fieldLabel
9405                 },
9406                 {
9407                     cls : "",
9408                     cn: [
9409                         inputblock
9410                     ]
9411                 }
9412
9413             ];
9414             
9415             if(this.labelWidth > 12){
9416                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9417             }
9418
9419             if(this.labelWidth < 13 && this.labelmd == 0){
9420                 this.labelmd = this.labelWidth;
9421             }
9422
9423             if(this.labellg > 0){
9424                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9425                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9426             }
9427
9428             if(this.labelmd > 0){
9429                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9430                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9431             }
9432
9433             if(this.labelsm > 0){
9434                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9435                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9436             }
9437
9438             if(this.labelxs > 0){
9439                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9440                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9441             }
9442             
9443         } else if ( this.fieldLabel.length) {
9444             cfg.cn = [
9445
9446                {
9447                    tag: 'label',
9448                    //cls : 'input-group-addon',
9449                    html : this.fieldLabel
9450
9451                },
9452
9453                inputblock
9454
9455            ];
9456
9457         } else {
9458
9459             cfg.cn = [
9460
9461                 inputblock
9462
9463             ];
9464                 
9465         }
9466         
9467         if (this.disabled) {
9468             input.disabled=true;
9469         }
9470         
9471         return cfg;
9472         
9473     },
9474     /**
9475      * return the real textarea element.
9476      */
9477     inputEl: function ()
9478     {
9479         return this.el.select('textarea.form-control',true).first();
9480     },
9481     
9482     /**
9483      * Clear any invalid styles/messages for this field
9484      */
9485     clearInvalid : function()
9486     {
9487         
9488         if(!this.el || this.preventMark){ // not rendered
9489             return;
9490         }
9491         
9492         var label = this.el.select('label', true).first();
9493         var icon = this.el.select('i.fa-star', true).first();
9494         
9495         if(label && icon){
9496             icon.remove();
9497         }
9498         
9499         this.el.removeClass(this.invalidClass);
9500         
9501         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9502             
9503             var feedback = this.el.select('.form-control-feedback', true).first();
9504             
9505             if(feedback){
9506                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9507             }
9508             
9509         }
9510         
9511         this.fireEvent('valid', this);
9512     },
9513     
9514      /**
9515      * Mark this field as valid
9516      */
9517     markValid : function()
9518     {
9519         if(!this.el  || this.preventMark){ // not rendered
9520             return;
9521         }
9522         
9523         this.el.removeClass([this.invalidClass, this.validClass]);
9524         
9525         var feedback = this.el.select('.form-control-feedback', true).first();
9526             
9527         if(feedback){
9528             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529         }
9530
9531         if(this.disabled || this.allowBlank){
9532             return;
9533         }
9534         
9535         var label = this.el.select('label', true).first();
9536         var icon = this.el.select('i.fa-star', true).first();
9537         
9538         if(label && icon){
9539             icon.remove();
9540         }
9541         
9542         this.el.addClass(this.validClass);
9543         
9544         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9545             
9546             var feedback = this.el.select('.form-control-feedback', true).first();
9547             
9548             if(feedback){
9549                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9550                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9551             }
9552             
9553         }
9554         
9555         this.fireEvent('valid', this);
9556     },
9557     
9558      /**
9559      * Mark this field as invalid
9560      * @param {String} msg The validation message
9561      */
9562     markInvalid : function(msg)
9563     {
9564         if(!this.el  || this.preventMark){ // not rendered
9565             return;
9566         }
9567         
9568         this.el.removeClass([this.invalidClass, this.validClass]);
9569         
9570         var feedback = this.el.select('.form-control-feedback', true).first();
9571             
9572         if(feedback){
9573             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9574         }
9575
9576         if(this.disabled || this.allowBlank){
9577             return;
9578         }
9579         
9580         var label = this.el.select('label', true).first();
9581         var icon = this.el.select('i.fa-star', true).first();
9582         
9583         if(!this.getValue().length && label && !icon){
9584             this.el.createChild({
9585                 tag : 'i',
9586                 cls : 'text-danger fa fa-lg fa-star',
9587                 tooltip : 'This field is required',
9588                 style : 'margin-right:5px;'
9589             }, label, true);
9590         }
9591
9592         this.el.addClass(this.invalidClass);
9593         
9594         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9595             
9596             var feedback = this.el.select('.form-control-feedback', true).first();
9597             
9598             if(feedback){
9599                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9600                 
9601                 if(this.getValue().length || this.forceFeedback){
9602                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9603                 }
9604                 
9605             }
9606             
9607         }
9608         
9609         this.fireEvent('invalid', this, msg);
9610     }
9611 });
9612
9613  
9614 /*
9615  * - LGPL
9616  *
9617  * trigger field - base class for combo..
9618  * 
9619  */
9620  
9621 /**
9622  * @class Roo.bootstrap.TriggerField
9623  * @extends Roo.bootstrap.Input
9624  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9625  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9626  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9627  * for which you can provide a custom implementation.  For example:
9628  * <pre><code>
9629 var trigger = new Roo.bootstrap.TriggerField();
9630 trigger.onTriggerClick = myTriggerFn;
9631 trigger.applyTo('my-field');
9632 </code></pre>
9633  *
9634  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9635  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9636  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9637  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9638  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9639
9640  * @constructor
9641  * Create a new TriggerField.
9642  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9643  * to the base TextField)
9644  */
9645 Roo.bootstrap.TriggerField = function(config){
9646     this.mimicing = false;
9647     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9648 };
9649
9650 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9651     /**
9652      * @cfg {String} triggerClass A CSS class to apply to the trigger
9653      */
9654      /**
9655      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9656      */
9657     hideTrigger:false,
9658
9659     /**
9660      * @cfg {Boolean} removable (true|false) special filter default false
9661      */
9662     removable : false,
9663     
9664     /** @cfg {Boolean} grow @hide */
9665     /** @cfg {Number} growMin @hide */
9666     /** @cfg {Number} growMax @hide */
9667
9668     /**
9669      * @hide 
9670      * @method
9671      */
9672     autoSize: Roo.emptyFn,
9673     // private
9674     monitorTab : true,
9675     // private
9676     deferHeight : true,
9677
9678     
9679     actionMode : 'wrap',
9680     
9681     caret : false,
9682     
9683     
9684     getAutoCreate : function(){
9685        
9686         var align = this.labelAlign || this.parentLabelAlign();
9687         
9688         var id = Roo.id();
9689         
9690         var cfg = {
9691             cls: 'form-group' //input-group
9692         };
9693         
9694         
9695         var input =  {
9696             tag: 'input',
9697             id : id,
9698             type : this.inputType,
9699             cls : 'form-control',
9700             autocomplete: 'new-password',
9701             placeholder : this.placeholder || '' 
9702             
9703         };
9704         if (this.name) {
9705             input.name = this.name;
9706         }
9707         if (this.size) {
9708             input.cls += ' input-' + this.size;
9709         }
9710         
9711         if (this.disabled) {
9712             input.disabled=true;
9713         }
9714         
9715         var inputblock = input;
9716         
9717         if(this.hasFeedback && !this.allowBlank){
9718             
9719             var feedback = {
9720                 tag: 'span',
9721                 cls: 'glyphicon form-control-feedback'
9722             };
9723             
9724             if(this.removable && !this.editable && !this.tickable){
9725                 inputblock = {
9726                     cls : 'has-feedback',
9727                     cn :  [
9728                         inputblock,
9729                         {
9730                             tag: 'button',
9731                             html : 'x',
9732                             cls : 'roo-combo-removable-btn close'
9733                         },
9734                         feedback
9735                     ] 
9736                 };
9737             } else {
9738                 inputblock = {
9739                     cls : 'has-feedback',
9740                     cn :  [
9741                         inputblock,
9742                         feedback
9743                     ] 
9744                 };
9745             }
9746
9747         } else {
9748             if(this.removable && !this.editable && !this.tickable){
9749                 inputblock = {
9750                     cls : 'roo-removable',
9751                     cn :  [
9752                         inputblock,
9753                         {
9754                             tag: 'button',
9755                             html : 'x',
9756                             cls : 'roo-combo-removable-btn close'
9757                         }
9758                     ] 
9759                 };
9760             }
9761         }
9762         
9763         if (this.before || this.after) {
9764             
9765             inputblock = {
9766                 cls : 'input-group',
9767                 cn :  [] 
9768             };
9769             if (this.before) {
9770                 inputblock.cn.push({
9771                     tag :'span',
9772                     cls : 'input-group-addon',
9773                     html : this.before
9774                 });
9775             }
9776             
9777             inputblock.cn.push(input);
9778             
9779             if(this.hasFeedback && !this.allowBlank){
9780                 inputblock.cls += ' has-feedback';
9781                 inputblock.cn.push(feedback);
9782             }
9783             
9784             if (this.after) {
9785                 inputblock.cn.push({
9786                     tag :'span',
9787                     cls : 'input-group-addon',
9788                     html : this.after
9789                 });
9790             }
9791             
9792         };
9793         
9794         var box = {
9795             tag: 'div',
9796             cn: [
9797                 {
9798                     tag: 'input',
9799                     type : 'hidden',
9800                     cls: 'form-hidden-field'
9801                 },
9802                 inputblock
9803             ]
9804             
9805         };
9806         
9807         if(this.multiple){
9808             box = {
9809                 tag: 'div',
9810                 cn: [
9811                     {
9812                         tag: 'input',
9813                         type : 'hidden',
9814                         cls: 'form-hidden-field'
9815                     },
9816                     {
9817                         tag: 'ul',
9818                         cls: 'roo-select2-choices',
9819                         cn:[
9820                             {
9821                                 tag: 'li',
9822                                 cls: 'roo-select2-search-field',
9823                                 cn: [
9824
9825                                     inputblock
9826                                 ]
9827                             }
9828                         ]
9829                     }
9830                 ]
9831             }
9832         };
9833         
9834         var combobox = {
9835             cls: 'roo-select2-container input-group',
9836             cn: [
9837                 box
9838 //                {
9839 //                    tag: 'ul',
9840 //                    cls: 'typeahead typeahead-long dropdown-menu',
9841 //                    style: 'display:none'
9842 //                }
9843             ]
9844         };
9845         
9846         if(!this.multiple && this.showToggleBtn){
9847             
9848             var caret = {
9849                         tag: 'span',
9850                         cls: 'caret'
9851              };
9852             if (this.caret != false) {
9853                 caret = {
9854                      tag: 'i',
9855                      cls: 'fa fa-' + this.caret
9856                 };
9857                 
9858             }
9859             
9860             combobox.cn.push({
9861                 tag :'span',
9862                 cls : 'input-group-addon btn dropdown-toggle',
9863                 cn : [
9864                     caret,
9865                     {
9866                         tag: 'span',
9867                         cls: 'combobox-clear',
9868                         cn  : [
9869                             {
9870                                 tag : 'i',
9871                                 cls: 'icon-remove'
9872                             }
9873                         ]
9874                     }
9875                 ]
9876
9877             })
9878         }
9879         
9880         if(this.multiple){
9881             combobox.cls += ' roo-select2-container-multi';
9882         }
9883         
9884         if (align ==='left' && this.fieldLabel.length) {
9885             
9886             cfg.cls += ' roo-form-group-label-left';
9887
9888             cfg.cn = [
9889                 {
9890                     tag : 'i',
9891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9892                     tooltip : 'This field is required'
9893                 },
9894                 {
9895                     tag: 'label',
9896                     'for' :  id,
9897                     cls : 'control-label',
9898                     html : this.fieldLabel
9899
9900                 },
9901                 {
9902                     cls : "", 
9903                     cn: [
9904                         combobox
9905                     ]
9906                 }
9907
9908             ];
9909             
9910             var labelCfg = cfg.cn[1];
9911             var contentCfg = cfg.cn[2];
9912             
9913             if(this.indicatorpos == 'right'){
9914                 cfg.cn = [
9915                     {
9916                         tag: 'label',
9917                         'for' :  id,
9918                         cls : 'control-label',
9919                         cn : [
9920                             {
9921                                 tag : 'span',
9922                                 html : this.fieldLabel
9923                             },
9924                             {
9925                                 tag : 'i',
9926                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9927                                 tooltip : 'This field is required'
9928                             }
9929                         ]
9930                     },
9931                     {
9932                         cls : "", 
9933                         cn: [
9934                             combobox
9935                         ]
9936                     }
9937
9938                 ];
9939                 
9940                 labelCfg = cfg.cn[0];
9941                 contentCfg = cfg.cn[1];
9942             }
9943             
9944             if(this.labelWidth > 12){
9945                 labelCfg.style = "width: " + this.labelWidth + 'px';
9946             }
9947             
9948             if(this.labelWidth < 13 && this.labelmd == 0){
9949                 this.labelmd = this.labelWidth;
9950             }
9951             
9952             if(this.labellg > 0){
9953                 labelCfg.cls += ' col-lg-' + this.labellg;
9954                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9955             }
9956             
9957             if(this.labelmd > 0){
9958                 labelCfg.cls += ' col-md-' + this.labelmd;
9959                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9960             }
9961             
9962             if(this.labelsm > 0){
9963                 labelCfg.cls += ' col-sm-' + this.labelsm;
9964                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9965             }
9966             
9967             if(this.labelxs > 0){
9968                 labelCfg.cls += ' col-xs-' + this.labelxs;
9969                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9970             }
9971             
9972         } else if ( this.fieldLabel.length) {
9973 //                Roo.log(" label");
9974             cfg.cn = [
9975                 {
9976                    tag : 'i',
9977                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9978                    tooltip : 'This field is required'
9979                },
9980                {
9981                    tag: 'label',
9982                    //cls : 'input-group-addon',
9983                    html : this.fieldLabel
9984
9985                },
9986
9987                combobox
9988
9989             ];
9990             
9991             if(this.indicatorpos == 'right'){
9992                 
9993                 cfg.cn = [
9994                     {
9995                        tag: 'label',
9996                        cn : [
9997                            {
9998                                tag : 'span',
9999                                html : this.fieldLabel
10000                            },
10001                            {
10002                               tag : 'i',
10003                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10004                               tooltip : 'This field is required'
10005                            }
10006                        ]
10007
10008                     },
10009                     combobox
10010
10011                 ];
10012
10013             }
10014
10015         } else {
10016             
10017 //                Roo.log(" no label && no align");
10018                 cfg = combobox
10019                      
10020                 
10021         }
10022         
10023         var settings=this;
10024         ['xs','sm','md','lg'].map(function(size){
10025             if (settings[size]) {
10026                 cfg.cls += ' col-' + size + '-' + settings[size];
10027             }
10028         });
10029         
10030         return cfg;
10031         
10032     },
10033     
10034     
10035     
10036     // private
10037     onResize : function(w, h){
10038 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10039 //        if(typeof w == 'number'){
10040 //            var x = w - this.trigger.getWidth();
10041 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10042 //            this.trigger.setStyle('left', x+'px');
10043 //        }
10044     },
10045
10046     // private
10047     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10048
10049     // private
10050     getResizeEl : function(){
10051         return this.inputEl();
10052     },
10053
10054     // private
10055     getPositionEl : function(){
10056         return this.inputEl();
10057     },
10058
10059     // private
10060     alignErrorIcon : function(){
10061         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10062     },
10063
10064     // private
10065     initEvents : function(){
10066         
10067         this.createList();
10068         
10069         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10070         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10071         if(!this.multiple && this.showToggleBtn){
10072             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10073             if(this.hideTrigger){
10074                 this.trigger.setDisplayed(false);
10075             }
10076             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10077         }
10078         
10079         if(this.multiple){
10080             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10081         }
10082         
10083         if(this.removable && !this.editable && !this.tickable){
10084             var close = this.closeTriggerEl();
10085             
10086             if(close){
10087                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10088                 close.on('click', this.removeBtnClick, this, close);
10089             }
10090         }
10091         
10092         //this.trigger.addClassOnOver('x-form-trigger-over');
10093         //this.trigger.addClassOnClick('x-form-trigger-click');
10094         
10095         //if(!this.width){
10096         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10097         //}
10098     },
10099     
10100     closeTriggerEl : function()
10101     {
10102         var close = this.el.select('.roo-combo-removable-btn', true).first();
10103         return close ? close : false;
10104     },
10105     
10106     removeBtnClick : function(e, h, el)
10107     {
10108         e.preventDefault();
10109         
10110         if(this.fireEvent("remove", this) !== false){
10111             this.reset();
10112             this.fireEvent("afterremove", this)
10113         }
10114     },
10115     
10116     createList : function()
10117     {
10118         this.list = Roo.get(document.body).createChild({
10119             tag: 'ul',
10120             cls: 'typeahead typeahead-long dropdown-menu',
10121             style: 'display:none'
10122         });
10123         
10124         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10125         
10126     },
10127
10128     // private
10129     initTrigger : function(){
10130        
10131     },
10132
10133     // private
10134     onDestroy : function(){
10135         if(this.trigger){
10136             this.trigger.removeAllListeners();
10137           //  this.trigger.remove();
10138         }
10139         //if(this.wrap){
10140         //    this.wrap.remove();
10141         //}
10142         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10143     },
10144
10145     // private
10146     onFocus : function(){
10147         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10148         /*
10149         if(!this.mimicing){
10150             this.wrap.addClass('x-trigger-wrap-focus');
10151             this.mimicing = true;
10152             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10153             if(this.monitorTab){
10154                 this.el.on("keydown", this.checkTab, this);
10155             }
10156         }
10157         */
10158     },
10159
10160     // private
10161     checkTab : function(e){
10162         if(e.getKey() == e.TAB){
10163             this.triggerBlur();
10164         }
10165     },
10166
10167     // private
10168     onBlur : function(){
10169         // do nothing
10170     },
10171
10172     // private
10173     mimicBlur : function(e, t){
10174         /*
10175         if(!this.wrap.contains(t) && this.validateBlur()){
10176             this.triggerBlur();
10177         }
10178         */
10179     },
10180
10181     // private
10182     triggerBlur : function(){
10183         this.mimicing = false;
10184         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10185         if(this.monitorTab){
10186             this.el.un("keydown", this.checkTab, this);
10187         }
10188         //this.wrap.removeClass('x-trigger-wrap-focus');
10189         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10190     },
10191
10192     // private
10193     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10194     validateBlur : function(e, t){
10195         return true;
10196     },
10197
10198     // private
10199     onDisable : function(){
10200         this.inputEl().dom.disabled = true;
10201         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10202         //if(this.wrap){
10203         //    this.wrap.addClass('x-item-disabled');
10204         //}
10205     },
10206
10207     // private
10208     onEnable : function(){
10209         this.inputEl().dom.disabled = false;
10210         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10211         //if(this.wrap){
10212         //    this.el.removeClass('x-item-disabled');
10213         //}
10214     },
10215
10216     // private
10217     onShow : function(){
10218         var ae = this.getActionEl();
10219         
10220         if(ae){
10221             ae.dom.style.display = '';
10222             ae.dom.style.visibility = 'visible';
10223         }
10224     },
10225
10226     // private
10227     
10228     onHide : function(){
10229         var ae = this.getActionEl();
10230         ae.dom.style.display = 'none';
10231     },
10232
10233     /**
10234      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10235      * by an implementing function.
10236      * @method
10237      * @param {EventObject} e
10238      */
10239     onTriggerClick : Roo.emptyFn
10240 });
10241  /*
10242  * Based on:
10243  * Ext JS Library 1.1.1
10244  * Copyright(c) 2006-2007, Ext JS, LLC.
10245  *
10246  * Originally Released Under LGPL - original licence link has changed is not relivant.
10247  *
10248  * Fork - LGPL
10249  * <script type="text/javascript">
10250  */
10251
10252
10253 /**
10254  * @class Roo.data.SortTypes
10255  * @singleton
10256  * Defines the default sorting (casting?) comparison functions used when sorting data.
10257  */
10258 Roo.data.SortTypes = {
10259     /**
10260      * Default sort that does nothing
10261      * @param {Mixed} s The value being converted
10262      * @return {Mixed} The comparison value
10263      */
10264     none : function(s){
10265         return s;
10266     },
10267     
10268     /**
10269      * The regular expression used to strip tags
10270      * @type {RegExp}
10271      * @property
10272      */
10273     stripTagsRE : /<\/?[^>]+>/gi,
10274     
10275     /**
10276      * Strips all HTML tags to sort on text only
10277      * @param {Mixed} s The value being converted
10278      * @return {String} The comparison value
10279      */
10280     asText : function(s){
10281         return String(s).replace(this.stripTagsRE, "");
10282     },
10283     
10284     /**
10285      * Strips all HTML tags to sort on text only - Case insensitive
10286      * @param {Mixed} s The value being converted
10287      * @return {String} The comparison value
10288      */
10289     asUCText : function(s){
10290         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10291     },
10292     
10293     /**
10294      * Case insensitive string
10295      * @param {Mixed} s The value being converted
10296      * @return {String} The comparison value
10297      */
10298     asUCString : function(s) {
10299         return String(s).toUpperCase();
10300     },
10301     
10302     /**
10303      * Date sorting
10304      * @param {Mixed} s The value being converted
10305      * @return {Number} The comparison value
10306      */
10307     asDate : function(s) {
10308         if(!s){
10309             return 0;
10310         }
10311         if(s instanceof Date){
10312             return s.getTime();
10313         }
10314         return Date.parse(String(s));
10315     },
10316     
10317     /**
10318      * Float sorting
10319      * @param {Mixed} s The value being converted
10320      * @return {Float} The comparison value
10321      */
10322     asFloat : function(s) {
10323         var val = parseFloat(String(s).replace(/,/g, ""));
10324         if(isNaN(val)) {
10325             val = 0;
10326         }
10327         return val;
10328     },
10329     
10330     /**
10331      * Integer sorting
10332      * @param {Mixed} s The value being converted
10333      * @return {Number} The comparison value
10334      */
10335     asInt : function(s) {
10336         var val = parseInt(String(s).replace(/,/g, ""));
10337         if(isNaN(val)) {
10338             val = 0;
10339         }
10340         return val;
10341     }
10342 };/*
10343  * Based on:
10344  * Ext JS Library 1.1.1
10345  * Copyright(c) 2006-2007, Ext JS, LLC.
10346  *
10347  * Originally Released Under LGPL - original licence link has changed is not relivant.
10348  *
10349  * Fork - LGPL
10350  * <script type="text/javascript">
10351  */
10352
10353 /**
10354 * @class Roo.data.Record
10355  * Instances of this class encapsulate both record <em>definition</em> information, and record
10356  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10357  * to access Records cached in an {@link Roo.data.Store} object.<br>
10358  * <p>
10359  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10360  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10361  * objects.<br>
10362  * <p>
10363  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10364  * @constructor
10365  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10366  * {@link #create}. The parameters are the same.
10367  * @param {Array} data An associative Array of data values keyed by the field name.
10368  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10369  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10370  * not specified an integer id is generated.
10371  */
10372 Roo.data.Record = function(data, id){
10373     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10374     this.data = data;
10375 };
10376
10377 /**
10378  * Generate a constructor for a specific record layout.
10379  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10380  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10381  * Each field definition object may contain the following properties: <ul>
10382  * <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,
10383  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10384  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10385  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10386  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10387  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10388  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10389  * this may be omitted.</p></li>
10390  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10391  * <ul><li>auto (Default, implies no conversion)</li>
10392  * <li>string</li>
10393  * <li>int</li>
10394  * <li>float</li>
10395  * <li>boolean</li>
10396  * <li>date</li></ul></p></li>
10397  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10398  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10399  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10400  * by the Reader into an object that will be stored in the Record. It is passed the
10401  * following parameters:<ul>
10402  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10403  * </ul></p></li>
10404  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10405  * </ul>
10406  * <br>usage:<br><pre><code>
10407 var TopicRecord = Roo.data.Record.create(
10408     {name: 'title', mapping: 'topic_title'},
10409     {name: 'author', mapping: 'username'},
10410     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10411     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10412     {name: 'lastPoster', mapping: 'user2'},
10413     {name: 'excerpt', mapping: 'post_text'}
10414 );
10415
10416 var myNewRecord = new TopicRecord({
10417     title: 'Do my job please',
10418     author: 'noobie',
10419     totalPosts: 1,
10420     lastPost: new Date(),
10421     lastPoster: 'Animal',
10422     excerpt: 'No way dude!'
10423 });
10424 myStore.add(myNewRecord);
10425 </code></pre>
10426  * @method create
10427  * @static
10428  */
10429 Roo.data.Record.create = function(o){
10430     var f = function(){
10431         f.superclass.constructor.apply(this, arguments);
10432     };
10433     Roo.extend(f, Roo.data.Record);
10434     var p = f.prototype;
10435     p.fields = new Roo.util.MixedCollection(false, function(field){
10436         return field.name;
10437     });
10438     for(var i = 0, len = o.length; i < len; i++){
10439         p.fields.add(new Roo.data.Field(o[i]));
10440     }
10441     f.getField = function(name){
10442         return p.fields.get(name);  
10443     };
10444     return f;
10445 };
10446
10447 Roo.data.Record.AUTO_ID = 1000;
10448 Roo.data.Record.EDIT = 'edit';
10449 Roo.data.Record.REJECT = 'reject';
10450 Roo.data.Record.COMMIT = 'commit';
10451
10452 Roo.data.Record.prototype = {
10453     /**
10454      * Readonly flag - true if this record has been modified.
10455      * @type Boolean
10456      */
10457     dirty : false,
10458     editing : false,
10459     error: null,
10460     modified: null,
10461
10462     // private
10463     join : function(store){
10464         this.store = store;
10465     },
10466
10467     /**
10468      * Set the named field to the specified value.
10469      * @param {String} name The name of the field to set.
10470      * @param {Object} value The value to set the field to.
10471      */
10472     set : function(name, value){
10473         if(this.data[name] == value){
10474             return;
10475         }
10476         this.dirty = true;
10477         if(!this.modified){
10478             this.modified = {};
10479         }
10480         if(typeof this.modified[name] == 'undefined'){
10481             this.modified[name] = this.data[name];
10482         }
10483         this.data[name] = value;
10484         if(!this.editing && this.store){
10485             this.store.afterEdit(this);
10486         }       
10487     },
10488
10489     /**
10490      * Get the value of the named field.
10491      * @param {String} name The name of the field to get the value of.
10492      * @return {Object} The value of the field.
10493      */
10494     get : function(name){
10495         return this.data[name]; 
10496     },
10497
10498     // private
10499     beginEdit : function(){
10500         this.editing = true;
10501         this.modified = {}; 
10502     },
10503
10504     // private
10505     cancelEdit : function(){
10506         this.editing = false;
10507         delete this.modified;
10508     },
10509
10510     // private
10511     endEdit : function(){
10512         this.editing = false;
10513         if(this.dirty && this.store){
10514             this.store.afterEdit(this);
10515         }
10516     },
10517
10518     /**
10519      * Usually called by the {@link Roo.data.Store} which owns the Record.
10520      * Rejects all changes made to the Record since either creation, or the last commit operation.
10521      * Modified fields are reverted to their original values.
10522      * <p>
10523      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10524      * of reject operations.
10525      */
10526     reject : function(){
10527         var m = this.modified;
10528         for(var n in m){
10529             if(typeof m[n] != "function"){
10530                 this.data[n] = m[n];
10531             }
10532         }
10533         this.dirty = false;
10534         delete this.modified;
10535         this.editing = false;
10536         if(this.store){
10537             this.store.afterReject(this);
10538         }
10539     },
10540
10541     /**
10542      * Usually called by the {@link Roo.data.Store} which owns the Record.
10543      * Commits all changes made to the Record since either creation, or the last commit operation.
10544      * <p>
10545      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10546      * of commit operations.
10547      */
10548     commit : function(){
10549         this.dirty = false;
10550         delete this.modified;
10551         this.editing = false;
10552         if(this.store){
10553             this.store.afterCommit(this);
10554         }
10555     },
10556
10557     // private
10558     hasError : function(){
10559         return this.error != null;
10560     },
10561
10562     // private
10563     clearError : function(){
10564         this.error = null;
10565     },
10566
10567     /**
10568      * Creates a copy of this record.
10569      * @param {String} id (optional) A new record id if you don't want to use this record's id
10570      * @return {Record}
10571      */
10572     copy : function(newId) {
10573         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10574     }
10575 };/*
10576  * Based on:
10577  * Ext JS Library 1.1.1
10578  * Copyright(c) 2006-2007, Ext JS, LLC.
10579  *
10580  * Originally Released Under LGPL - original licence link has changed is not relivant.
10581  *
10582  * Fork - LGPL
10583  * <script type="text/javascript">
10584  */
10585
10586
10587
10588 /**
10589  * @class Roo.data.Store
10590  * @extends Roo.util.Observable
10591  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10592  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10593  * <p>
10594  * 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
10595  * has no knowledge of the format of the data returned by the Proxy.<br>
10596  * <p>
10597  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10598  * instances from the data object. These records are cached and made available through accessor functions.
10599  * @constructor
10600  * Creates a new Store.
10601  * @param {Object} config A config object containing the objects needed for the Store to access data,
10602  * and read the data into Records.
10603  */
10604 Roo.data.Store = function(config){
10605     this.data = new Roo.util.MixedCollection(false);
10606     this.data.getKey = function(o){
10607         return o.id;
10608     };
10609     this.baseParams = {};
10610     // private
10611     this.paramNames = {
10612         "start" : "start",
10613         "limit" : "limit",
10614         "sort" : "sort",
10615         "dir" : "dir",
10616         "multisort" : "_multisort"
10617     };
10618
10619     if(config && config.data){
10620         this.inlineData = config.data;
10621         delete config.data;
10622     }
10623
10624     Roo.apply(this, config);
10625     
10626     if(this.reader){ // reader passed
10627         this.reader = Roo.factory(this.reader, Roo.data);
10628         this.reader.xmodule = this.xmodule || false;
10629         if(!this.recordType){
10630             this.recordType = this.reader.recordType;
10631         }
10632         if(this.reader.onMetaChange){
10633             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10634         }
10635     }
10636
10637     if(this.recordType){
10638         this.fields = this.recordType.prototype.fields;
10639     }
10640     this.modified = [];
10641
10642     this.addEvents({
10643         /**
10644          * @event datachanged
10645          * Fires when the data cache has changed, and a widget which is using this Store
10646          * as a Record cache should refresh its view.
10647          * @param {Store} this
10648          */
10649         datachanged : true,
10650         /**
10651          * @event metachange
10652          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10653          * @param {Store} this
10654          * @param {Object} meta The JSON metadata
10655          */
10656         metachange : true,
10657         /**
10658          * @event add
10659          * Fires when Records have been added to the Store
10660          * @param {Store} this
10661          * @param {Roo.data.Record[]} records The array of Records added
10662          * @param {Number} index The index at which the record(s) were added
10663          */
10664         add : true,
10665         /**
10666          * @event remove
10667          * Fires when a Record has been removed from the Store
10668          * @param {Store} this
10669          * @param {Roo.data.Record} record The Record that was removed
10670          * @param {Number} index The index at which the record was removed
10671          */
10672         remove : true,
10673         /**
10674          * @event update
10675          * Fires when a Record has been updated
10676          * @param {Store} this
10677          * @param {Roo.data.Record} record The Record that was updated
10678          * @param {String} operation The update operation being performed.  Value may be one of:
10679          * <pre><code>
10680  Roo.data.Record.EDIT
10681  Roo.data.Record.REJECT
10682  Roo.data.Record.COMMIT
10683          * </code></pre>
10684          */
10685         update : true,
10686         /**
10687          * @event clear
10688          * Fires when the data cache has been cleared.
10689          * @param {Store} this
10690          */
10691         clear : true,
10692         /**
10693          * @event beforeload
10694          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10695          * the load action will be canceled.
10696          * @param {Store} this
10697          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10698          */
10699         beforeload : true,
10700         /**
10701          * @event beforeloadadd
10702          * Fires after a new set of Records has been loaded.
10703          * @param {Store} this
10704          * @param {Roo.data.Record[]} records The Records that were loaded
10705          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10706          */
10707         beforeloadadd : true,
10708         /**
10709          * @event load
10710          * Fires after a new set of Records has been loaded, before they are added to the store.
10711          * @param {Store} this
10712          * @param {Roo.data.Record[]} records The Records that were loaded
10713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10714          * @params {Object} return from reader
10715          */
10716         load : true,
10717         /**
10718          * @event loadexception
10719          * Fires if an exception occurs in the Proxy during loading.
10720          * Called with the signature of the Proxy's "loadexception" event.
10721          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10722          * 
10723          * @param {Proxy} 
10724          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10725          * @param {Object} load options 
10726          * @param {Object} jsonData from your request (normally this contains the Exception)
10727          */
10728         loadexception : true
10729     });
10730     
10731     if(this.proxy){
10732         this.proxy = Roo.factory(this.proxy, Roo.data);
10733         this.proxy.xmodule = this.xmodule || false;
10734         this.relayEvents(this.proxy,  ["loadexception"]);
10735     }
10736     this.sortToggle = {};
10737     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10738
10739     Roo.data.Store.superclass.constructor.call(this);
10740
10741     if(this.inlineData){
10742         this.loadData(this.inlineData);
10743         delete this.inlineData;
10744     }
10745 };
10746
10747 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10748      /**
10749     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10750     * without a remote query - used by combo/forms at present.
10751     */
10752     
10753     /**
10754     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10755     */
10756     /**
10757     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10758     */
10759     /**
10760     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10761     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10762     */
10763     /**
10764     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10765     * on any HTTP request
10766     */
10767     /**
10768     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10769     */
10770     /**
10771     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10772     */
10773     multiSort: false,
10774     /**
10775     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10776     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10777     */
10778     remoteSort : false,
10779
10780     /**
10781     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10782      * loaded or when a record is removed. (defaults to false).
10783     */
10784     pruneModifiedRecords : false,
10785
10786     // private
10787     lastOptions : null,
10788
10789     /**
10790      * Add Records to the Store and fires the add event.
10791      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10792      */
10793     add : function(records){
10794         records = [].concat(records);
10795         for(var i = 0, len = records.length; i < len; i++){
10796             records[i].join(this);
10797         }
10798         var index = this.data.length;
10799         this.data.addAll(records);
10800         this.fireEvent("add", this, records, index);
10801     },
10802
10803     /**
10804      * Remove a Record from the Store and fires the remove event.
10805      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10806      */
10807     remove : function(record){
10808         var index = this.data.indexOf(record);
10809         this.data.removeAt(index);
10810         if(this.pruneModifiedRecords){
10811             this.modified.remove(record);
10812         }
10813         this.fireEvent("remove", this, record, index);
10814     },
10815
10816     /**
10817      * Remove all Records from the Store and fires the clear event.
10818      */
10819     removeAll : function(){
10820         this.data.clear();
10821         if(this.pruneModifiedRecords){
10822             this.modified = [];
10823         }
10824         this.fireEvent("clear", this);
10825     },
10826
10827     /**
10828      * Inserts Records to the Store at the given index and fires the add event.
10829      * @param {Number} index The start index at which to insert the passed Records.
10830      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10831      */
10832     insert : function(index, records){
10833         records = [].concat(records);
10834         for(var i = 0, len = records.length; i < len; i++){
10835             this.data.insert(index, records[i]);
10836             records[i].join(this);
10837         }
10838         this.fireEvent("add", this, records, index);
10839     },
10840
10841     /**
10842      * Get the index within the cache of the passed Record.
10843      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10844      * @return {Number} The index of the passed Record. Returns -1 if not found.
10845      */
10846     indexOf : function(record){
10847         return this.data.indexOf(record);
10848     },
10849
10850     /**
10851      * Get the index within the cache of the Record with the passed id.
10852      * @param {String} id The id of the Record to find.
10853      * @return {Number} The index of the Record. Returns -1 if not found.
10854      */
10855     indexOfId : function(id){
10856         return this.data.indexOfKey(id);
10857     },
10858
10859     /**
10860      * Get the Record with the specified id.
10861      * @param {String} id The id of the Record to find.
10862      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10863      */
10864     getById : function(id){
10865         return this.data.key(id);
10866     },
10867
10868     /**
10869      * Get the Record at the specified index.
10870      * @param {Number} index The index of the Record to find.
10871      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10872      */
10873     getAt : function(index){
10874         return this.data.itemAt(index);
10875     },
10876
10877     /**
10878      * Returns a range of Records between specified indices.
10879      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10880      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10881      * @return {Roo.data.Record[]} An array of Records
10882      */
10883     getRange : function(start, end){
10884         return this.data.getRange(start, end);
10885     },
10886
10887     // private
10888     storeOptions : function(o){
10889         o = Roo.apply({}, o);
10890         delete o.callback;
10891         delete o.scope;
10892         this.lastOptions = o;
10893     },
10894
10895     /**
10896      * Loads the Record cache from the configured Proxy using the configured Reader.
10897      * <p>
10898      * If using remote paging, then the first load call must specify the <em>start</em>
10899      * and <em>limit</em> properties in the options.params property to establish the initial
10900      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10901      * <p>
10902      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10903      * and this call will return before the new data has been loaded. Perform any post-processing
10904      * in a callback function, or in a "load" event handler.</strong>
10905      * <p>
10906      * @param {Object} options An object containing properties which control loading options:<ul>
10907      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10908      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10909      * passed the following arguments:<ul>
10910      * <li>r : Roo.data.Record[]</li>
10911      * <li>options: Options object from the load call</li>
10912      * <li>success: Boolean success indicator</li></ul></li>
10913      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10914      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10915      * </ul>
10916      */
10917     load : function(options){
10918         options = options || {};
10919         if(this.fireEvent("beforeload", this, options) !== false){
10920             this.storeOptions(options);
10921             var p = Roo.apply(options.params || {}, this.baseParams);
10922             // if meta was not loaded from remote source.. try requesting it.
10923             if (!this.reader.metaFromRemote) {
10924                 p._requestMeta = 1;
10925             }
10926             if(this.sortInfo && this.remoteSort){
10927                 var pn = this.paramNames;
10928                 p[pn["sort"]] = this.sortInfo.field;
10929                 p[pn["dir"]] = this.sortInfo.direction;
10930             }
10931             if (this.multiSort) {
10932                 var pn = this.paramNames;
10933                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10934             }
10935             
10936             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10937         }
10938     },
10939
10940     /**
10941      * Reloads the Record cache from the configured Proxy using the configured Reader and
10942      * the options from the last load operation performed.
10943      * @param {Object} options (optional) An object containing properties which may override the options
10944      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10945      * the most recently used options are reused).
10946      */
10947     reload : function(options){
10948         this.load(Roo.applyIf(options||{}, this.lastOptions));
10949     },
10950
10951     // private
10952     // Called as a callback by the Reader during a load operation.
10953     loadRecords : function(o, options, success){
10954         if(!o || success === false){
10955             if(success !== false){
10956                 this.fireEvent("load", this, [], options, o);
10957             }
10958             if(options.callback){
10959                 options.callback.call(options.scope || this, [], options, false);
10960             }
10961             return;
10962         }
10963         // if data returned failure - throw an exception.
10964         if (o.success === false) {
10965             // show a message if no listener is registered.
10966             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10967                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10968             }
10969             // loadmask wil be hooked into this..
10970             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10971             return;
10972         }
10973         var r = o.records, t = o.totalRecords || r.length;
10974         
10975         this.fireEvent("beforeloadadd", this, r, options, o);
10976         
10977         if(!options || options.add !== true){
10978             if(this.pruneModifiedRecords){
10979                 this.modified = [];
10980             }
10981             for(var i = 0, len = r.length; i < len; i++){
10982                 r[i].join(this);
10983             }
10984             if(this.snapshot){
10985                 this.data = this.snapshot;
10986                 delete this.snapshot;
10987             }
10988             this.data.clear();
10989             this.data.addAll(r);
10990             this.totalLength = t;
10991             this.applySort();
10992             this.fireEvent("datachanged", this);
10993         }else{
10994             this.totalLength = Math.max(t, this.data.length+r.length);
10995             this.add(r);
10996         }
10997         
10998         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
10999                 
11000             var e = new Roo.data.Record({});
11001
11002             e.set(this.parent.displayField, this.parent.emptyTitle);
11003             e.set(this.parent.valueField, '');
11004
11005             this.insert(0, e);
11006         }
11007             
11008         this.fireEvent("load", this, r, options, o);
11009         if(options.callback){
11010             options.callback.call(options.scope || this, r, options, true);
11011         }
11012     },
11013
11014
11015     /**
11016      * Loads data from a passed data block. A Reader which understands the format of the data
11017      * must have been configured in the constructor.
11018      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11019      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11020      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11021      */
11022     loadData : function(o, append){
11023         var r = this.reader.readRecords(o);
11024         this.loadRecords(r, {add: append}, true);
11025     },
11026
11027     /**
11028      * Gets the number of cached records.
11029      * <p>
11030      * <em>If using paging, this may not be the total size of the dataset. If the data object
11031      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11032      * the data set size</em>
11033      */
11034     getCount : function(){
11035         return this.data.length || 0;
11036     },
11037
11038     /**
11039      * Gets the total number of records in the dataset as returned by the server.
11040      * <p>
11041      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11042      * the dataset size</em>
11043      */
11044     getTotalCount : function(){
11045         return this.totalLength || 0;
11046     },
11047
11048     /**
11049      * Returns the sort state of the Store as an object with two properties:
11050      * <pre><code>
11051  field {String} The name of the field by which the Records are sorted
11052  direction {String} The sort order, "ASC" or "DESC"
11053      * </code></pre>
11054      */
11055     getSortState : function(){
11056         return this.sortInfo;
11057     },
11058
11059     // private
11060     applySort : function(){
11061         if(this.sortInfo && !this.remoteSort){
11062             var s = this.sortInfo, f = s.field;
11063             var st = this.fields.get(f).sortType;
11064             var fn = function(r1, r2){
11065                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11066                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11067             };
11068             this.data.sort(s.direction, fn);
11069             if(this.snapshot && this.snapshot != this.data){
11070                 this.snapshot.sort(s.direction, fn);
11071             }
11072         }
11073     },
11074
11075     /**
11076      * Sets the default sort column and order to be used by the next load operation.
11077      * @param {String} fieldName The name of the field to sort by.
11078      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11079      */
11080     setDefaultSort : function(field, dir){
11081         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11082     },
11083
11084     /**
11085      * Sort the Records.
11086      * If remote sorting is used, the sort is performed on the server, and the cache is
11087      * reloaded. If local sorting is used, the cache is sorted internally.
11088      * @param {String} fieldName The name of the field to sort by.
11089      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11090      */
11091     sort : function(fieldName, dir){
11092         var f = this.fields.get(fieldName);
11093         if(!dir){
11094             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11095             
11096             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11097                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11098             }else{
11099                 dir = f.sortDir;
11100             }
11101         }
11102         this.sortToggle[f.name] = dir;
11103         this.sortInfo = {field: f.name, direction: dir};
11104         if(!this.remoteSort){
11105             this.applySort();
11106             this.fireEvent("datachanged", this);
11107         }else{
11108             this.load(this.lastOptions);
11109         }
11110     },
11111
11112     /**
11113      * Calls the specified function for each of the Records in the cache.
11114      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11115      * Returning <em>false</em> aborts and exits the iteration.
11116      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11117      */
11118     each : function(fn, scope){
11119         this.data.each(fn, scope);
11120     },
11121
11122     /**
11123      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11124      * (e.g., during paging).
11125      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11126      */
11127     getModifiedRecords : function(){
11128         return this.modified;
11129     },
11130
11131     // private
11132     createFilterFn : function(property, value, anyMatch){
11133         if(!value.exec){ // not a regex
11134             value = String(value);
11135             if(value.length == 0){
11136                 return false;
11137             }
11138             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11139         }
11140         return function(r){
11141             return value.test(r.data[property]);
11142         };
11143     },
11144
11145     /**
11146      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11147      * @param {String} property A field on your records
11148      * @param {Number} start The record index to start at (defaults to 0)
11149      * @param {Number} end The last record index to include (defaults to length - 1)
11150      * @return {Number} The sum
11151      */
11152     sum : function(property, start, end){
11153         var rs = this.data.items, v = 0;
11154         start = start || 0;
11155         end = (end || end === 0) ? end : rs.length-1;
11156
11157         for(var i = start; i <= end; i++){
11158             v += (rs[i].data[property] || 0);
11159         }
11160         return v;
11161     },
11162
11163     /**
11164      * Filter the records by a specified property.
11165      * @param {String} field A field on your records
11166      * @param {String/RegExp} value Either a string that the field
11167      * should start with or a RegExp to test against the field
11168      * @param {Boolean} anyMatch True to match any part not just the beginning
11169      */
11170     filter : function(property, value, anyMatch){
11171         var fn = this.createFilterFn(property, value, anyMatch);
11172         return fn ? this.filterBy(fn) : this.clearFilter();
11173     },
11174
11175     /**
11176      * Filter by a function. The specified function will be called with each
11177      * record in this data source. If the function returns true the record is included,
11178      * otherwise it is filtered.
11179      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11180      * @param {Object} scope (optional) The scope of the function (defaults to this)
11181      */
11182     filterBy : function(fn, scope){
11183         this.snapshot = this.snapshot || this.data;
11184         this.data = this.queryBy(fn, scope||this);
11185         this.fireEvent("datachanged", this);
11186     },
11187
11188     /**
11189      * Query the records by a specified property.
11190      * @param {String} field A field on your records
11191      * @param {String/RegExp} value Either a string that the field
11192      * should start with or a RegExp to test against the field
11193      * @param {Boolean} anyMatch True to match any part not just the beginning
11194      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11195      */
11196     query : function(property, value, anyMatch){
11197         var fn = this.createFilterFn(property, value, anyMatch);
11198         return fn ? this.queryBy(fn) : this.data.clone();
11199     },
11200
11201     /**
11202      * Query by a function. The specified function will be called with each
11203      * record in this data source. If the function returns true the record is included
11204      * in the results.
11205      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11206      * @param {Object} scope (optional) The scope of the function (defaults to this)
11207       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11208      **/
11209     queryBy : function(fn, scope){
11210         var data = this.snapshot || this.data;
11211         return data.filterBy(fn, scope||this);
11212     },
11213
11214     /**
11215      * Collects unique values for a particular dataIndex from this store.
11216      * @param {String} dataIndex The property to collect
11217      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11218      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11219      * @return {Array} An array of the unique values
11220      **/
11221     collect : function(dataIndex, allowNull, bypassFilter){
11222         var d = (bypassFilter === true && this.snapshot) ?
11223                 this.snapshot.items : this.data.items;
11224         var v, sv, r = [], l = {};
11225         for(var i = 0, len = d.length; i < len; i++){
11226             v = d[i].data[dataIndex];
11227             sv = String(v);
11228             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11229                 l[sv] = true;
11230                 r[r.length] = v;
11231             }
11232         }
11233         return r;
11234     },
11235
11236     /**
11237      * Revert to a view of the Record cache with no filtering applied.
11238      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11239      */
11240     clearFilter : function(suppressEvent){
11241         if(this.snapshot && this.snapshot != this.data){
11242             this.data = this.snapshot;
11243             delete this.snapshot;
11244             if(suppressEvent !== true){
11245                 this.fireEvent("datachanged", this);
11246             }
11247         }
11248     },
11249
11250     // private
11251     afterEdit : function(record){
11252         if(this.modified.indexOf(record) == -1){
11253             this.modified.push(record);
11254         }
11255         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11256     },
11257     
11258     // private
11259     afterReject : function(record){
11260         this.modified.remove(record);
11261         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11262     },
11263
11264     // private
11265     afterCommit : function(record){
11266         this.modified.remove(record);
11267         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11268     },
11269
11270     /**
11271      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11272      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11273      */
11274     commitChanges : function(){
11275         var m = this.modified.slice(0);
11276         this.modified = [];
11277         for(var i = 0, len = m.length; i < len; i++){
11278             m[i].commit();
11279         }
11280     },
11281
11282     /**
11283      * Cancel outstanding changes on all changed records.
11284      */
11285     rejectChanges : function(){
11286         var m = this.modified.slice(0);
11287         this.modified = [];
11288         for(var i = 0, len = m.length; i < len; i++){
11289             m[i].reject();
11290         }
11291     },
11292
11293     onMetaChange : function(meta, rtype, o){
11294         this.recordType = rtype;
11295         this.fields = rtype.prototype.fields;
11296         delete this.snapshot;
11297         this.sortInfo = meta.sortInfo || this.sortInfo;
11298         this.modified = [];
11299         this.fireEvent('metachange', this, this.reader.meta);
11300     },
11301     
11302     moveIndex : function(data, type)
11303     {
11304         var index = this.indexOf(data);
11305         
11306         var newIndex = index + type;
11307         
11308         this.remove(data);
11309         
11310         this.insert(newIndex, data);
11311         
11312     }
11313 });/*
11314  * Based on:
11315  * Ext JS Library 1.1.1
11316  * Copyright(c) 2006-2007, Ext JS, LLC.
11317  *
11318  * Originally Released Under LGPL - original licence link has changed is not relivant.
11319  *
11320  * Fork - LGPL
11321  * <script type="text/javascript">
11322  */
11323
11324 /**
11325  * @class Roo.data.SimpleStore
11326  * @extends Roo.data.Store
11327  * Small helper class to make creating Stores from Array data easier.
11328  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11329  * @cfg {Array} fields An array of field definition objects, or field name strings.
11330  * @cfg {Array} data The multi-dimensional array of data
11331  * @constructor
11332  * @param {Object} config
11333  */
11334 Roo.data.SimpleStore = function(config){
11335     Roo.data.SimpleStore.superclass.constructor.call(this, {
11336         isLocal : true,
11337         reader: new Roo.data.ArrayReader({
11338                 id: config.id
11339             },
11340             Roo.data.Record.create(config.fields)
11341         ),
11342         proxy : new Roo.data.MemoryProxy(config.data)
11343     });
11344     this.load();
11345 };
11346 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11347  * Based on:
11348  * Ext JS Library 1.1.1
11349  * Copyright(c) 2006-2007, Ext JS, LLC.
11350  *
11351  * Originally Released Under LGPL - original licence link has changed is not relivant.
11352  *
11353  * Fork - LGPL
11354  * <script type="text/javascript">
11355  */
11356
11357 /**
11358 /**
11359  * @extends Roo.data.Store
11360  * @class Roo.data.JsonStore
11361  * Small helper class to make creating Stores for JSON data easier. <br/>
11362 <pre><code>
11363 var store = new Roo.data.JsonStore({
11364     url: 'get-images.php',
11365     root: 'images',
11366     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11367 });
11368 </code></pre>
11369  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11370  * JsonReader and HttpProxy (unless inline data is provided).</b>
11371  * @cfg {Array} fields An array of field definition objects, or field name strings.
11372  * @constructor
11373  * @param {Object} config
11374  */
11375 Roo.data.JsonStore = function(c){
11376     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11377         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11378         reader: new Roo.data.JsonReader(c, c.fields)
11379     }));
11380 };
11381 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11382  * Based on:
11383  * Ext JS Library 1.1.1
11384  * Copyright(c) 2006-2007, Ext JS, LLC.
11385  *
11386  * Originally Released Under LGPL - original licence link has changed is not relivant.
11387  *
11388  * Fork - LGPL
11389  * <script type="text/javascript">
11390  */
11391
11392  
11393 Roo.data.Field = function(config){
11394     if(typeof config == "string"){
11395         config = {name: config};
11396     }
11397     Roo.apply(this, config);
11398     
11399     if(!this.type){
11400         this.type = "auto";
11401     }
11402     
11403     var st = Roo.data.SortTypes;
11404     // named sortTypes are supported, here we look them up
11405     if(typeof this.sortType == "string"){
11406         this.sortType = st[this.sortType];
11407     }
11408     
11409     // set default sortType for strings and dates
11410     if(!this.sortType){
11411         switch(this.type){
11412             case "string":
11413                 this.sortType = st.asUCString;
11414                 break;
11415             case "date":
11416                 this.sortType = st.asDate;
11417                 break;
11418             default:
11419                 this.sortType = st.none;
11420         }
11421     }
11422
11423     // define once
11424     var stripRe = /[\$,%]/g;
11425
11426     // prebuilt conversion function for this field, instead of
11427     // switching every time we're reading a value
11428     if(!this.convert){
11429         var cv, dateFormat = this.dateFormat;
11430         switch(this.type){
11431             case "":
11432             case "auto":
11433             case undefined:
11434                 cv = function(v){ return v; };
11435                 break;
11436             case "string":
11437                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11438                 break;
11439             case "int":
11440                 cv = function(v){
11441                     return v !== undefined && v !== null && v !== '' ?
11442                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11443                     };
11444                 break;
11445             case "float":
11446                 cv = function(v){
11447                     return v !== undefined && v !== null && v !== '' ?
11448                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11449                     };
11450                 break;
11451             case "bool":
11452             case "boolean":
11453                 cv = function(v){ return v === true || v === "true" || v == 1; };
11454                 break;
11455             case "date":
11456                 cv = function(v){
11457                     if(!v){
11458                         return '';
11459                     }
11460                     if(v instanceof Date){
11461                         return v;
11462                     }
11463                     if(dateFormat){
11464                         if(dateFormat == "timestamp"){
11465                             return new Date(v*1000);
11466                         }
11467                         return Date.parseDate(v, dateFormat);
11468                     }
11469                     var parsed = Date.parse(v);
11470                     return parsed ? new Date(parsed) : null;
11471                 };
11472              break;
11473             
11474         }
11475         this.convert = cv;
11476     }
11477 };
11478
11479 Roo.data.Field.prototype = {
11480     dateFormat: null,
11481     defaultValue: "",
11482     mapping: null,
11483     sortType : null,
11484     sortDir : "ASC"
11485 };/*
11486  * Based on:
11487  * Ext JS Library 1.1.1
11488  * Copyright(c) 2006-2007, Ext JS, LLC.
11489  *
11490  * Originally Released Under LGPL - original licence link has changed is not relivant.
11491  *
11492  * Fork - LGPL
11493  * <script type="text/javascript">
11494  */
11495  
11496 // Base class for reading structured data from a data source.  This class is intended to be
11497 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11498
11499 /**
11500  * @class Roo.data.DataReader
11501  * Base class for reading structured data from a data source.  This class is intended to be
11502  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11503  */
11504
11505 Roo.data.DataReader = function(meta, recordType){
11506     
11507     this.meta = meta;
11508     
11509     this.recordType = recordType instanceof Array ? 
11510         Roo.data.Record.create(recordType) : recordType;
11511 };
11512
11513 Roo.data.DataReader.prototype = {
11514      /**
11515      * Create an empty record
11516      * @param {Object} data (optional) - overlay some values
11517      * @return {Roo.data.Record} record created.
11518      */
11519     newRow :  function(d) {
11520         var da =  {};
11521         this.recordType.prototype.fields.each(function(c) {
11522             switch( c.type) {
11523                 case 'int' : da[c.name] = 0; break;
11524                 case 'date' : da[c.name] = new Date(); break;
11525                 case 'float' : da[c.name] = 0.0; break;
11526                 case 'boolean' : da[c.name] = false; break;
11527                 default : da[c.name] = ""; break;
11528             }
11529             
11530         });
11531         return new this.recordType(Roo.apply(da, d));
11532     }
11533     
11534 };/*
11535  * Based on:
11536  * Ext JS Library 1.1.1
11537  * Copyright(c) 2006-2007, Ext JS, LLC.
11538  *
11539  * Originally Released Under LGPL - original licence link has changed is not relivant.
11540  *
11541  * Fork - LGPL
11542  * <script type="text/javascript">
11543  */
11544
11545 /**
11546  * @class Roo.data.DataProxy
11547  * @extends Roo.data.Observable
11548  * This class is an abstract base class for implementations which provide retrieval of
11549  * unformatted data objects.<br>
11550  * <p>
11551  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11552  * (of the appropriate type which knows how to parse the data object) to provide a block of
11553  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11554  * <p>
11555  * Custom implementations must implement the load method as described in
11556  * {@link Roo.data.HttpProxy#load}.
11557  */
11558 Roo.data.DataProxy = function(){
11559     this.addEvents({
11560         /**
11561          * @event beforeload
11562          * Fires before a network request is made to retrieve a data object.
11563          * @param {Object} This DataProxy object.
11564          * @param {Object} params The params parameter to the load function.
11565          */
11566         beforeload : true,
11567         /**
11568          * @event load
11569          * Fires before the load method's callback is called.
11570          * @param {Object} This DataProxy object.
11571          * @param {Object} o The data object.
11572          * @param {Object} arg The callback argument object passed to the load function.
11573          */
11574         load : true,
11575         /**
11576          * @event loadexception
11577          * Fires if an Exception occurs during data retrieval.
11578          * @param {Object} This DataProxy object.
11579          * @param {Object} o The data object.
11580          * @param {Object} arg The callback argument object passed to the load function.
11581          * @param {Object} e The Exception.
11582          */
11583         loadexception : true
11584     });
11585     Roo.data.DataProxy.superclass.constructor.call(this);
11586 };
11587
11588 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11589
11590     /**
11591      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11592      */
11593 /*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603 /**
11604  * @class Roo.data.MemoryProxy
11605  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11606  * to the Reader when its load method is called.
11607  * @constructor
11608  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11609  */
11610 Roo.data.MemoryProxy = function(data){
11611     if (data.data) {
11612         data = data.data;
11613     }
11614     Roo.data.MemoryProxy.superclass.constructor.call(this);
11615     this.data = data;
11616 };
11617
11618 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11619     
11620     /**
11621      * Load data from the requested source (in this case an in-memory
11622      * data object passed to the constructor), read the data object into
11623      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11624      * process that block using the passed callback.
11625      * @param {Object} params This parameter is not used by the MemoryProxy class.
11626      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11627      * object into a block of Roo.data.Records.
11628      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11629      * The function must be passed <ul>
11630      * <li>The Record block object</li>
11631      * <li>The "arg" argument from the load function</li>
11632      * <li>A boolean success indicator</li>
11633      * </ul>
11634      * @param {Object} scope The scope in which to call the callback
11635      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11636      */
11637     load : function(params, reader, callback, scope, arg){
11638         params = params || {};
11639         var result;
11640         try {
11641             result = reader.readRecords(this.data);
11642         }catch(e){
11643             this.fireEvent("loadexception", this, arg, null, e);
11644             callback.call(scope, null, arg, false);
11645             return;
11646         }
11647         callback.call(scope, result, arg, true);
11648     },
11649     
11650     // private
11651     update : function(params, records){
11652         
11653     }
11654 });/*
11655  * Based on:
11656  * Ext JS Library 1.1.1
11657  * Copyright(c) 2006-2007, Ext JS, LLC.
11658  *
11659  * Originally Released Under LGPL - original licence link has changed is not relivant.
11660  *
11661  * Fork - LGPL
11662  * <script type="text/javascript">
11663  */
11664 /**
11665  * @class Roo.data.HttpProxy
11666  * @extends Roo.data.DataProxy
11667  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11668  * configured to reference a certain URL.<br><br>
11669  * <p>
11670  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11671  * from which the running page was served.<br><br>
11672  * <p>
11673  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11674  * <p>
11675  * Be aware that to enable the browser to parse an XML document, the server must set
11676  * the Content-Type header in the HTTP response to "text/xml".
11677  * @constructor
11678  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11679  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11680  * will be used to make the request.
11681  */
11682 Roo.data.HttpProxy = function(conn){
11683     Roo.data.HttpProxy.superclass.constructor.call(this);
11684     // is conn a conn config or a real conn?
11685     this.conn = conn;
11686     this.useAjax = !conn || !conn.events;
11687   
11688 };
11689
11690 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11691     // thse are take from connection...
11692     
11693     /**
11694      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11695      */
11696     /**
11697      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11698      * extra parameters to each request made by this object. (defaults to undefined)
11699      */
11700     /**
11701      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11702      *  to each request made by this object. (defaults to undefined)
11703      */
11704     /**
11705      * @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)
11706      */
11707     /**
11708      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11709      */
11710      /**
11711      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11712      * @type Boolean
11713      */
11714   
11715
11716     /**
11717      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11718      * @type Boolean
11719      */
11720     /**
11721      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11722      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11723      * a finer-grained basis than the DataProxy events.
11724      */
11725     getConnection : function(){
11726         return this.useAjax ? Roo.Ajax : this.conn;
11727     },
11728
11729     /**
11730      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11731      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11732      * process that block using the passed callback.
11733      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11734      * for the request to the remote server.
11735      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11736      * object into a block of Roo.data.Records.
11737      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11738      * The function must be passed <ul>
11739      * <li>The Record block object</li>
11740      * <li>The "arg" argument from the load function</li>
11741      * <li>A boolean success indicator</li>
11742      * </ul>
11743      * @param {Object} scope The scope in which to call the callback
11744      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11745      */
11746     load : function(params, reader, callback, scope, arg){
11747         if(this.fireEvent("beforeload", this, params) !== false){
11748             var  o = {
11749                 params : params || {},
11750                 request: {
11751                     callback : callback,
11752                     scope : scope,
11753                     arg : arg
11754                 },
11755                 reader: reader,
11756                 callback : this.loadResponse,
11757                 scope: this
11758             };
11759             if(this.useAjax){
11760                 Roo.applyIf(o, this.conn);
11761                 if(this.activeRequest){
11762                     Roo.Ajax.abort(this.activeRequest);
11763                 }
11764                 this.activeRequest = Roo.Ajax.request(o);
11765             }else{
11766                 this.conn.request(o);
11767             }
11768         }else{
11769             callback.call(scope||this, null, arg, false);
11770         }
11771     },
11772
11773     // private
11774     loadResponse : function(o, success, response){
11775         delete this.activeRequest;
11776         if(!success){
11777             this.fireEvent("loadexception", this, o, response);
11778             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11779             return;
11780         }
11781         var result;
11782         try {
11783             result = o.reader.read(response);
11784         }catch(e){
11785             this.fireEvent("loadexception", this, o, response, e);
11786             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11787             return;
11788         }
11789         
11790         this.fireEvent("load", this, o, o.request.arg);
11791         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11792     },
11793
11794     // private
11795     update : function(dataSet){
11796
11797     },
11798
11799     // private
11800     updateResponse : function(dataSet){
11801
11802     }
11803 });/*
11804  * Based on:
11805  * Ext JS Library 1.1.1
11806  * Copyright(c) 2006-2007, Ext JS, LLC.
11807  *
11808  * Originally Released Under LGPL - original licence link has changed is not relivant.
11809  *
11810  * Fork - LGPL
11811  * <script type="text/javascript">
11812  */
11813
11814 /**
11815  * @class Roo.data.ScriptTagProxy
11816  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11817  * other than the originating domain of the running page.<br><br>
11818  * <p>
11819  * <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
11820  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11821  * <p>
11822  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11823  * source code that is used as the source inside a &lt;script> tag.<br><br>
11824  * <p>
11825  * In order for the browser to process the returned data, the server must wrap the data object
11826  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11827  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11828  * depending on whether the callback name was passed:
11829  * <p>
11830  * <pre><code>
11831 boolean scriptTag = false;
11832 String cb = request.getParameter("callback");
11833 if (cb != null) {
11834     scriptTag = true;
11835     response.setContentType("text/javascript");
11836 } else {
11837     response.setContentType("application/x-json");
11838 }
11839 Writer out = response.getWriter();
11840 if (scriptTag) {
11841     out.write(cb + "(");
11842 }
11843 out.print(dataBlock.toJsonString());
11844 if (scriptTag) {
11845     out.write(");");
11846 }
11847 </pre></code>
11848  *
11849  * @constructor
11850  * @param {Object} config A configuration object.
11851  */
11852 Roo.data.ScriptTagProxy = function(config){
11853     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11854     Roo.apply(this, config);
11855     this.head = document.getElementsByTagName("head")[0];
11856 };
11857
11858 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11859
11860 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11861     /**
11862      * @cfg {String} url The URL from which to request the data object.
11863      */
11864     /**
11865      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11866      */
11867     timeout : 30000,
11868     /**
11869      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11870      * the server the name of the callback function set up by the load call to process the returned data object.
11871      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11872      * javascript output which calls this named function passing the data object as its only parameter.
11873      */
11874     callbackParam : "callback",
11875     /**
11876      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11877      * name to the request.
11878      */
11879     nocache : true,
11880
11881     /**
11882      * Load data from the configured URL, read the data object into
11883      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11884      * process that block using the passed callback.
11885      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11886      * for the request to the remote server.
11887      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11888      * object into a block of Roo.data.Records.
11889      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11890      * The function must be passed <ul>
11891      * <li>The Record block object</li>
11892      * <li>The "arg" argument from the load function</li>
11893      * <li>A boolean success indicator</li>
11894      * </ul>
11895      * @param {Object} scope The scope in which to call the callback
11896      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11897      */
11898     load : function(params, reader, callback, scope, arg){
11899         if(this.fireEvent("beforeload", this, params) !== false){
11900
11901             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11902
11903             var url = this.url;
11904             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11905             if(this.nocache){
11906                 url += "&_dc=" + (new Date().getTime());
11907             }
11908             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11909             var trans = {
11910                 id : transId,
11911                 cb : "stcCallback"+transId,
11912                 scriptId : "stcScript"+transId,
11913                 params : params,
11914                 arg : arg,
11915                 url : url,
11916                 callback : callback,
11917                 scope : scope,
11918                 reader : reader
11919             };
11920             var conn = this;
11921
11922             window[trans.cb] = function(o){
11923                 conn.handleResponse(o, trans);
11924             };
11925
11926             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11927
11928             if(this.autoAbort !== false){
11929                 this.abort();
11930             }
11931
11932             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11933
11934             var script = document.createElement("script");
11935             script.setAttribute("src", url);
11936             script.setAttribute("type", "text/javascript");
11937             script.setAttribute("id", trans.scriptId);
11938             this.head.appendChild(script);
11939
11940             this.trans = trans;
11941         }else{
11942             callback.call(scope||this, null, arg, false);
11943         }
11944     },
11945
11946     // private
11947     isLoading : function(){
11948         return this.trans ? true : false;
11949     },
11950
11951     /**
11952      * Abort the current server request.
11953      */
11954     abort : function(){
11955         if(this.isLoading()){
11956             this.destroyTrans(this.trans);
11957         }
11958     },
11959
11960     // private
11961     destroyTrans : function(trans, isLoaded){
11962         this.head.removeChild(document.getElementById(trans.scriptId));
11963         clearTimeout(trans.timeoutId);
11964         if(isLoaded){
11965             window[trans.cb] = undefined;
11966             try{
11967                 delete window[trans.cb];
11968             }catch(e){}
11969         }else{
11970             // if hasn't been loaded, wait for load to remove it to prevent script error
11971             window[trans.cb] = function(){
11972                 window[trans.cb] = undefined;
11973                 try{
11974                     delete window[trans.cb];
11975                 }catch(e){}
11976             };
11977         }
11978     },
11979
11980     // private
11981     handleResponse : function(o, trans){
11982         this.trans = false;
11983         this.destroyTrans(trans, true);
11984         var result;
11985         try {
11986             result = trans.reader.readRecords(o);
11987         }catch(e){
11988             this.fireEvent("loadexception", this, o, trans.arg, e);
11989             trans.callback.call(trans.scope||window, null, trans.arg, false);
11990             return;
11991         }
11992         this.fireEvent("load", this, o, trans.arg);
11993         trans.callback.call(trans.scope||window, result, trans.arg, true);
11994     },
11995
11996     // private
11997     handleFailure : function(trans){
11998         this.trans = false;
11999         this.destroyTrans(trans, false);
12000         this.fireEvent("loadexception", this, null, trans.arg);
12001         trans.callback.call(trans.scope||window, null, trans.arg, false);
12002     }
12003 });/*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013
12014 /**
12015  * @class Roo.data.JsonReader
12016  * @extends Roo.data.DataReader
12017  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12018  * based on mappings in a provided Roo.data.Record constructor.
12019  * 
12020  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12021  * in the reply previously. 
12022  * 
12023  * <p>
12024  * Example code:
12025  * <pre><code>
12026 var RecordDef = Roo.data.Record.create([
12027     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12028     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12029 ]);
12030 var myReader = new Roo.data.JsonReader({
12031     totalProperty: "results",    // The property which contains the total dataset size (optional)
12032     root: "rows",                // The property which contains an Array of row objects
12033     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12034 }, RecordDef);
12035 </code></pre>
12036  * <p>
12037  * This would consume a JSON file like this:
12038  * <pre><code>
12039 { 'results': 2, 'rows': [
12040     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12041     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12042 }
12043 </code></pre>
12044  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12045  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12046  * paged from the remote server.
12047  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12048  * @cfg {String} root name of the property which contains the Array of row objects.
12049  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12050  * @cfg {Array} fields Array of field definition objects
12051  * @constructor
12052  * Create a new JsonReader
12053  * @param {Object} meta Metadata configuration options
12054  * @param {Object} recordType Either an Array of field definition objects,
12055  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12056  */
12057 Roo.data.JsonReader = function(meta, recordType){
12058     
12059     meta = meta || {};
12060     // set some defaults:
12061     Roo.applyIf(meta, {
12062         totalProperty: 'total',
12063         successProperty : 'success',
12064         root : 'data',
12065         id : 'id'
12066     });
12067     
12068     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12069 };
12070 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12071     
12072     /**
12073      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12074      * Used by Store query builder to append _requestMeta to params.
12075      * 
12076      */
12077     metaFromRemote : false,
12078     /**
12079      * This method is only used by a DataProxy which has retrieved data from a remote server.
12080      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12081      * @return {Object} data A data block which is used by an Roo.data.Store object as
12082      * a cache of Roo.data.Records.
12083      */
12084     read : function(response){
12085         var json = response.responseText;
12086        
12087         var o = /* eval:var:o */ eval("("+json+")");
12088         if(!o) {
12089             throw {message: "JsonReader.read: Json object not found"};
12090         }
12091         
12092         if(o.metaData){
12093             
12094             delete this.ef;
12095             this.metaFromRemote = true;
12096             this.meta = o.metaData;
12097             this.recordType = Roo.data.Record.create(o.metaData.fields);
12098             this.onMetaChange(this.meta, this.recordType, o);
12099         }
12100         return this.readRecords(o);
12101     },
12102
12103     // private function a store will implement
12104     onMetaChange : function(meta, recordType, o){
12105
12106     },
12107
12108     /**
12109          * @ignore
12110          */
12111     simpleAccess: function(obj, subsc) {
12112         return obj[subsc];
12113     },
12114
12115         /**
12116          * @ignore
12117          */
12118     getJsonAccessor: function(){
12119         var re = /[\[\.]/;
12120         return function(expr) {
12121             try {
12122                 return(re.test(expr))
12123                     ? new Function("obj", "return obj." + expr)
12124                     : function(obj){
12125                         return obj[expr];
12126                     };
12127             } catch(e){}
12128             return Roo.emptyFn;
12129         };
12130     }(),
12131
12132     /**
12133      * Create a data block containing Roo.data.Records from an XML document.
12134      * @param {Object} o An object which contains an Array of row objects in the property specified
12135      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12136      * which contains the total size of the dataset.
12137      * @return {Object} data A data block which is used by an Roo.data.Store object as
12138      * a cache of Roo.data.Records.
12139      */
12140     readRecords : function(o){
12141         /**
12142          * After any data loads, the raw JSON data is available for further custom processing.
12143          * @type Object
12144          */
12145         this.o = o;
12146         var s = this.meta, Record = this.recordType,
12147             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12148
12149 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12150         if (!this.ef) {
12151             if(s.totalProperty) {
12152                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12153                 }
12154                 if(s.successProperty) {
12155                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12156                 }
12157                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12158                 if (s.id) {
12159                         var g = this.getJsonAccessor(s.id);
12160                         this.getId = function(rec) {
12161                                 var r = g(rec);  
12162                                 return (r === undefined || r === "") ? null : r;
12163                         };
12164                 } else {
12165                         this.getId = function(){return null;};
12166                 }
12167             this.ef = [];
12168             for(var jj = 0; jj < fl; jj++){
12169                 f = fi[jj];
12170                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12171                 this.ef[jj] = this.getJsonAccessor(map);
12172             }
12173         }
12174
12175         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12176         if(s.totalProperty){
12177             var vt = parseInt(this.getTotal(o), 10);
12178             if(!isNaN(vt)){
12179                 totalRecords = vt;
12180             }
12181         }
12182         if(s.successProperty){
12183             var vs = this.getSuccess(o);
12184             if(vs === false || vs === 'false'){
12185                 success = false;
12186             }
12187         }
12188         var records = [];
12189         for(var i = 0; i < c; i++){
12190                 var n = root[i];
12191             var values = {};
12192             var id = this.getId(n);
12193             for(var j = 0; j < fl; j++){
12194                 f = fi[j];
12195             var v = this.ef[j](n);
12196             if (!f.convert) {
12197                 Roo.log('missing convert for ' + f.name);
12198                 Roo.log(f);
12199                 continue;
12200             }
12201             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12202             }
12203             var record = new Record(values, id);
12204             record.json = n;
12205             records[i] = record;
12206         }
12207         return {
12208             raw : o,
12209             success : success,
12210             records : records,
12211             totalRecords : totalRecords
12212         };
12213     }
12214 });/*
12215  * Based on:
12216  * Ext JS Library 1.1.1
12217  * Copyright(c) 2006-2007, Ext JS, LLC.
12218  *
12219  * Originally Released Under LGPL - original licence link has changed is not relivant.
12220  *
12221  * Fork - LGPL
12222  * <script type="text/javascript">
12223  */
12224
12225 /**
12226  * @class Roo.data.ArrayReader
12227  * @extends Roo.data.DataReader
12228  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12229  * Each element of that Array represents a row of data fields. The
12230  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12231  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12232  * <p>
12233  * Example code:.
12234  * <pre><code>
12235 var RecordDef = Roo.data.Record.create([
12236     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12237     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12238 ]);
12239 var myReader = new Roo.data.ArrayReader({
12240     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12241 }, RecordDef);
12242 </code></pre>
12243  * <p>
12244  * This would consume an Array like this:
12245  * <pre><code>
12246 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12247   </code></pre>
12248  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12249  * @constructor
12250  * Create a new JsonReader
12251  * @param {Object} meta Metadata configuration options.
12252  * @param {Object} recordType Either an Array of field definition objects
12253  * as specified to {@link Roo.data.Record#create},
12254  * or an {@link Roo.data.Record} object
12255  * created using {@link Roo.data.Record#create}.
12256  */
12257 Roo.data.ArrayReader = function(meta, recordType){
12258     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12259 };
12260
12261 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12262     /**
12263      * Create a data block containing Roo.data.Records from an XML document.
12264      * @param {Object} o An Array of row objects which represents the dataset.
12265      * @return {Object} data A data block which is used by an Roo.data.Store object as
12266      * a cache of Roo.data.Records.
12267      */
12268     readRecords : function(o){
12269         var sid = this.meta ? this.meta.id : null;
12270         var recordType = this.recordType, fields = recordType.prototype.fields;
12271         var records = [];
12272         var root = o;
12273             for(var i = 0; i < root.length; i++){
12274                     var n = root[i];
12275                 var values = {};
12276                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12277                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12278                 var f = fields.items[j];
12279                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12280                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12281                 v = f.convert(v);
12282                 values[f.name] = v;
12283             }
12284                 var record = new recordType(values, id);
12285                 record.json = n;
12286                 records[records.length] = record;
12287             }
12288             return {
12289                 records : records,
12290                 totalRecords : records.length
12291             };
12292     }
12293 });/*
12294  * - LGPL
12295  * * 
12296  */
12297
12298 /**
12299  * @class Roo.bootstrap.ComboBox
12300  * @extends Roo.bootstrap.TriggerField
12301  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12302  * @cfg {Boolean} append (true|false) default false
12303  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12304  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12305  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12306  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12307  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12308  * @cfg {Boolean} animate default true
12309  * @cfg {Boolean} emptyResultText only for touch device
12310  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12311  * @cfg {String} emptyTitle default ''
12312  * @constructor
12313  * Create a new ComboBox.
12314  * @param {Object} config Configuration options
12315  */
12316 Roo.bootstrap.ComboBox = function(config){
12317     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12318     this.addEvents({
12319         /**
12320          * @event expand
12321          * Fires when the dropdown list is expanded
12322              * @param {Roo.bootstrap.ComboBox} combo This combo box
12323              */
12324         'expand' : true,
12325         /**
12326          * @event collapse
12327          * Fires when the dropdown list is collapsed
12328              * @param {Roo.bootstrap.ComboBox} combo This combo box
12329              */
12330         'collapse' : true,
12331         /**
12332          * @event beforeselect
12333          * Fires before a list item is selected. Return false to cancel the selection.
12334              * @param {Roo.bootstrap.ComboBox} combo This combo box
12335              * @param {Roo.data.Record} record The data record returned from the underlying store
12336              * @param {Number} index The index of the selected item in the dropdown list
12337              */
12338         'beforeselect' : true,
12339         /**
12340          * @event select
12341          * Fires when a list item is selected
12342              * @param {Roo.bootstrap.ComboBox} combo This combo box
12343              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12344              * @param {Number} index The index of the selected item in the dropdown list
12345              */
12346         'select' : true,
12347         /**
12348          * @event beforequery
12349          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12350          * The event object passed has these properties:
12351              * @param {Roo.bootstrap.ComboBox} combo This combo box
12352              * @param {String} query The query
12353              * @param {Boolean} forceAll true to force "all" query
12354              * @param {Boolean} cancel true to cancel the query
12355              * @param {Object} e The query event object
12356              */
12357         'beforequery': true,
12358          /**
12359          * @event add
12360          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12361              * @param {Roo.bootstrap.ComboBox} combo This combo box
12362              */
12363         'add' : true,
12364         /**
12365          * @event edit
12366          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12367              * @param {Roo.bootstrap.ComboBox} combo This combo box
12368              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12369              */
12370         'edit' : true,
12371         /**
12372          * @event remove
12373          * Fires when the remove value from the combobox array
12374              * @param {Roo.bootstrap.ComboBox} combo This combo box
12375              */
12376         'remove' : true,
12377         /**
12378          * @event afterremove
12379          * Fires when the remove value from the combobox array
12380              * @param {Roo.bootstrap.ComboBox} combo This combo box
12381              */
12382         'afterremove' : true,
12383         /**
12384          * @event specialfilter
12385          * Fires when specialfilter
12386             * @param {Roo.bootstrap.ComboBox} combo This combo box
12387             */
12388         'specialfilter' : true,
12389         /**
12390          * @event tick
12391          * Fires when tick the element
12392             * @param {Roo.bootstrap.ComboBox} combo This combo box
12393             */
12394         'tick' : true,
12395         /**
12396          * @event touchviewdisplay
12397          * Fires when touch view require special display (default is using displayField)
12398             * @param {Roo.bootstrap.ComboBox} combo This combo box
12399             * @param {Object} cfg set html .
12400             */
12401         'touchviewdisplay' : true
12402         
12403     });
12404     
12405     this.item = [];
12406     this.tickItems = [];
12407     
12408     this.selectedIndex = -1;
12409     if(this.mode == 'local'){
12410         if(config.queryDelay === undefined){
12411             this.queryDelay = 10;
12412         }
12413         if(config.minChars === undefined){
12414             this.minChars = 0;
12415         }
12416     }
12417 };
12418
12419 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12420      
12421     /**
12422      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12423      * rendering into an Roo.Editor, defaults to false)
12424      */
12425     /**
12426      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12427      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12428      */
12429     /**
12430      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12431      */
12432     /**
12433      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12434      * the dropdown list (defaults to undefined, with no header element)
12435      */
12436
12437      /**
12438      * @cfg {String/Roo.Template} tpl The template to use to render the output
12439      */
12440      
12441      /**
12442      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12443      */
12444     listWidth: undefined,
12445     /**
12446      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12447      * mode = 'remote' or 'text' if mode = 'local')
12448      */
12449     displayField: undefined,
12450     
12451     /**
12452      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12453      * mode = 'remote' or 'value' if mode = 'local'). 
12454      * Note: use of a valueField requires the user make a selection
12455      * in order for a value to be mapped.
12456      */
12457     valueField: undefined,
12458     /**
12459      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12460      */
12461     modalTitle : '',
12462     
12463     /**
12464      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12465      * field's data value (defaults to the underlying DOM element's name)
12466      */
12467     hiddenName: undefined,
12468     /**
12469      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12470      */
12471     listClass: '',
12472     /**
12473      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12474      */
12475     selectedClass: 'active',
12476     
12477     /**
12478      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12479      */
12480     shadow:'sides',
12481     /**
12482      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12483      * anchor positions (defaults to 'tl-bl')
12484      */
12485     listAlign: 'tl-bl?',
12486     /**
12487      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12488      */
12489     maxHeight: 300,
12490     /**
12491      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12492      * query specified by the allQuery config option (defaults to 'query')
12493      */
12494     triggerAction: 'query',
12495     /**
12496      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12497      * (defaults to 4, does not apply if editable = false)
12498      */
12499     minChars : 4,
12500     /**
12501      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12502      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12503      */
12504     typeAhead: false,
12505     /**
12506      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12507      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12508      */
12509     queryDelay: 500,
12510     /**
12511      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12512      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12513      */
12514     pageSize: 0,
12515     /**
12516      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12517      * when editable = true (defaults to false)
12518      */
12519     selectOnFocus:false,
12520     /**
12521      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12522      */
12523     queryParam: 'query',
12524     /**
12525      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12526      * when mode = 'remote' (defaults to 'Loading...')
12527      */
12528     loadingText: 'Loading...',
12529     /**
12530      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12531      */
12532     resizable: false,
12533     /**
12534      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12535      */
12536     handleHeight : 8,
12537     /**
12538      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12539      * traditional select (defaults to true)
12540      */
12541     editable: true,
12542     /**
12543      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12544      */
12545     allQuery: '',
12546     /**
12547      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12548      */
12549     mode: 'remote',
12550     /**
12551      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12552      * listWidth has a higher value)
12553      */
12554     minListWidth : 70,
12555     /**
12556      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12557      * allow the user to set arbitrary text into the field (defaults to false)
12558      */
12559     forceSelection:false,
12560     /**
12561      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12562      * if typeAhead = true (defaults to 250)
12563      */
12564     typeAheadDelay : 250,
12565     /**
12566      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12567      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12568      */
12569     valueNotFoundText : undefined,
12570     /**
12571      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12572      */
12573     blockFocus : false,
12574     
12575     /**
12576      * @cfg {Boolean} disableClear Disable showing of clear button.
12577      */
12578     disableClear : false,
12579     /**
12580      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12581      */
12582     alwaysQuery : false,
12583     
12584     /**
12585      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12586      */
12587     multiple : false,
12588     
12589     /**
12590      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12591      */
12592     invalidClass : "has-warning",
12593     
12594     /**
12595      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12596      */
12597     validClass : "has-success",
12598     
12599     /**
12600      * @cfg {Boolean} specialFilter (true|false) special filter default false
12601      */
12602     specialFilter : false,
12603     
12604     /**
12605      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12606      */
12607     mobileTouchView : true,
12608     
12609     /**
12610      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12611      */
12612     useNativeIOS : false,
12613     
12614     ios_options : false,
12615     
12616     //private
12617     addicon : false,
12618     editicon: false,
12619     
12620     page: 0,
12621     hasQuery: false,
12622     append: false,
12623     loadNext: false,
12624     autoFocus : true,
12625     tickable : false,
12626     btnPosition : 'right',
12627     triggerList : true,
12628     showToggleBtn : true,
12629     animate : true,
12630     emptyResultText: 'Empty',
12631     triggerText : 'Select',
12632     emptyTitle : '',
12633     
12634     // element that contains real text value.. (when hidden is used..)
12635     
12636     getAutoCreate : function()
12637     {   
12638         var cfg = false;
12639         //render
12640         /*
12641          * Render classic select for iso
12642          */
12643         
12644         if(Roo.isIOS && this.useNativeIOS){
12645             cfg = this.getAutoCreateNativeIOS();
12646             return cfg;
12647         }
12648         
12649         /*
12650          * Touch Devices
12651          */
12652         
12653         if(Roo.isTouch && this.mobileTouchView){
12654             cfg = this.getAutoCreateTouchView();
12655             return cfg;;
12656         }
12657         
12658         /*
12659          *  Normal ComboBox
12660          */
12661         if(!this.tickable){
12662             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12663             if(this.name == 'info_year_invest_id_display_name'){
12664                 Roo.log('cfg.................................................');
12665                 Roo.log(cfg);
12666             }
12667             return cfg;
12668         }
12669         
12670         /*
12671          *  ComboBox with tickable selections
12672          */
12673              
12674         var align = this.labelAlign || this.parentLabelAlign();
12675         
12676         cfg = {
12677             cls : 'form-group roo-combobox-tickable' //input-group
12678         };
12679         
12680         var btn_text_select = '';
12681         var btn_text_done = '';
12682         var btn_text_cancel = '';
12683         
12684         if (this.btn_text_show) {
12685             btn_text_select = 'Select';
12686             btn_text_done = 'Done';
12687             btn_text_cancel = 'Cancel'; 
12688         }
12689         
12690         var buttons = {
12691             tag : 'div',
12692             cls : 'tickable-buttons',
12693             cn : [
12694                 {
12695                     tag : 'button',
12696                     type : 'button',
12697                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12698                     //html : this.triggerText
12699                     html: btn_text_select
12700                 },
12701                 {
12702                     tag : 'button',
12703                     type : 'button',
12704                     name : 'ok',
12705                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12706                     //html : 'Done'
12707                     html: btn_text_done
12708                 },
12709                 {
12710                     tag : 'button',
12711                     type : 'button',
12712                     name : 'cancel',
12713                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12714                     //html : 'Cancel'
12715                     html: btn_text_cancel
12716                 }
12717             ]
12718         };
12719         
12720         if(this.editable){
12721             buttons.cn.unshift({
12722                 tag: 'input',
12723                 cls: 'roo-select2-search-field-input'
12724             });
12725         }
12726         
12727         var _this = this;
12728         
12729         Roo.each(buttons.cn, function(c){
12730             if (_this.size) {
12731                 c.cls += ' btn-' + _this.size;
12732             }
12733
12734             if (_this.disabled) {
12735                 c.disabled = true;
12736             }
12737         });
12738         
12739         var box = {
12740             tag: 'div',
12741             cn: [
12742                 {
12743                     tag: 'input',
12744                     type : 'hidden',
12745                     cls: 'form-hidden-field'
12746                 },
12747                 {
12748                     tag: 'ul',
12749                     cls: 'roo-select2-choices',
12750                     cn:[
12751                         {
12752                             tag: 'li',
12753                             cls: 'roo-select2-search-field',
12754                             cn: [
12755                                 buttons
12756                             ]
12757                         }
12758                     ]
12759                 }
12760             ]
12761         };
12762         
12763         var combobox = {
12764             cls: 'roo-select2-container input-group roo-select2-container-multi',
12765             cn: [
12766                 box
12767 //                {
12768 //                    tag: 'ul',
12769 //                    cls: 'typeahead typeahead-long dropdown-menu',
12770 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12771 //                }
12772             ]
12773         };
12774         
12775         if(this.hasFeedback && !this.allowBlank){
12776             
12777             var feedback = {
12778                 tag: 'span',
12779                 cls: 'glyphicon form-control-feedback'
12780             };
12781
12782             combobox.cn.push(feedback);
12783         }
12784         
12785         
12786         if (align ==='left' && this.fieldLabel.length) {
12787             
12788             cfg.cls += ' roo-form-group-label-left';
12789             
12790             cfg.cn = [
12791                 {
12792                     tag : 'i',
12793                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12794                     tooltip : 'This field is required'
12795                 },
12796                 {
12797                     tag: 'label',
12798                     'for' :  id,
12799                     cls : 'control-label',
12800                     html : this.fieldLabel
12801
12802                 },
12803                 {
12804                     cls : "", 
12805                     cn: [
12806                         combobox
12807                     ]
12808                 }
12809
12810             ];
12811             
12812             var labelCfg = cfg.cn[1];
12813             var contentCfg = cfg.cn[2];
12814             
12815
12816             if(this.indicatorpos == 'right'){
12817                 
12818                 cfg.cn = [
12819                     {
12820                         tag: 'label',
12821                         'for' :  id,
12822                         cls : 'control-label',
12823                         cn : [
12824                             {
12825                                 tag : 'span',
12826                                 html : this.fieldLabel
12827                             },
12828                             {
12829                                 tag : 'i',
12830                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12831                                 tooltip : 'This field is required'
12832                             }
12833                         ]
12834                     },
12835                     {
12836                         cls : "",
12837                         cn: [
12838                             combobox
12839                         ]
12840                     }
12841
12842                 ];
12843                 
12844                 
12845                 
12846                 labelCfg = cfg.cn[0];
12847                 contentCfg = cfg.cn[1];
12848             
12849             }
12850             
12851             if(this.labelWidth > 12){
12852                 labelCfg.style = "width: " + this.labelWidth + 'px';
12853             }
12854             
12855             if(this.labelWidth < 13 && this.labelmd == 0){
12856                 this.labelmd = this.labelWidth;
12857             }
12858             
12859             if(this.labellg > 0){
12860                 labelCfg.cls += ' col-lg-' + this.labellg;
12861                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12862             }
12863             
12864             if(this.labelmd > 0){
12865                 labelCfg.cls += ' col-md-' + this.labelmd;
12866                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12867             }
12868             
12869             if(this.labelsm > 0){
12870                 labelCfg.cls += ' col-sm-' + this.labelsm;
12871                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12872             }
12873             
12874             if(this.labelxs > 0){
12875                 labelCfg.cls += ' col-xs-' + this.labelxs;
12876                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12877             }
12878                 
12879                 
12880         } else if ( this.fieldLabel.length) {
12881 //                Roo.log(" label");
12882                  cfg.cn = [
12883                     {
12884                         tag : 'i',
12885                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12886                         tooltip : 'This field is required'
12887                     },
12888                     {
12889                         tag: 'label',
12890                         //cls : 'input-group-addon',
12891                         html : this.fieldLabel
12892                         
12893                     },
12894                     
12895                     combobox
12896                     
12897                 ];
12898                 
12899                 if(this.indicatorpos == 'right'){
12900                     
12901                     cfg.cn = [
12902                         {
12903                             tag: 'label',
12904                             //cls : 'input-group-addon',
12905                             cn : [
12906                                 {
12907                                     tag : 'span',
12908                                     html : this.fieldLabel
12909                                 },
12910                                 {
12911                                     tag : 'i',
12912                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12913                                     tooltip : 'This field is required'
12914                                 }
12915                             ]
12916                         },
12917                         
12918                         
12919                         
12920                         combobox
12921
12922                     ];
12923                 
12924                 }
12925
12926         } else {
12927             
12928 //                Roo.log(" no label && no align");
12929                 cfg = combobox
12930                      
12931                 
12932         }
12933          
12934         var settings=this;
12935         ['xs','sm','md','lg'].map(function(size){
12936             if (settings[size]) {
12937                 cfg.cls += ' col-' + size + '-' + settings[size];
12938             }
12939         });
12940         
12941         return cfg;
12942         
12943     },
12944     
12945     _initEventsCalled : false,
12946     
12947     // private
12948     initEvents: function()
12949     {   
12950         if (this._initEventsCalled) { // as we call render... prevent looping...
12951             return;
12952         }
12953         this._initEventsCalled = true;
12954         
12955         if (!this.store) {
12956             throw "can not find store for combo";
12957         }
12958         
12959         this.store = Roo.factory(this.store, Roo.data);
12960         this.store.parent = this;
12961         
12962         // if we are building from html. then this element is so complex, that we can not really
12963         // use the rendered HTML.
12964         // so we have to trash and replace the previous code.
12965         if (Roo.XComponent.build_from_html) {
12966             
12967             // remove this element....
12968             var e = this.el.dom, k=0;
12969             while (e ) { e = e.previousSibling;  ++k;}
12970
12971             this.el.remove();
12972             
12973             this.el=false;
12974             this.rendered = false;
12975             
12976             this.render(this.parent().getChildContainer(true), k);
12977             
12978             
12979             
12980         }
12981         
12982         if(Roo.isIOS && this.useNativeIOS){
12983             this.initIOSView();
12984             return;
12985         }
12986         
12987         /*
12988          * Touch Devices
12989          */
12990         
12991         if(Roo.isTouch && this.mobileTouchView){
12992             this.initTouchView();
12993             return;
12994         }
12995         
12996         if(this.tickable){
12997             this.initTickableEvents();
12998             return;
12999         }
13000         
13001         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13002         
13003         if(this.hiddenName){
13004             
13005             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13006             
13007             this.hiddenField.dom.value =
13008                 this.hiddenValue !== undefined ? this.hiddenValue :
13009                 this.value !== undefined ? this.value : '';
13010
13011             // prevent input submission
13012             this.el.dom.removeAttribute('name');
13013             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13014              
13015              
13016         }
13017         //if(Roo.isGecko){
13018         //    this.el.dom.setAttribute('autocomplete', 'off');
13019         //}
13020         
13021         var cls = 'x-combo-list';
13022         
13023         //this.list = new Roo.Layer({
13024         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13025         //});
13026         
13027         var _this = this;
13028         
13029         (function(){
13030             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13031             _this.list.setWidth(lw);
13032         }).defer(100);
13033         
13034         this.list.on('mouseover', this.onViewOver, this);
13035         this.list.on('mousemove', this.onViewMove, this);
13036         
13037         this.list.on('scroll', this.onViewScroll, this);
13038         
13039         /*
13040         this.list.swallowEvent('mousewheel');
13041         this.assetHeight = 0;
13042
13043         if(this.title){
13044             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13045             this.assetHeight += this.header.getHeight();
13046         }
13047
13048         this.innerList = this.list.createChild({cls:cls+'-inner'});
13049         this.innerList.on('mouseover', this.onViewOver, this);
13050         this.innerList.on('mousemove', this.onViewMove, this);
13051         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13052         
13053         if(this.allowBlank && !this.pageSize && !this.disableClear){
13054             this.footer = this.list.createChild({cls:cls+'-ft'});
13055             this.pageTb = new Roo.Toolbar(this.footer);
13056            
13057         }
13058         if(this.pageSize){
13059             this.footer = this.list.createChild({cls:cls+'-ft'});
13060             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13061                     {pageSize: this.pageSize});
13062             
13063         }
13064         
13065         if (this.pageTb && this.allowBlank && !this.disableClear) {
13066             var _this = this;
13067             this.pageTb.add(new Roo.Toolbar.Fill(), {
13068                 cls: 'x-btn-icon x-btn-clear',
13069                 text: '&#160;',
13070                 handler: function()
13071                 {
13072                     _this.collapse();
13073                     _this.clearValue();
13074                     _this.onSelect(false, -1);
13075                 }
13076             });
13077         }
13078         if (this.footer) {
13079             this.assetHeight += this.footer.getHeight();
13080         }
13081         */
13082             
13083         if(!this.tpl){
13084             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13085         }
13086
13087         this.view = new Roo.View(this.list, this.tpl, {
13088             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13089         });
13090         //this.view.wrapEl.setDisplayed(false);
13091         this.view.on('click', this.onViewClick, this);
13092         
13093         
13094         this.store.on('beforeload', this.onBeforeLoad, this);
13095         this.store.on('load', this.onLoad, this);
13096         this.store.on('loadexception', this.onLoadException, this);
13097         /*
13098         if(this.resizable){
13099             this.resizer = new Roo.Resizable(this.list,  {
13100                pinned:true, handles:'se'
13101             });
13102             this.resizer.on('resize', function(r, w, h){
13103                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13104                 this.listWidth = w;
13105                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13106                 this.restrictHeight();
13107             }, this);
13108             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13109         }
13110         */
13111         if(!this.editable){
13112             this.editable = true;
13113             this.setEditable(false);
13114         }
13115         
13116         /*
13117         
13118         if (typeof(this.events.add.listeners) != 'undefined') {
13119             
13120             this.addicon = this.wrap.createChild(
13121                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13122        
13123             this.addicon.on('click', function(e) {
13124                 this.fireEvent('add', this);
13125             }, this);
13126         }
13127         if (typeof(this.events.edit.listeners) != 'undefined') {
13128             
13129             this.editicon = this.wrap.createChild(
13130                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13131             if (this.addicon) {
13132                 this.editicon.setStyle('margin-left', '40px');
13133             }
13134             this.editicon.on('click', function(e) {
13135                 
13136                 // we fire even  if inothing is selected..
13137                 this.fireEvent('edit', this, this.lastData );
13138                 
13139             }, this);
13140         }
13141         */
13142         
13143         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13144             "up" : function(e){
13145                 this.inKeyMode = true;
13146                 this.selectPrev();
13147             },
13148
13149             "down" : function(e){
13150                 if(!this.isExpanded()){
13151                     this.onTriggerClick();
13152                 }else{
13153                     this.inKeyMode = true;
13154                     this.selectNext();
13155                 }
13156             },
13157
13158             "enter" : function(e){
13159 //                this.onViewClick();
13160                 //return true;
13161                 this.collapse();
13162                 
13163                 if(this.fireEvent("specialkey", this, e)){
13164                     this.onViewClick(false);
13165                 }
13166                 
13167                 return true;
13168             },
13169
13170             "esc" : function(e){
13171                 this.collapse();
13172             },
13173
13174             "tab" : function(e){
13175                 this.collapse();
13176                 
13177                 if(this.fireEvent("specialkey", this, e)){
13178                     this.onViewClick(false);
13179                 }
13180                 
13181                 return true;
13182             },
13183
13184             scope : this,
13185
13186             doRelay : function(foo, bar, hname){
13187                 if(hname == 'down' || this.scope.isExpanded()){
13188                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13189                 }
13190                 return true;
13191             },
13192
13193             forceKeyDown: true
13194         });
13195         
13196         
13197         this.queryDelay = Math.max(this.queryDelay || 10,
13198                 this.mode == 'local' ? 10 : 250);
13199         
13200         
13201         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13202         
13203         if(this.typeAhead){
13204             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13205         }
13206         if(this.editable !== false){
13207             this.inputEl().on("keyup", this.onKeyUp, this);
13208         }
13209         if(this.forceSelection){
13210             this.inputEl().on('blur', this.doForce, this);
13211         }
13212         
13213         if(this.multiple){
13214             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13215             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13216         }
13217     },
13218     
13219     initTickableEvents: function()
13220     {   
13221         this.createList();
13222         
13223         if(this.hiddenName){
13224             
13225             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13226             
13227             this.hiddenField.dom.value =
13228                 this.hiddenValue !== undefined ? this.hiddenValue :
13229                 this.value !== undefined ? this.value : '';
13230
13231             // prevent input submission
13232             this.el.dom.removeAttribute('name');
13233             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13234              
13235              
13236         }
13237         
13238 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13239         
13240         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13241         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13242         if(this.triggerList){
13243             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13244         }
13245          
13246         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13247         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13248         
13249         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13250         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13251         
13252         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13253         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13254         
13255         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13256         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13257         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13258         
13259         this.okBtn.hide();
13260         this.cancelBtn.hide();
13261         
13262         var _this = this;
13263         
13264         (function(){
13265             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13266             _this.list.setWidth(lw);
13267         }).defer(100);
13268         
13269         this.list.on('mouseover', this.onViewOver, this);
13270         this.list.on('mousemove', this.onViewMove, this);
13271         
13272         this.list.on('scroll', this.onViewScroll, this);
13273         
13274         if(!this.tpl){
13275             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></div></li>';
13276         }
13277
13278         this.view = new Roo.View(this.list, this.tpl, {
13279             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13280         });
13281         
13282         //this.view.wrapEl.setDisplayed(false);
13283         this.view.on('click', this.onViewClick, this);
13284         
13285         
13286         
13287         this.store.on('beforeload', this.onBeforeLoad, this);
13288         this.store.on('load', this.onLoad, this);
13289         this.store.on('loadexception', this.onLoadException, this);
13290         
13291         if(this.editable){
13292             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13293                 "up" : function(e){
13294                     this.inKeyMode = true;
13295                     this.selectPrev();
13296                 },
13297
13298                 "down" : function(e){
13299                     this.inKeyMode = true;
13300                     this.selectNext();
13301                 },
13302
13303                 "enter" : function(e){
13304                     if(this.fireEvent("specialkey", this, e)){
13305                         this.onViewClick(false);
13306                     }
13307                     
13308                     return true;
13309                 },
13310
13311                 "esc" : function(e){
13312                     this.onTickableFooterButtonClick(e, false, false);
13313                 },
13314
13315                 "tab" : function(e){
13316                     this.fireEvent("specialkey", this, e);
13317                     
13318                     this.onTickableFooterButtonClick(e, false, false);
13319                     
13320                     return true;
13321                 },
13322
13323                 scope : this,
13324
13325                 doRelay : function(e, fn, key){
13326                     if(this.scope.isExpanded()){
13327                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13328                     }
13329                     return true;
13330                 },
13331
13332                 forceKeyDown: true
13333             });
13334         }
13335         
13336         this.queryDelay = Math.max(this.queryDelay || 10,
13337                 this.mode == 'local' ? 10 : 250);
13338         
13339         
13340         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13341         
13342         if(this.typeAhead){
13343             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13344         }
13345         
13346         if(this.editable !== false){
13347             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13348         }
13349         
13350         this.indicator = this.indicatorEl();
13351         
13352         if(this.indicator){
13353             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13354             this.indicator.hide();
13355         }
13356         
13357     },
13358
13359     onDestroy : function(){
13360         if(this.view){
13361             this.view.setStore(null);
13362             this.view.el.removeAllListeners();
13363             this.view.el.remove();
13364             this.view.purgeListeners();
13365         }
13366         if(this.list){
13367             this.list.dom.innerHTML  = '';
13368         }
13369         
13370         if(this.store){
13371             this.store.un('beforeload', this.onBeforeLoad, this);
13372             this.store.un('load', this.onLoad, this);
13373             this.store.un('loadexception', this.onLoadException, this);
13374         }
13375         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13376     },
13377
13378     // private
13379     fireKey : function(e){
13380         if(e.isNavKeyPress() && !this.list.isVisible()){
13381             this.fireEvent("specialkey", this, e);
13382         }
13383     },
13384
13385     // private
13386     onResize: function(w, h){
13387 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13388 //        
13389 //        if(typeof w != 'number'){
13390 //            // we do not handle it!?!?
13391 //            return;
13392 //        }
13393 //        var tw = this.trigger.getWidth();
13394 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13395 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13396 //        var x = w - tw;
13397 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13398 //            
13399 //        //this.trigger.setStyle('left', x+'px');
13400 //        
13401 //        if(this.list && this.listWidth === undefined){
13402 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13403 //            this.list.setWidth(lw);
13404 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13405 //        }
13406         
13407     
13408         
13409     },
13410
13411     /**
13412      * Allow or prevent the user from directly editing the field text.  If false is passed,
13413      * the user will only be able to select from the items defined in the dropdown list.  This method
13414      * is the runtime equivalent of setting the 'editable' config option at config time.
13415      * @param {Boolean} value True to allow the user to directly edit the field text
13416      */
13417     setEditable : function(value){
13418         if(value == this.editable){
13419             return;
13420         }
13421         this.editable = value;
13422         if(!value){
13423             this.inputEl().dom.setAttribute('readOnly', true);
13424             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13425             this.inputEl().addClass('x-combo-noedit');
13426         }else{
13427             this.inputEl().dom.setAttribute('readOnly', false);
13428             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13429             this.inputEl().removeClass('x-combo-noedit');
13430         }
13431     },
13432
13433     // private
13434     
13435     onBeforeLoad : function(combo,opts){
13436         if(!this.hasFocus){
13437             return;
13438         }
13439          if (!opts.add) {
13440             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13441          }
13442         this.restrictHeight();
13443         this.selectedIndex = -1;
13444     },
13445
13446     // private
13447     onLoad : function(){
13448         
13449         this.hasQuery = false;
13450         
13451         if(!this.hasFocus){
13452             return;
13453         }
13454         
13455         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13456             this.loading.hide();
13457         }
13458         
13459         if(this.store.getCount() > 0){
13460             
13461             this.expand();
13462             this.restrictHeight();
13463             if(this.lastQuery == this.allQuery){
13464                 if(this.editable && !this.tickable){
13465                     this.inputEl().dom.select();
13466                 }
13467                 
13468                 if(
13469                     !this.selectByValue(this.value, true) &&
13470                     this.autoFocus && 
13471                     (
13472                         !this.store.lastOptions ||
13473                         typeof(this.store.lastOptions.add) == 'undefined' || 
13474                         this.store.lastOptions.add != true
13475                     )
13476                 ){
13477                     this.select(0, true);
13478                 }
13479             }else{
13480                 if(this.autoFocus){
13481                     this.selectNext();
13482                 }
13483                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13484                     this.taTask.delay(this.typeAheadDelay);
13485                 }
13486             }
13487         }else{
13488             this.onEmptyResults();
13489         }
13490         
13491         //this.el.focus();
13492     },
13493     // private
13494     onLoadException : function()
13495     {
13496         this.hasQuery = false;
13497         
13498         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13499             this.loading.hide();
13500         }
13501         
13502         if(this.tickable && this.editable){
13503             return;
13504         }
13505         
13506         this.collapse();
13507         // only causes errors at present
13508         //Roo.log(this.store.reader.jsonData);
13509         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13510             // fixme
13511             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13512         //}
13513         
13514         
13515     },
13516     // private
13517     onTypeAhead : function(){
13518         if(this.store.getCount() > 0){
13519             var r = this.store.getAt(0);
13520             var newValue = r.data[this.displayField];
13521             var len = newValue.length;
13522             var selStart = this.getRawValue().length;
13523             
13524             if(selStart != len){
13525                 this.setRawValue(newValue);
13526                 this.selectText(selStart, newValue.length);
13527             }
13528         }
13529     },
13530
13531     // private
13532     onSelect : function(record, index){
13533         
13534         if(this.fireEvent('beforeselect', this, record, index) !== false){
13535         
13536             this.setFromData(index > -1 ? record.data : false);
13537             
13538             this.collapse();
13539             this.fireEvent('select', this, record, index);
13540         }
13541     },
13542
13543     /**
13544      * Returns the currently selected field value or empty string if no value is set.
13545      * @return {String} value The selected value
13546      */
13547     getValue : function()
13548     {
13549         if(Roo.isIOS && this.useNativeIOS){
13550             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13551         }
13552         
13553         if(this.multiple){
13554             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13555         }
13556         
13557         if(this.valueField){
13558             return typeof this.value != 'undefined' ? this.value : '';
13559         }else{
13560             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13561         }
13562     },
13563     
13564     getRawValue : function()
13565     {
13566         if(Roo.isIOS && this.useNativeIOS){
13567             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13568         }
13569         
13570         var v = this.inputEl().getValue();
13571         
13572         return v;
13573     },
13574
13575     /**
13576      * Clears any text/value currently set in the field
13577      */
13578     clearValue : function(){
13579         
13580         if(this.hiddenField){
13581             this.hiddenField.dom.value = '';
13582         }
13583         this.value = '';
13584         this.setRawValue('');
13585         this.lastSelectionText = '';
13586         this.lastData = false;
13587         
13588         var close = this.closeTriggerEl();
13589         
13590         if(close){
13591             close.hide();
13592         }
13593         
13594         this.validate();
13595         
13596     },
13597
13598     /**
13599      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13600      * will be displayed in the field.  If the value does not match the data value of an existing item,
13601      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13602      * Otherwise the field will be blank (although the value will still be set).
13603      * @param {String} value The value to match
13604      */
13605     setValue : function(v)
13606     {
13607         if(Roo.isIOS && this.useNativeIOS){
13608             this.setIOSValue(v);
13609             return;
13610         }
13611         
13612         if(this.multiple){
13613             this.syncValue();
13614             return;
13615         }
13616         
13617         var text = v;
13618         if(this.valueField){
13619             var r = this.findRecord(this.valueField, v);
13620             if(r){
13621                 text = r.data[this.displayField];
13622             }else if(this.valueNotFoundText !== undefined){
13623                 text = this.valueNotFoundText;
13624             }
13625         }
13626         this.lastSelectionText = text;
13627         if(this.hiddenField){
13628             this.hiddenField.dom.value = v;
13629         }
13630         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13631         this.value = v;
13632         
13633         var close = this.closeTriggerEl();
13634         
13635         if(close){
13636             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13637         }
13638         
13639         this.validate();
13640     },
13641     /**
13642      * @property {Object} the last set data for the element
13643      */
13644     
13645     lastData : false,
13646     /**
13647      * Sets the value of the field based on a object which is related to the record format for the store.
13648      * @param {Object} value the value to set as. or false on reset?
13649      */
13650     setFromData : function(o){
13651         
13652         if(this.multiple){
13653             this.addItem(o);
13654             return;
13655         }
13656             
13657         var dv = ''; // display value
13658         var vv = ''; // value value..
13659         this.lastData = o;
13660         if (this.displayField) {
13661             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13662         } else {
13663             // this is an error condition!!!
13664             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13665         }
13666         
13667         if(this.valueField){
13668             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13669         }
13670         
13671         var close = this.closeTriggerEl();
13672         
13673         if(close){
13674             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13675         }
13676         
13677         if(this.hiddenField){
13678             this.hiddenField.dom.value = vv;
13679             
13680             this.lastSelectionText = dv;
13681             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13682             this.value = vv;
13683             return;
13684         }
13685         // no hidden field.. - we store the value in 'value', but still display
13686         // display field!!!!
13687         this.lastSelectionText = dv;
13688         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13689         this.value = vv;
13690         
13691         
13692         
13693     },
13694     // private
13695     reset : function(){
13696         // overridden so that last data is reset..
13697         
13698         if(this.multiple){
13699             this.clearItem();
13700             return;
13701         }
13702         
13703         this.setValue(this.originalValue);
13704         //this.clearInvalid();
13705         this.lastData = false;
13706         if (this.view) {
13707             this.view.clearSelections();
13708         }
13709         
13710         this.validate();
13711     },
13712     // private
13713     findRecord : function(prop, value){
13714         var record;
13715         if(this.store.getCount() > 0){
13716             this.store.each(function(r){
13717                 if(r.data[prop] == value){
13718                     record = r;
13719                     return false;
13720                 }
13721                 return true;
13722             });
13723         }
13724         return record;
13725     },
13726     
13727     getName: function()
13728     {
13729         // returns hidden if it's set..
13730         if (!this.rendered) {return ''};
13731         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13732         
13733     },
13734     // private
13735     onViewMove : function(e, t){
13736         this.inKeyMode = false;
13737     },
13738
13739     // private
13740     onViewOver : function(e, t){
13741         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13742             return;
13743         }
13744         var item = this.view.findItemFromChild(t);
13745         
13746         if(item){
13747             var index = this.view.indexOf(item);
13748             this.select(index, false);
13749         }
13750     },
13751
13752     // private
13753     onViewClick : function(view, doFocus, el, e)
13754     {
13755         var index = this.view.getSelectedIndexes()[0];
13756         
13757         var r = this.store.getAt(index);
13758         
13759         if(this.tickable){
13760             
13761             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13762                 return;
13763             }
13764             
13765             var rm = false;
13766             var _this = this;
13767             
13768             Roo.each(this.tickItems, function(v,k){
13769                 
13770                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13771                     Roo.log(v);
13772                     _this.tickItems.splice(k, 1);
13773                     
13774                     if(typeof(e) == 'undefined' && view == false){
13775                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13776                     }
13777                     
13778                     rm = true;
13779                     return;
13780                 }
13781             });
13782             
13783             if(rm){
13784                 return;
13785             }
13786             
13787             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13788                 this.tickItems.push(r.data);
13789             }
13790             
13791             if(typeof(e) == 'undefined' && view == false){
13792                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13793             }
13794                     
13795             return;
13796         }
13797         
13798         if(r){
13799             this.onSelect(r, index);
13800         }
13801         if(doFocus !== false && !this.blockFocus){
13802             this.inputEl().focus();
13803         }
13804     },
13805
13806     // private
13807     restrictHeight : function(){
13808         //this.innerList.dom.style.height = '';
13809         //var inner = this.innerList.dom;
13810         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13811         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13812         //this.list.beginUpdate();
13813         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13814         this.list.alignTo(this.inputEl(), this.listAlign);
13815         this.list.alignTo(this.inputEl(), this.listAlign);
13816         //this.list.endUpdate();
13817     },
13818
13819     // private
13820     onEmptyResults : function(){
13821         
13822         if(this.tickable && this.editable){
13823             this.restrictHeight();
13824             return;
13825         }
13826         
13827         this.collapse();
13828     },
13829
13830     /**
13831      * Returns true if the dropdown list is expanded, else false.
13832      */
13833     isExpanded : function(){
13834         return this.list.isVisible();
13835     },
13836
13837     /**
13838      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13839      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13840      * @param {String} value The data value of the item to select
13841      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13842      * selected item if it is not currently in view (defaults to true)
13843      * @return {Boolean} True if the value matched an item in the list, else false
13844      */
13845     selectByValue : function(v, scrollIntoView){
13846         if(v !== undefined && v !== null){
13847             var r = this.findRecord(this.valueField || this.displayField, v);
13848             if(r){
13849                 this.select(this.store.indexOf(r), scrollIntoView);
13850                 return true;
13851             }
13852         }
13853         return false;
13854     },
13855
13856     /**
13857      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13858      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13859      * @param {Number} index The zero-based index of the list item to select
13860      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13861      * selected item if it is not currently in view (defaults to true)
13862      */
13863     select : function(index, scrollIntoView){
13864         this.selectedIndex = index;
13865         this.view.select(index);
13866         if(scrollIntoView !== false){
13867             var el = this.view.getNode(index);
13868             /*
13869              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13870              */
13871             if(el){
13872                 this.list.scrollChildIntoView(el, false);
13873             }
13874         }
13875     },
13876
13877     // private
13878     selectNext : function(){
13879         var ct = this.store.getCount();
13880         if(ct > 0){
13881             if(this.selectedIndex == -1){
13882                 this.select(0);
13883             }else if(this.selectedIndex < ct-1){
13884                 this.select(this.selectedIndex+1);
13885             }
13886         }
13887     },
13888
13889     // private
13890     selectPrev : function(){
13891         var ct = this.store.getCount();
13892         if(ct > 0){
13893             if(this.selectedIndex == -1){
13894                 this.select(0);
13895             }else if(this.selectedIndex != 0){
13896                 this.select(this.selectedIndex-1);
13897             }
13898         }
13899     },
13900
13901     // private
13902     onKeyUp : function(e){
13903         if(this.editable !== false && !e.isSpecialKey()){
13904             this.lastKey = e.getKey();
13905             this.dqTask.delay(this.queryDelay);
13906         }
13907     },
13908
13909     // private
13910     validateBlur : function(){
13911         return !this.list || !this.list.isVisible();   
13912     },
13913
13914     // private
13915     initQuery : function(){
13916         
13917         var v = this.getRawValue();
13918         
13919         if(this.tickable && this.editable){
13920             v = this.tickableInputEl().getValue();
13921         }
13922         
13923         this.doQuery(v);
13924     },
13925
13926     // private
13927     doForce : function(){
13928         if(this.inputEl().dom.value.length > 0){
13929             this.inputEl().dom.value =
13930                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13931              
13932         }
13933     },
13934
13935     /**
13936      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13937      * query allowing the query action to be canceled if needed.
13938      * @param {String} query The SQL query to execute
13939      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13940      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13941      * saved in the current store (defaults to false)
13942      */
13943     doQuery : function(q, forceAll){
13944         
13945         if(q === undefined || q === null){
13946             q = '';
13947         }
13948         var qe = {
13949             query: q,
13950             forceAll: forceAll,
13951             combo: this,
13952             cancel:false
13953         };
13954         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13955             return false;
13956         }
13957         q = qe.query;
13958         
13959         forceAll = qe.forceAll;
13960         if(forceAll === true || (q.length >= this.minChars)){
13961             
13962             this.hasQuery = true;
13963             
13964             if(this.lastQuery != q || this.alwaysQuery){
13965                 this.lastQuery = q;
13966                 if(this.mode == 'local'){
13967                     this.selectedIndex = -1;
13968                     if(forceAll){
13969                         this.store.clearFilter();
13970                     }else{
13971                         
13972                         if(this.specialFilter){
13973                             this.fireEvent('specialfilter', this);
13974                             this.onLoad();
13975                             return;
13976                         }
13977                         
13978                         this.store.filter(this.displayField, q);
13979                     }
13980                     
13981                     this.store.fireEvent("datachanged", this.store);
13982                     
13983                     this.onLoad();
13984                     
13985                     
13986                 }else{
13987                     
13988                     this.store.baseParams[this.queryParam] = q;
13989                     
13990                     var options = {params : this.getParams(q)};
13991                     
13992                     if(this.loadNext){
13993                         options.add = true;
13994                         options.params.start = this.page * this.pageSize;
13995                     }
13996                     
13997                     this.store.load(options);
13998                     
13999                     /*
14000                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14001                      *  we should expand the list on onLoad
14002                      *  so command out it
14003                      */
14004 //                    this.expand();
14005                 }
14006             }else{
14007                 this.selectedIndex = -1;
14008                 this.onLoad();   
14009             }
14010         }
14011         
14012         this.loadNext = false;
14013     },
14014     
14015     // private
14016     getParams : function(q){
14017         var p = {};
14018         //p[this.queryParam] = q;
14019         
14020         if(this.pageSize){
14021             p.start = 0;
14022             p.limit = this.pageSize;
14023         }
14024         return p;
14025     },
14026
14027     /**
14028      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14029      */
14030     collapse : function(){
14031         if(!this.isExpanded()){
14032             return;
14033         }
14034         
14035         this.list.hide();
14036         
14037         this.hasFocus = false;
14038         
14039         if(this.tickable){
14040             this.okBtn.hide();
14041             this.cancelBtn.hide();
14042             this.trigger.show();
14043             
14044             if(this.editable){
14045                 this.tickableInputEl().dom.value = '';
14046                 this.tickableInputEl().blur();
14047             }
14048             
14049         }
14050         
14051         Roo.get(document).un('mousedown', this.collapseIf, this);
14052         Roo.get(document).un('mousewheel', this.collapseIf, this);
14053         if (!this.editable) {
14054             Roo.get(document).un('keydown', this.listKeyPress, this);
14055         }
14056         this.fireEvent('collapse', this);
14057         
14058         this.validate();
14059     },
14060
14061     // private
14062     collapseIf : function(e){
14063         var in_combo  = e.within(this.el);
14064         var in_list =  e.within(this.list);
14065         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14066         
14067         if (in_combo || in_list || is_list) {
14068             //e.stopPropagation();
14069             return;
14070         }
14071         
14072         if(this.tickable){
14073             this.onTickableFooterButtonClick(e, false, false);
14074         }
14075
14076         this.collapse();
14077         
14078     },
14079
14080     /**
14081      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14082      */
14083     expand : function(){
14084        
14085         if(this.isExpanded() || !this.hasFocus){
14086             return;
14087         }
14088         
14089         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14090         this.list.setWidth(lw);
14091         
14092         Roo.log('expand');
14093         
14094         this.list.show();
14095         
14096         this.restrictHeight();
14097         
14098         if(this.tickable){
14099             
14100             this.tickItems = Roo.apply([], this.item);
14101             
14102             this.okBtn.show();
14103             this.cancelBtn.show();
14104             this.trigger.hide();
14105             
14106             if(this.editable){
14107                 this.tickableInputEl().focus();
14108             }
14109             
14110         }
14111         
14112         Roo.get(document).on('mousedown', this.collapseIf, this);
14113         Roo.get(document).on('mousewheel', this.collapseIf, this);
14114         if (!this.editable) {
14115             Roo.get(document).on('keydown', this.listKeyPress, this);
14116         }
14117         
14118         this.fireEvent('expand', this);
14119     },
14120
14121     // private
14122     // Implements the default empty TriggerField.onTriggerClick function
14123     onTriggerClick : function(e)
14124     {
14125         Roo.log('trigger click');
14126         
14127         if(this.disabled || !this.triggerList){
14128             return;
14129         }
14130         
14131         this.page = 0;
14132         this.loadNext = false;
14133         
14134         if(this.isExpanded()){
14135             this.collapse();
14136             if (!this.blockFocus) {
14137                 this.inputEl().focus();
14138             }
14139             
14140         }else {
14141             this.hasFocus = true;
14142             if(this.triggerAction == 'all') {
14143                 this.doQuery(this.allQuery, true);
14144             } else {
14145                 this.doQuery(this.getRawValue());
14146             }
14147             if (!this.blockFocus) {
14148                 this.inputEl().focus();
14149             }
14150         }
14151     },
14152     
14153     onTickableTriggerClick : function(e)
14154     {
14155         if(this.disabled){
14156             return;
14157         }
14158         
14159         this.page = 0;
14160         this.loadNext = false;
14161         this.hasFocus = true;
14162         
14163         if(this.triggerAction == 'all') {
14164             this.doQuery(this.allQuery, true);
14165         } else {
14166             this.doQuery(this.getRawValue());
14167         }
14168     },
14169     
14170     onSearchFieldClick : function(e)
14171     {
14172         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14173             this.onTickableFooterButtonClick(e, false, false);
14174             return;
14175         }
14176         
14177         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14178             return;
14179         }
14180         
14181         this.page = 0;
14182         this.loadNext = false;
14183         this.hasFocus = true;
14184         
14185         if(this.triggerAction == 'all') {
14186             this.doQuery(this.allQuery, true);
14187         } else {
14188             this.doQuery(this.getRawValue());
14189         }
14190     },
14191     
14192     listKeyPress : function(e)
14193     {
14194         //Roo.log('listkeypress');
14195         // scroll to first matching element based on key pres..
14196         if (e.isSpecialKey()) {
14197             return false;
14198         }
14199         var k = String.fromCharCode(e.getKey()).toUpperCase();
14200         //Roo.log(k);
14201         var match  = false;
14202         var csel = this.view.getSelectedNodes();
14203         var cselitem = false;
14204         if (csel.length) {
14205             var ix = this.view.indexOf(csel[0]);
14206             cselitem  = this.store.getAt(ix);
14207             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14208                 cselitem = false;
14209             }
14210             
14211         }
14212         
14213         this.store.each(function(v) { 
14214             if (cselitem) {
14215                 // start at existing selection.
14216                 if (cselitem.id == v.id) {
14217                     cselitem = false;
14218                 }
14219                 return true;
14220             }
14221                 
14222             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14223                 match = this.store.indexOf(v);
14224                 return false;
14225             }
14226             return true;
14227         }, this);
14228         
14229         if (match === false) {
14230             return true; // no more action?
14231         }
14232         // scroll to?
14233         this.view.select(match);
14234         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14235         sn.scrollIntoView(sn.dom.parentNode, false);
14236     },
14237     
14238     onViewScroll : function(e, t){
14239         
14240         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){
14241             return;
14242         }
14243         
14244         this.hasQuery = true;
14245         
14246         this.loading = this.list.select('.loading', true).first();
14247         
14248         if(this.loading === null){
14249             this.list.createChild({
14250                 tag: 'div',
14251                 cls: 'loading roo-select2-more-results roo-select2-active',
14252                 html: 'Loading more results...'
14253             });
14254             
14255             this.loading = this.list.select('.loading', true).first();
14256             
14257             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14258             
14259             this.loading.hide();
14260         }
14261         
14262         this.loading.show();
14263         
14264         var _combo = this;
14265         
14266         this.page++;
14267         this.loadNext = true;
14268         
14269         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14270         
14271         return;
14272     },
14273     
14274     addItem : function(o)
14275     {   
14276         var dv = ''; // display value
14277         
14278         if (this.displayField) {
14279             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14280         } else {
14281             // this is an error condition!!!
14282             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14283         }
14284         
14285         if(!dv.length){
14286             return;
14287         }
14288         
14289         var choice = this.choices.createChild({
14290             tag: 'li',
14291             cls: 'roo-select2-search-choice',
14292             cn: [
14293                 {
14294                     tag: 'div',
14295                     html: dv
14296                 },
14297                 {
14298                     tag: 'a',
14299                     href: '#',
14300                     cls: 'roo-select2-search-choice-close',
14301                     tabindex: '-1'
14302                 }
14303             ]
14304             
14305         }, this.searchField);
14306         
14307         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14308         
14309         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14310         
14311         this.item.push(o);
14312         
14313         this.lastData = o;
14314         
14315         this.syncValue();
14316         
14317         this.inputEl().dom.value = '';
14318         
14319         this.validate();
14320     },
14321     
14322     onRemoveItem : function(e, _self, o)
14323     {
14324         e.preventDefault();
14325         
14326         this.lastItem = Roo.apply([], this.item);
14327         
14328         var index = this.item.indexOf(o.data) * 1;
14329         
14330         if( index < 0){
14331             Roo.log('not this item?!');
14332             return;
14333         }
14334         
14335         this.item.splice(index, 1);
14336         o.item.remove();
14337         
14338         this.syncValue();
14339         
14340         this.fireEvent('remove', this, e);
14341         
14342         this.validate();
14343         
14344     },
14345     
14346     syncValue : function()
14347     {
14348         if(!this.item.length){
14349             this.clearValue();
14350             return;
14351         }
14352             
14353         var value = [];
14354         var _this = this;
14355         Roo.each(this.item, function(i){
14356             if(_this.valueField){
14357                 value.push(i[_this.valueField]);
14358                 return;
14359             }
14360
14361             value.push(i);
14362         });
14363
14364         this.value = value.join(',');
14365
14366         if(this.hiddenField){
14367             this.hiddenField.dom.value = this.value;
14368         }
14369         
14370         this.store.fireEvent("datachanged", this.store);
14371         
14372         this.validate();
14373     },
14374     
14375     clearItem : function()
14376     {
14377         if(!this.multiple){
14378             return;
14379         }
14380         
14381         this.item = [];
14382         
14383         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14384            c.remove();
14385         });
14386         
14387         this.syncValue();
14388         
14389         this.validate();
14390         
14391         if(this.tickable && !Roo.isTouch){
14392             this.view.refresh();
14393         }
14394     },
14395     
14396     inputEl: function ()
14397     {
14398         if(Roo.isIOS && this.useNativeIOS){
14399             return this.el.select('select.roo-ios-select', true).first();
14400         }
14401         
14402         if(Roo.isTouch && this.mobileTouchView){
14403             return this.el.select('input.form-control',true).first();
14404         }
14405         
14406         if(this.tickable){
14407             return this.searchField;
14408         }
14409         
14410         return this.el.select('input.form-control',true).first();
14411     },
14412     
14413     onTickableFooterButtonClick : function(e, btn, el)
14414     {
14415         e.preventDefault();
14416         
14417         this.lastItem = Roo.apply([], this.item);
14418         
14419         if(btn && btn.name == 'cancel'){
14420             this.tickItems = Roo.apply([], this.item);
14421             this.collapse();
14422             return;
14423         }
14424         
14425         this.clearItem();
14426         
14427         var _this = this;
14428         
14429         Roo.each(this.tickItems, function(o){
14430             _this.addItem(o);
14431         });
14432         
14433         this.collapse();
14434         
14435     },
14436     
14437     validate : function()
14438     {
14439         var v = this.getRawValue();
14440         
14441         if(this.multiple){
14442             v = this.getValue();
14443         }
14444         
14445         if(this.disabled || this.allowBlank || v.length){
14446             this.markValid();
14447             return true;
14448         }
14449         
14450         this.markInvalid();
14451         return false;
14452     },
14453     
14454     tickableInputEl : function()
14455     {
14456         if(!this.tickable || !this.editable){
14457             return this.inputEl();
14458         }
14459         
14460         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14461     },
14462     
14463     
14464     getAutoCreateTouchView : function()
14465     {
14466         var id = Roo.id();
14467         
14468         var cfg = {
14469             cls: 'form-group' //input-group
14470         };
14471         
14472         var input =  {
14473             tag: 'input',
14474             id : id,
14475             type : this.inputType,
14476             cls : 'form-control x-combo-noedit',
14477             autocomplete: 'new-password',
14478             placeholder : this.placeholder || '',
14479             readonly : true
14480         };
14481         
14482         if (this.name) {
14483             input.name = this.name;
14484         }
14485         
14486         if (this.size) {
14487             input.cls += ' input-' + this.size;
14488         }
14489         
14490         if (this.disabled) {
14491             input.disabled = true;
14492         }
14493         
14494         var inputblock = {
14495             cls : '',
14496             cn : [
14497                 input
14498             ]
14499         };
14500         
14501         if(this.before){
14502             inputblock.cls += ' input-group';
14503             
14504             inputblock.cn.unshift({
14505                 tag :'span',
14506                 cls : 'input-group-addon',
14507                 html : this.before
14508             });
14509         }
14510         
14511         if(this.removable && !this.multiple){
14512             inputblock.cls += ' roo-removable';
14513             
14514             inputblock.cn.push({
14515                 tag: 'button',
14516                 html : 'x',
14517                 cls : 'roo-combo-removable-btn close'
14518             });
14519         }
14520
14521         if(this.hasFeedback && !this.allowBlank){
14522             
14523             inputblock.cls += ' has-feedback';
14524             
14525             inputblock.cn.push({
14526                 tag: 'span',
14527                 cls: 'glyphicon form-control-feedback'
14528             });
14529             
14530         }
14531         
14532         if (this.after) {
14533             
14534             inputblock.cls += (this.before) ? '' : ' input-group';
14535             
14536             inputblock.cn.push({
14537                 tag :'span',
14538                 cls : 'input-group-addon',
14539                 html : this.after
14540             });
14541         }
14542
14543         var box = {
14544             tag: 'div',
14545             cn: [
14546                 {
14547                     tag: 'input',
14548                     type : 'hidden',
14549                     cls: 'form-hidden-field'
14550                 },
14551                 inputblock
14552             ]
14553             
14554         };
14555         
14556         if(this.multiple){
14557             box = {
14558                 tag: 'div',
14559                 cn: [
14560                     {
14561                         tag: 'input',
14562                         type : 'hidden',
14563                         cls: 'form-hidden-field'
14564                     },
14565                     {
14566                         tag: 'ul',
14567                         cls: 'roo-select2-choices',
14568                         cn:[
14569                             {
14570                                 tag: 'li',
14571                                 cls: 'roo-select2-search-field',
14572                                 cn: [
14573
14574                                     inputblock
14575                                 ]
14576                             }
14577                         ]
14578                     }
14579                 ]
14580             }
14581         };
14582         
14583         var combobox = {
14584             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14585             cn: [
14586                 box
14587             ]
14588         };
14589         
14590         if(!this.multiple && this.showToggleBtn){
14591             
14592             var caret = {
14593                         tag: 'span',
14594                         cls: 'caret'
14595             };
14596             
14597             if (this.caret != false) {
14598                 caret = {
14599                      tag: 'i',
14600                      cls: 'fa fa-' + this.caret
14601                 };
14602                 
14603             }
14604             
14605             combobox.cn.push({
14606                 tag :'span',
14607                 cls : 'input-group-addon btn dropdown-toggle',
14608                 cn : [
14609                     caret,
14610                     {
14611                         tag: 'span',
14612                         cls: 'combobox-clear',
14613                         cn  : [
14614                             {
14615                                 tag : 'i',
14616                                 cls: 'icon-remove'
14617                             }
14618                         ]
14619                     }
14620                 ]
14621
14622             })
14623         }
14624         
14625         if(this.multiple){
14626             combobox.cls += ' roo-select2-container-multi';
14627         }
14628         
14629         var align = this.labelAlign || this.parentLabelAlign();
14630         
14631         if (align ==='left' && this.fieldLabel.length) {
14632
14633             cfg.cn = [
14634                 {
14635                    tag : 'i',
14636                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14637                    tooltip : 'This field is required'
14638                 },
14639                 {
14640                     tag: 'label',
14641                     cls : 'control-label',
14642                     html : this.fieldLabel
14643
14644                 },
14645                 {
14646                     cls : '', 
14647                     cn: [
14648                         combobox
14649                     ]
14650                 }
14651             ];
14652             
14653             var labelCfg = cfg.cn[1];
14654             var contentCfg = cfg.cn[2];
14655             
14656
14657             if(this.indicatorpos == 'right'){
14658                 cfg.cn = [
14659                     {
14660                         tag: 'label',
14661                         cls : 'control-label',
14662                         html : this.fieldLabel,
14663                         cn : [
14664                             {
14665                                tag : 'i',
14666                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14667                                tooltip : 'This field is required'
14668                             }
14669                         ]
14670                     },
14671                     {
14672                         cls : '', 
14673                         cn: [
14674                             combobox
14675                         ]
14676                     }
14677                 ];
14678             }
14679             
14680             labelCfg = cfg.cn[0];
14681             contentCfg = cfg.cn[2];
14682             
14683             if(this.labelWidth > 12){
14684                 labelCfg.style = "width: " + this.labelWidth + 'px';
14685             }
14686             
14687             if(this.labelWidth < 13 && this.labelmd == 0){
14688                 this.labelmd = this.labelWidth;
14689             }
14690             
14691             if(this.labellg > 0){
14692                 labelCfg.cls += ' col-lg-' + this.labellg;
14693                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14694             }
14695             
14696             if(this.labelmd > 0){
14697                 labelCfg.cls += ' col-md-' + this.labelmd;
14698                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14699             }
14700             
14701             if(this.labelsm > 0){
14702                 labelCfg.cls += ' col-sm-' + this.labelsm;
14703                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14704             }
14705             
14706             if(this.labelxs > 0){
14707                 labelCfg.cls += ' col-xs-' + this.labelxs;
14708                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14709             }
14710                 
14711                 
14712         } else if ( this.fieldLabel.length) {
14713             cfg.cn = [
14714                 {
14715                    tag : 'i',
14716                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14717                    tooltip : 'This field is required'
14718                 },
14719                 {
14720                     tag: 'label',
14721                     cls : 'control-label',
14722                     html : this.fieldLabel
14723
14724                 },
14725                 {
14726                     cls : '', 
14727                     cn: [
14728                         combobox
14729                     ]
14730                 }
14731             ];
14732             
14733             if(this.indicatorpos == 'right'){
14734                 cfg.cn = [
14735                     {
14736                         tag: 'label',
14737                         cls : 'control-label',
14738                         html : this.fieldLabel,
14739                         cn : [
14740                             {
14741                                tag : 'i',
14742                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14743                                tooltip : 'This field is required'
14744                             }
14745                         ]
14746                     },
14747                     {
14748                         cls : '', 
14749                         cn: [
14750                             combobox
14751                         ]
14752                     }
14753                 ];
14754             }
14755         } else {
14756             cfg.cn = combobox;    
14757         }
14758         
14759         
14760         var settings = this;
14761         
14762         ['xs','sm','md','lg'].map(function(size){
14763             if (settings[size]) {
14764                 cfg.cls += ' col-' + size + '-' + settings[size];
14765             }
14766         });
14767         
14768         return cfg;
14769     },
14770     
14771     initTouchView : function()
14772     {
14773         this.renderTouchView();
14774         
14775         this.touchViewEl.on('scroll', function(){
14776             this.el.dom.scrollTop = 0;
14777         }, this);
14778         
14779         this.originalValue = this.getValue();
14780         
14781         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14782         
14783         this.inputEl().on("click", this.showTouchView, this);
14784         if (this.triggerEl) {
14785             this.triggerEl.on("click", this.showTouchView, this);
14786         }
14787         
14788         
14789         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14790         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14791         
14792         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14793         
14794         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14795         this.store.on('load', this.onTouchViewLoad, this);
14796         this.store.on('loadexception', this.onTouchViewLoadException, this);
14797         
14798         if(this.hiddenName){
14799             
14800             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14801             
14802             this.hiddenField.dom.value =
14803                 this.hiddenValue !== undefined ? this.hiddenValue :
14804                 this.value !== undefined ? this.value : '';
14805         
14806             this.el.dom.removeAttribute('name');
14807             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14808         }
14809         
14810         if(this.multiple){
14811             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14812             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14813         }
14814         
14815         if(this.removable && !this.multiple){
14816             var close = this.closeTriggerEl();
14817             if(close){
14818                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14819                 close.on('click', this.removeBtnClick, this, close);
14820             }
14821         }
14822         /*
14823          * fix the bug in Safari iOS8
14824          */
14825         this.inputEl().on("focus", function(e){
14826             document.activeElement.blur();
14827         }, this);
14828         
14829         return;
14830         
14831         
14832     },
14833     
14834     renderTouchView : function()
14835     {
14836         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14837         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14838         
14839         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14840         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14841         
14842         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14843         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14844         this.touchViewBodyEl.setStyle('overflow', 'auto');
14845         
14846         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14847         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14848         
14849         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14850         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14851         
14852     },
14853     
14854     showTouchView : function()
14855     {
14856         if(this.disabled){
14857             return;
14858         }
14859         
14860         this.touchViewHeaderEl.hide();
14861
14862         if(this.modalTitle.length){
14863             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14864             this.touchViewHeaderEl.show();
14865         }
14866
14867         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14868         this.touchViewEl.show();
14869
14870         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14871         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14872                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14873
14874         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14875
14876         if(this.modalTitle.length){
14877             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14878         }
14879         
14880         this.touchViewBodyEl.setHeight(bodyHeight);
14881
14882         if(this.animate){
14883             var _this = this;
14884             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14885         }else{
14886             this.touchViewEl.addClass('in');
14887         }
14888
14889         this.doTouchViewQuery();
14890         
14891     },
14892     
14893     hideTouchView : function()
14894     {
14895         this.touchViewEl.removeClass('in');
14896
14897         if(this.animate){
14898             var _this = this;
14899             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14900         }else{
14901             this.touchViewEl.setStyle('display', 'none');
14902         }
14903         
14904     },
14905     
14906     setTouchViewValue : function()
14907     {
14908         if(this.multiple){
14909             this.clearItem();
14910         
14911             var _this = this;
14912
14913             Roo.each(this.tickItems, function(o){
14914                 this.addItem(o);
14915             }, this);
14916         }
14917         
14918         this.hideTouchView();
14919     },
14920     
14921     doTouchViewQuery : function()
14922     {
14923         var qe = {
14924             query: '',
14925             forceAll: true,
14926             combo: this,
14927             cancel:false
14928         };
14929         
14930         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14931             return false;
14932         }
14933         
14934         if(!this.alwaysQuery || this.mode == 'local'){
14935             this.onTouchViewLoad();
14936             return;
14937         }
14938         
14939         this.store.load();
14940     },
14941     
14942     onTouchViewBeforeLoad : function(combo,opts)
14943     {
14944         return;
14945     },
14946
14947     // private
14948     onTouchViewLoad : function()
14949     {
14950         if(this.store.getCount() < 1){
14951             this.onTouchViewEmptyResults();
14952             return;
14953         }
14954         
14955         this.clearTouchView();
14956         
14957         var rawValue = this.getRawValue();
14958         
14959         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14960         
14961         this.tickItems = [];
14962         
14963         this.store.data.each(function(d, rowIndex){
14964             var row = this.touchViewListGroup.createChild(template);
14965             
14966             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14967                 row.addClass(d.data.cls);
14968             }
14969             
14970             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14971                 var cfg = {
14972                     data : d.data,
14973                     html : d.data[this.displayField]
14974                 };
14975                 
14976                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14977                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14978                 }
14979             }
14980             row.removeClass('selected');
14981             if(!this.multiple && this.valueField &&
14982                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14983             {
14984                 // radio buttons..
14985                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14986                 row.addClass('selected');
14987             }
14988             
14989             if(this.multiple && this.valueField &&
14990                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14991             {
14992                 
14993                 // checkboxes...
14994                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14995                 this.tickItems.push(d.data);
14996             }
14997             
14998             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14999             
15000         }, this);
15001         
15002         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15003         
15004         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15005
15006         if(this.modalTitle.length){
15007             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15008         }
15009
15010         var listHeight = this.touchViewListGroup.getHeight();
15011         
15012         var _this = this;
15013         
15014         if(firstChecked && listHeight > bodyHeight){
15015             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15016         }
15017         
15018     },
15019     
15020     onTouchViewLoadException : function()
15021     {
15022         this.hideTouchView();
15023     },
15024     
15025     onTouchViewEmptyResults : function()
15026     {
15027         this.clearTouchView();
15028         
15029         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15030         
15031         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15032         
15033     },
15034     
15035     clearTouchView : function()
15036     {
15037         this.touchViewListGroup.dom.innerHTML = '';
15038     },
15039     
15040     onTouchViewClick : function(e, el, o)
15041     {
15042         e.preventDefault();
15043         
15044         var row = o.row;
15045         var rowIndex = o.rowIndex;
15046         
15047         var r = this.store.getAt(rowIndex);
15048         
15049         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15050             
15051             if(!this.multiple){
15052                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15053                     c.dom.removeAttribute('checked');
15054                 }, this);
15055
15056                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15057
15058                 this.setFromData(r.data);
15059
15060                 var close = this.closeTriggerEl();
15061
15062                 if(close){
15063                     close.show();
15064                 }
15065
15066                 this.hideTouchView();
15067
15068                 this.fireEvent('select', this, r, rowIndex);
15069
15070                 return;
15071             }
15072
15073             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15074                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15075                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15076                 return;
15077             }
15078
15079             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15080             this.addItem(r.data);
15081             this.tickItems.push(r.data);
15082         }
15083     },
15084     
15085     getAutoCreateNativeIOS : function()
15086     {
15087         var cfg = {
15088             cls: 'form-group' //input-group,
15089         };
15090         
15091         var combobox =  {
15092             tag: 'select',
15093             cls : 'roo-ios-select'
15094         };
15095         
15096         if (this.name) {
15097             combobox.name = this.name;
15098         }
15099         
15100         if (this.disabled) {
15101             combobox.disabled = true;
15102         }
15103         
15104         var settings = this;
15105         
15106         ['xs','sm','md','lg'].map(function(size){
15107             if (settings[size]) {
15108                 cfg.cls += ' col-' + size + '-' + settings[size];
15109             }
15110         });
15111         
15112         cfg.cn = combobox;
15113         
15114         return cfg;
15115         
15116     },
15117     
15118     initIOSView : function()
15119     {
15120         this.store.on('load', this.onIOSViewLoad, this);
15121         
15122         return;
15123     },
15124     
15125     onIOSViewLoad : function()
15126     {
15127         if(this.store.getCount() < 1){
15128             return;
15129         }
15130         
15131         this.clearIOSView();
15132         
15133         if(this.allowBlank) {
15134             
15135             var default_text = '-- SELECT --';
15136             
15137             var opt = this.inputEl().createChild({
15138                 tag: 'option',
15139                 value : 0,
15140                 html : default_text
15141             });
15142             
15143             var o = {};
15144             o[this.valueField] = 0;
15145             o[this.displayField] = default_text;
15146             
15147             this.ios_options.push({
15148                 data : o,
15149                 el : opt
15150             });
15151             
15152         }
15153         
15154         this.store.data.each(function(d, rowIndex){
15155             
15156             var html = '';
15157             
15158             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15159                 html = d.data[this.displayField];
15160             }
15161             
15162             var value = '';
15163             
15164             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15165                 value = d.data[this.valueField];
15166             }
15167             
15168             var option = {
15169                 tag: 'option',
15170                 value : value,
15171                 html : html
15172             };
15173             
15174             if(this.value == d.data[this.valueField]){
15175                 option['selected'] = true;
15176             }
15177             
15178             var opt = this.inputEl().createChild(option);
15179             
15180             this.ios_options.push({
15181                 data : d.data,
15182                 el : opt
15183             });
15184             
15185         }, this);
15186         
15187         this.inputEl().on('change', function(){
15188            this.fireEvent('select', this);
15189         }, this);
15190         
15191     },
15192     
15193     clearIOSView: function()
15194     {
15195         this.inputEl().dom.innerHTML = '';
15196         
15197         this.ios_options = [];
15198     },
15199     
15200     setIOSValue: function(v)
15201     {
15202         this.value = v;
15203         
15204         if(!this.ios_options){
15205             return;
15206         }
15207         
15208         Roo.each(this.ios_options, function(opts){
15209            
15210            opts.el.dom.removeAttribute('selected');
15211            
15212            if(opts.data[this.valueField] != v){
15213                return;
15214            }
15215            
15216            opts.el.dom.setAttribute('selected', true);
15217            
15218         }, this);
15219     }
15220
15221     /** 
15222     * @cfg {Boolean} grow 
15223     * @hide 
15224     */
15225     /** 
15226     * @cfg {Number} growMin 
15227     * @hide 
15228     */
15229     /** 
15230     * @cfg {Number} growMax 
15231     * @hide 
15232     */
15233     /**
15234      * @hide
15235      * @method autoSize
15236      */
15237 });
15238
15239 Roo.apply(Roo.bootstrap.ComboBox,  {
15240     
15241     header : {
15242         tag: 'div',
15243         cls: 'modal-header',
15244         cn: [
15245             {
15246                 tag: 'h4',
15247                 cls: 'modal-title'
15248             }
15249         ]
15250     },
15251     
15252     body : {
15253         tag: 'div',
15254         cls: 'modal-body',
15255         cn: [
15256             {
15257                 tag: 'ul',
15258                 cls: 'list-group'
15259             }
15260         ]
15261     },
15262     
15263     listItemRadio : {
15264         tag: 'li',
15265         cls: 'list-group-item',
15266         cn: [
15267             {
15268                 tag: 'span',
15269                 cls: 'roo-combobox-list-group-item-value'
15270             },
15271             {
15272                 tag: 'div',
15273                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15274                 cn: [
15275                     {
15276                         tag: 'input',
15277                         type: 'radio'
15278                     },
15279                     {
15280                         tag: 'label'
15281                     }
15282                 ]
15283             }
15284         ]
15285     },
15286     
15287     listItemCheckbox : {
15288         tag: 'li',
15289         cls: 'list-group-item',
15290         cn: [
15291             {
15292                 tag: 'span',
15293                 cls: 'roo-combobox-list-group-item-value'
15294             },
15295             {
15296                 tag: 'div',
15297                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15298                 cn: [
15299                     {
15300                         tag: 'input',
15301                         type: 'checkbox'
15302                     },
15303                     {
15304                         tag: 'label'
15305                     }
15306                 ]
15307             }
15308         ]
15309     },
15310     
15311     emptyResult : {
15312         tag: 'div',
15313         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15314     },
15315     
15316     footer : {
15317         tag: 'div',
15318         cls: 'modal-footer',
15319         cn: [
15320             {
15321                 tag: 'div',
15322                 cls: 'row',
15323                 cn: [
15324                     {
15325                         tag: 'div',
15326                         cls: 'col-xs-6 text-left',
15327                         cn: {
15328                             tag: 'button',
15329                             cls: 'btn btn-danger roo-touch-view-cancel',
15330                             html: 'Cancel'
15331                         }
15332                     },
15333                     {
15334                         tag: 'div',
15335                         cls: 'col-xs-6 text-right',
15336                         cn: {
15337                             tag: 'button',
15338                             cls: 'btn btn-success roo-touch-view-ok',
15339                             html: 'OK'
15340                         }
15341                     }
15342                 ]
15343             }
15344         ]
15345         
15346     }
15347 });
15348
15349 Roo.apply(Roo.bootstrap.ComboBox,  {
15350     
15351     touchViewTemplate : {
15352         tag: 'div',
15353         cls: 'modal fade roo-combobox-touch-view',
15354         cn: [
15355             {
15356                 tag: 'div',
15357                 cls: 'modal-dialog',
15358                 style : 'position:fixed', // we have to fix position....
15359                 cn: [
15360                     {
15361                         tag: 'div',
15362                         cls: 'modal-content',
15363                         cn: [
15364                             Roo.bootstrap.ComboBox.header,
15365                             Roo.bootstrap.ComboBox.body,
15366                             Roo.bootstrap.ComboBox.footer
15367                         ]
15368                     }
15369                 ]
15370             }
15371         ]
15372     }
15373 });/*
15374  * Based on:
15375  * Ext JS Library 1.1.1
15376  * Copyright(c) 2006-2007, Ext JS, LLC.
15377  *
15378  * Originally Released Under LGPL - original licence link has changed is not relivant.
15379  *
15380  * Fork - LGPL
15381  * <script type="text/javascript">
15382  */
15383
15384 /**
15385  * @class Roo.View
15386  * @extends Roo.util.Observable
15387  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15388  * This class also supports single and multi selection modes. <br>
15389  * Create a data model bound view:
15390  <pre><code>
15391  var store = new Roo.data.Store(...);
15392
15393  var view = new Roo.View({
15394     el : "my-element",
15395     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15396  
15397     singleSelect: true,
15398     selectedClass: "ydataview-selected",
15399     store: store
15400  });
15401
15402  // listen for node click?
15403  view.on("click", function(vw, index, node, e){
15404  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15405  });
15406
15407  // load XML data
15408  dataModel.load("foobar.xml");
15409  </code></pre>
15410  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15411  * <br><br>
15412  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15413  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15414  * 
15415  * Note: old style constructor is still suported (container, template, config)
15416  * 
15417  * @constructor
15418  * Create a new View
15419  * @param {Object} config The config object
15420  * 
15421  */
15422 Roo.View = function(config, depreciated_tpl, depreciated_config){
15423     
15424     this.parent = false;
15425     
15426     if (typeof(depreciated_tpl) == 'undefined') {
15427         // new way.. - universal constructor.
15428         Roo.apply(this, config);
15429         this.el  = Roo.get(this.el);
15430     } else {
15431         // old format..
15432         this.el  = Roo.get(config);
15433         this.tpl = depreciated_tpl;
15434         Roo.apply(this, depreciated_config);
15435     }
15436     this.wrapEl  = this.el.wrap().wrap();
15437     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15438     
15439     
15440     if(typeof(this.tpl) == "string"){
15441         this.tpl = new Roo.Template(this.tpl);
15442     } else {
15443         // support xtype ctors..
15444         this.tpl = new Roo.factory(this.tpl, Roo);
15445     }
15446     
15447     
15448     this.tpl.compile();
15449     
15450     /** @private */
15451     this.addEvents({
15452         /**
15453          * @event beforeclick
15454          * Fires before a click is processed. Returns false to cancel the default action.
15455          * @param {Roo.View} this
15456          * @param {Number} index The index of the target node
15457          * @param {HTMLElement} node The target node
15458          * @param {Roo.EventObject} e The raw event object
15459          */
15460             "beforeclick" : true,
15461         /**
15462          * @event click
15463          * Fires when a template node is clicked.
15464          * @param {Roo.View} this
15465          * @param {Number} index The index of the target node
15466          * @param {HTMLElement} node The target node
15467          * @param {Roo.EventObject} e The raw event object
15468          */
15469             "click" : true,
15470         /**
15471          * @event dblclick
15472          * Fires when a template node is double clicked.
15473          * @param {Roo.View} this
15474          * @param {Number} index The index of the target node
15475          * @param {HTMLElement} node The target node
15476          * @param {Roo.EventObject} e The raw event object
15477          */
15478             "dblclick" : true,
15479         /**
15480          * @event contextmenu
15481          * Fires when a template node is right clicked.
15482          * @param {Roo.View} this
15483          * @param {Number} index The index of the target node
15484          * @param {HTMLElement} node The target node
15485          * @param {Roo.EventObject} e The raw event object
15486          */
15487             "contextmenu" : true,
15488         /**
15489          * @event selectionchange
15490          * Fires when the selected nodes change.
15491          * @param {Roo.View} this
15492          * @param {Array} selections Array of the selected nodes
15493          */
15494             "selectionchange" : true,
15495     
15496         /**
15497          * @event beforeselect
15498          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15499          * @param {Roo.View} this
15500          * @param {HTMLElement} node The node to be selected
15501          * @param {Array} selections Array of currently selected nodes
15502          */
15503             "beforeselect" : true,
15504         /**
15505          * @event preparedata
15506          * Fires on every row to render, to allow you to change the data.
15507          * @param {Roo.View} this
15508          * @param {Object} data to be rendered (change this)
15509          */
15510           "preparedata" : true
15511           
15512           
15513         });
15514
15515
15516
15517     this.el.on({
15518         "click": this.onClick,
15519         "dblclick": this.onDblClick,
15520         "contextmenu": this.onContextMenu,
15521         scope:this
15522     });
15523
15524     this.selections = [];
15525     this.nodes = [];
15526     this.cmp = new Roo.CompositeElementLite([]);
15527     if(this.store){
15528         this.store = Roo.factory(this.store, Roo.data);
15529         this.setStore(this.store, true);
15530     }
15531     
15532     if ( this.footer && this.footer.xtype) {
15533            
15534          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15535         
15536         this.footer.dataSource = this.store;
15537         this.footer.container = fctr;
15538         this.footer = Roo.factory(this.footer, Roo);
15539         fctr.insertFirst(this.el);
15540         
15541         // this is a bit insane - as the paging toolbar seems to detach the el..
15542 //        dom.parentNode.parentNode.parentNode
15543          // they get detached?
15544     }
15545     
15546     
15547     Roo.View.superclass.constructor.call(this);
15548     
15549     
15550 };
15551
15552 Roo.extend(Roo.View, Roo.util.Observable, {
15553     
15554      /**
15555      * @cfg {Roo.data.Store} store Data store to load data from.
15556      */
15557     store : false,
15558     
15559     /**
15560      * @cfg {String|Roo.Element} el The container element.
15561      */
15562     el : '',
15563     
15564     /**
15565      * @cfg {String|Roo.Template} tpl The template used by this View 
15566      */
15567     tpl : false,
15568     /**
15569      * @cfg {String} dataName the named area of the template to use as the data area
15570      *                          Works with domtemplates roo-name="name"
15571      */
15572     dataName: false,
15573     /**
15574      * @cfg {String} selectedClass The css class to add to selected nodes
15575      */
15576     selectedClass : "x-view-selected",
15577      /**
15578      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15579      */
15580     emptyText : "",
15581     
15582     /**
15583      * @cfg {String} text to display on mask (default Loading)
15584      */
15585     mask : false,
15586     /**
15587      * @cfg {Boolean} multiSelect Allow multiple selection
15588      */
15589     multiSelect : false,
15590     /**
15591      * @cfg {Boolean} singleSelect Allow single selection
15592      */
15593     singleSelect:  false,
15594     
15595     /**
15596      * @cfg {Boolean} toggleSelect - selecting 
15597      */
15598     toggleSelect : false,
15599     
15600     /**
15601      * @cfg {Boolean} tickable - selecting 
15602      */
15603     tickable : false,
15604     
15605     /**
15606      * Returns the element this view is bound to.
15607      * @return {Roo.Element}
15608      */
15609     getEl : function(){
15610         return this.wrapEl;
15611     },
15612     
15613     
15614
15615     /**
15616      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15617      */
15618     refresh : function(){
15619         //Roo.log('refresh');
15620         var t = this.tpl;
15621         
15622         // if we are using something like 'domtemplate', then
15623         // the what gets used is:
15624         // t.applySubtemplate(NAME, data, wrapping data..)
15625         // the outer template then get' applied with
15626         //     the store 'extra data'
15627         // and the body get's added to the
15628         //      roo-name="data" node?
15629         //      <span class='roo-tpl-{name}'></span> ?????
15630         
15631         
15632         
15633         this.clearSelections();
15634         this.el.update("");
15635         var html = [];
15636         var records = this.store.getRange();
15637         if(records.length < 1) {
15638             
15639             // is this valid??  = should it render a template??
15640             
15641             this.el.update(this.emptyText);
15642             return;
15643         }
15644         var el = this.el;
15645         if (this.dataName) {
15646             this.el.update(t.apply(this.store.meta)); //????
15647             el = this.el.child('.roo-tpl-' + this.dataName);
15648         }
15649         
15650         for(var i = 0, len = records.length; i < len; i++){
15651             var data = this.prepareData(records[i].data, i, records[i]);
15652             this.fireEvent("preparedata", this, data, i, records[i]);
15653             
15654             var d = Roo.apply({}, data);
15655             
15656             if(this.tickable){
15657                 Roo.apply(d, {'roo-id' : Roo.id()});
15658                 
15659                 var _this = this;
15660             
15661                 Roo.each(this.parent.item, function(item){
15662                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15663                         return;
15664                     }
15665                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15666                 });
15667             }
15668             
15669             html[html.length] = Roo.util.Format.trim(
15670                 this.dataName ?
15671                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15672                     t.apply(d)
15673             );
15674         }
15675         
15676         
15677         
15678         el.update(html.join(""));
15679         this.nodes = el.dom.childNodes;
15680         this.updateIndexes(0);
15681     },
15682     
15683
15684     /**
15685      * Function to override to reformat the data that is sent to
15686      * the template for each node.
15687      * DEPRICATED - use the preparedata event handler.
15688      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15689      * a JSON object for an UpdateManager bound view).
15690      */
15691     prepareData : function(data, index, record)
15692     {
15693         this.fireEvent("preparedata", this, data, index, record);
15694         return data;
15695     },
15696
15697     onUpdate : function(ds, record){
15698         // Roo.log('on update');   
15699         this.clearSelections();
15700         var index = this.store.indexOf(record);
15701         var n = this.nodes[index];
15702         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15703         n.parentNode.removeChild(n);
15704         this.updateIndexes(index, index);
15705     },
15706
15707     
15708     
15709 // --------- FIXME     
15710     onAdd : function(ds, records, index)
15711     {
15712         //Roo.log(['on Add', ds, records, index] );        
15713         this.clearSelections();
15714         if(this.nodes.length == 0){
15715             this.refresh();
15716             return;
15717         }
15718         var n = this.nodes[index];
15719         for(var i = 0, len = records.length; i < len; i++){
15720             var d = this.prepareData(records[i].data, i, records[i]);
15721             if(n){
15722                 this.tpl.insertBefore(n, d);
15723             }else{
15724                 
15725                 this.tpl.append(this.el, d);
15726             }
15727         }
15728         this.updateIndexes(index);
15729     },
15730
15731     onRemove : function(ds, record, index){
15732        // Roo.log('onRemove');
15733         this.clearSelections();
15734         var el = this.dataName  ?
15735             this.el.child('.roo-tpl-' + this.dataName) :
15736             this.el; 
15737         
15738         el.dom.removeChild(this.nodes[index]);
15739         this.updateIndexes(index);
15740     },
15741
15742     /**
15743      * Refresh an individual node.
15744      * @param {Number} index
15745      */
15746     refreshNode : function(index){
15747         this.onUpdate(this.store, this.store.getAt(index));
15748     },
15749
15750     updateIndexes : function(startIndex, endIndex){
15751         var ns = this.nodes;
15752         startIndex = startIndex || 0;
15753         endIndex = endIndex || ns.length - 1;
15754         for(var i = startIndex; i <= endIndex; i++){
15755             ns[i].nodeIndex = i;
15756         }
15757     },
15758
15759     /**
15760      * Changes the data store this view uses and refresh the view.
15761      * @param {Store} store
15762      */
15763     setStore : function(store, initial){
15764         if(!initial && this.store){
15765             this.store.un("datachanged", this.refresh);
15766             this.store.un("add", this.onAdd);
15767             this.store.un("remove", this.onRemove);
15768             this.store.un("update", this.onUpdate);
15769             this.store.un("clear", this.refresh);
15770             this.store.un("beforeload", this.onBeforeLoad);
15771             this.store.un("load", this.onLoad);
15772             this.store.un("loadexception", this.onLoad);
15773         }
15774         if(store){
15775           
15776             store.on("datachanged", this.refresh, this);
15777             store.on("add", this.onAdd, this);
15778             store.on("remove", this.onRemove, this);
15779             store.on("update", this.onUpdate, this);
15780             store.on("clear", this.refresh, this);
15781             store.on("beforeload", this.onBeforeLoad, this);
15782             store.on("load", this.onLoad, this);
15783             store.on("loadexception", this.onLoad, this);
15784         }
15785         
15786         if(store){
15787             this.refresh();
15788         }
15789     },
15790     /**
15791      * onbeforeLoad - masks the loading area.
15792      *
15793      */
15794     onBeforeLoad : function(store,opts)
15795     {
15796          //Roo.log('onBeforeLoad');   
15797         if (!opts.add) {
15798             this.el.update("");
15799         }
15800         this.el.mask(this.mask ? this.mask : "Loading" ); 
15801     },
15802     onLoad : function ()
15803     {
15804         this.el.unmask();
15805     },
15806     
15807
15808     /**
15809      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15810      * @param {HTMLElement} node
15811      * @return {HTMLElement} The template node
15812      */
15813     findItemFromChild : function(node){
15814         var el = this.dataName  ?
15815             this.el.child('.roo-tpl-' + this.dataName,true) :
15816             this.el.dom; 
15817         
15818         if(!node || node.parentNode == el){
15819                     return node;
15820             }
15821             var p = node.parentNode;
15822             while(p && p != el){
15823             if(p.parentNode == el){
15824                 return p;
15825             }
15826             p = p.parentNode;
15827         }
15828             return null;
15829     },
15830
15831     /** @ignore */
15832     onClick : function(e){
15833         var item = this.findItemFromChild(e.getTarget());
15834         if(item){
15835             var index = this.indexOf(item);
15836             if(this.onItemClick(item, index, e) !== false){
15837                 this.fireEvent("click", this, index, item, e);
15838             }
15839         }else{
15840             this.clearSelections();
15841         }
15842     },
15843
15844     /** @ignore */
15845     onContextMenu : function(e){
15846         var item = this.findItemFromChild(e.getTarget());
15847         if(item){
15848             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15849         }
15850     },
15851
15852     /** @ignore */
15853     onDblClick : function(e){
15854         var item = this.findItemFromChild(e.getTarget());
15855         if(item){
15856             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15857         }
15858     },
15859
15860     onItemClick : function(item, index, e)
15861     {
15862         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15863             return false;
15864         }
15865         if (this.toggleSelect) {
15866             var m = this.isSelected(item) ? 'unselect' : 'select';
15867             //Roo.log(m);
15868             var _t = this;
15869             _t[m](item, true, false);
15870             return true;
15871         }
15872         if(this.multiSelect || this.singleSelect){
15873             if(this.multiSelect && e.shiftKey && this.lastSelection){
15874                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15875             }else{
15876                 this.select(item, this.multiSelect && e.ctrlKey);
15877                 this.lastSelection = item;
15878             }
15879             
15880             if(!this.tickable){
15881                 e.preventDefault();
15882             }
15883             
15884         }
15885         return true;
15886     },
15887
15888     /**
15889      * Get the number of selected nodes.
15890      * @return {Number}
15891      */
15892     getSelectionCount : function(){
15893         return this.selections.length;
15894     },
15895
15896     /**
15897      * Get the currently selected nodes.
15898      * @return {Array} An array of HTMLElements
15899      */
15900     getSelectedNodes : function(){
15901         return this.selections;
15902     },
15903
15904     /**
15905      * Get the indexes of the selected nodes.
15906      * @return {Array}
15907      */
15908     getSelectedIndexes : function(){
15909         var indexes = [], s = this.selections;
15910         for(var i = 0, len = s.length; i < len; i++){
15911             indexes.push(s[i].nodeIndex);
15912         }
15913         return indexes;
15914     },
15915
15916     /**
15917      * Clear all selections
15918      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15919      */
15920     clearSelections : function(suppressEvent){
15921         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15922             this.cmp.elements = this.selections;
15923             this.cmp.removeClass(this.selectedClass);
15924             this.selections = [];
15925             if(!suppressEvent){
15926                 this.fireEvent("selectionchange", this, this.selections);
15927             }
15928         }
15929     },
15930
15931     /**
15932      * Returns true if the passed node is selected
15933      * @param {HTMLElement/Number} node The node or node index
15934      * @return {Boolean}
15935      */
15936     isSelected : function(node){
15937         var s = this.selections;
15938         if(s.length < 1){
15939             return false;
15940         }
15941         node = this.getNode(node);
15942         return s.indexOf(node) !== -1;
15943     },
15944
15945     /**
15946      * Selects nodes.
15947      * @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
15948      * @param {Boolean} keepExisting (optional) true to keep existing selections
15949      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15950      */
15951     select : function(nodeInfo, keepExisting, suppressEvent){
15952         if(nodeInfo instanceof Array){
15953             if(!keepExisting){
15954                 this.clearSelections(true);
15955             }
15956             for(var i = 0, len = nodeInfo.length; i < len; i++){
15957                 this.select(nodeInfo[i], true, true);
15958             }
15959             return;
15960         } 
15961         var node = this.getNode(nodeInfo);
15962         if(!node || this.isSelected(node)){
15963             return; // already selected.
15964         }
15965         if(!keepExisting){
15966             this.clearSelections(true);
15967         }
15968         
15969         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15970             Roo.fly(node).addClass(this.selectedClass);
15971             this.selections.push(node);
15972             if(!suppressEvent){
15973                 this.fireEvent("selectionchange", this, this.selections);
15974             }
15975         }
15976         
15977         
15978     },
15979       /**
15980      * Unselects nodes.
15981      * @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
15982      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15984      */
15985     unselect : function(nodeInfo, keepExisting, suppressEvent)
15986     {
15987         if(nodeInfo instanceof Array){
15988             Roo.each(this.selections, function(s) {
15989                 this.unselect(s, nodeInfo);
15990             }, this);
15991             return;
15992         }
15993         var node = this.getNode(nodeInfo);
15994         if(!node || !this.isSelected(node)){
15995             //Roo.log("not selected");
15996             return; // not selected.
15997         }
15998         // fireevent???
15999         var ns = [];
16000         Roo.each(this.selections, function(s) {
16001             if (s == node ) {
16002                 Roo.fly(node).removeClass(this.selectedClass);
16003
16004                 return;
16005             }
16006             ns.push(s);
16007         },this);
16008         
16009         this.selections= ns;
16010         this.fireEvent("selectionchange", this, this.selections);
16011     },
16012
16013     /**
16014      * Gets a template node.
16015      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16016      * @return {HTMLElement} The node or null if it wasn't found
16017      */
16018     getNode : function(nodeInfo){
16019         if(typeof nodeInfo == "string"){
16020             return document.getElementById(nodeInfo);
16021         }else if(typeof nodeInfo == "number"){
16022             return this.nodes[nodeInfo];
16023         }
16024         return nodeInfo;
16025     },
16026
16027     /**
16028      * Gets a range template nodes.
16029      * @param {Number} startIndex
16030      * @param {Number} endIndex
16031      * @return {Array} An array of nodes
16032      */
16033     getNodes : function(start, end){
16034         var ns = this.nodes;
16035         start = start || 0;
16036         end = typeof end == "undefined" ? ns.length - 1 : end;
16037         var nodes = [];
16038         if(start <= end){
16039             for(var i = start; i <= end; i++){
16040                 nodes.push(ns[i]);
16041             }
16042         } else{
16043             for(var i = start; i >= end; i--){
16044                 nodes.push(ns[i]);
16045             }
16046         }
16047         return nodes;
16048     },
16049
16050     /**
16051      * Finds the index of the passed node
16052      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16053      * @return {Number} The index of the node or -1
16054      */
16055     indexOf : function(node){
16056         node = this.getNode(node);
16057         if(typeof node.nodeIndex == "number"){
16058             return node.nodeIndex;
16059         }
16060         var ns = this.nodes;
16061         for(var i = 0, len = ns.length; i < len; i++){
16062             if(ns[i] == node){
16063                 return i;
16064             }
16065         }
16066         return -1;
16067     }
16068 });
16069 /*
16070  * - LGPL
16071  *
16072  * based on jquery fullcalendar
16073  * 
16074  */
16075
16076 Roo.bootstrap = Roo.bootstrap || {};
16077 /**
16078  * @class Roo.bootstrap.Calendar
16079  * @extends Roo.bootstrap.Component
16080  * Bootstrap Calendar class
16081  * @cfg {Boolean} loadMask (true|false) default false
16082  * @cfg {Object} header generate the user specific header of the calendar, default false
16083
16084  * @constructor
16085  * Create a new Container
16086  * @param {Object} config The config object
16087  */
16088
16089
16090
16091 Roo.bootstrap.Calendar = function(config){
16092     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16093      this.addEvents({
16094         /**
16095              * @event select
16096              * Fires when a date is selected
16097              * @param {DatePicker} this
16098              * @param {Date} date The selected date
16099              */
16100         'select': true,
16101         /**
16102              * @event monthchange
16103              * Fires when the displayed month changes 
16104              * @param {DatePicker} this
16105              * @param {Date} date The selected month
16106              */
16107         'monthchange': true,
16108         /**
16109              * @event evententer
16110              * Fires when mouse over an event
16111              * @param {Calendar} this
16112              * @param {event} Event
16113              */
16114         'evententer': true,
16115         /**
16116              * @event eventleave
16117              * Fires when the mouse leaves an
16118              * @param {Calendar} this
16119              * @param {event}
16120              */
16121         'eventleave': true,
16122         /**
16123              * @event eventclick
16124              * Fires when the mouse click an
16125              * @param {Calendar} this
16126              * @param {event}
16127              */
16128         'eventclick': true
16129         
16130     });
16131
16132 };
16133
16134 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16135     
16136      /**
16137      * @cfg {Number} startDay
16138      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16139      */
16140     startDay : 0,
16141     
16142     loadMask : false,
16143     
16144     header : false,
16145       
16146     getAutoCreate : function(){
16147         
16148         
16149         var fc_button = function(name, corner, style, content ) {
16150             return Roo.apply({},{
16151                 tag : 'span',
16152                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16153                          (corner.length ?
16154                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16155                             ''
16156                         ),
16157                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16158                 unselectable: 'on'
16159             });
16160         };
16161         
16162         var header = {};
16163         
16164         if(!this.header){
16165             header = {
16166                 tag : 'table',
16167                 cls : 'fc-header',
16168                 style : 'width:100%',
16169                 cn : [
16170                     {
16171                         tag: 'tr',
16172                         cn : [
16173                             {
16174                                 tag : 'td',
16175                                 cls : 'fc-header-left',
16176                                 cn : [
16177                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16178                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16179                                     { tag: 'span', cls: 'fc-header-space' },
16180                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16181
16182
16183                                 ]
16184                             },
16185
16186                             {
16187                                 tag : 'td',
16188                                 cls : 'fc-header-center',
16189                                 cn : [
16190                                     {
16191                                         tag: 'span',
16192                                         cls: 'fc-header-title',
16193                                         cn : {
16194                                             tag: 'H2',
16195                                             html : 'month / year'
16196                                         }
16197                                     }
16198
16199                                 ]
16200                             },
16201                             {
16202                                 tag : 'td',
16203                                 cls : 'fc-header-right',
16204                                 cn : [
16205                               /*      fc_button('month', 'left', '', 'month' ),
16206                                     fc_button('week', '', '', 'week' ),
16207                                     fc_button('day', 'right', '', 'day' )
16208                                 */    
16209
16210                                 ]
16211                             }
16212
16213                         ]
16214                     }
16215                 ]
16216             };
16217         }
16218         
16219         header = this.header;
16220         
16221        
16222         var cal_heads = function() {
16223             var ret = [];
16224             // fixme - handle this.
16225             
16226             for (var i =0; i < Date.dayNames.length; i++) {
16227                 var d = Date.dayNames[i];
16228                 ret.push({
16229                     tag: 'th',
16230                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16231                     html : d.substring(0,3)
16232                 });
16233                 
16234             }
16235             ret[0].cls += ' fc-first';
16236             ret[6].cls += ' fc-last';
16237             return ret;
16238         };
16239         var cal_cell = function(n) {
16240             return  {
16241                 tag: 'td',
16242                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16243                 cn : [
16244                     {
16245                         cn : [
16246                             {
16247                                 cls: 'fc-day-number',
16248                                 html: 'D'
16249                             },
16250                             {
16251                                 cls: 'fc-day-content',
16252                              
16253                                 cn : [
16254                                      {
16255                                         style: 'position: relative;' // height: 17px;
16256                                     }
16257                                 ]
16258                             }
16259                             
16260                             
16261                         ]
16262                     }
16263                 ]
16264                 
16265             }
16266         };
16267         var cal_rows = function() {
16268             
16269             var ret = [];
16270             for (var r = 0; r < 6; r++) {
16271                 var row= {
16272                     tag : 'tr',
16273                     cls : 'fc-week',
16274                     cn : []
16275                 };
16276                 
16277                 for (var i =0; i < Date.dayNames.length; i++) {
16278                     var d = Date.dayNames[i];
16279                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16280
16281                 }
16282                 row.cn[0].cls+=' fc-first';
16283                 row.cn[0].cn[0].style = 'min-height:90px';
16284                 row.cn[6].cls+=' fc-last';
16285                 ret.push(row);
16286                 
16287             }
16288             ret[0].cls += ' fc-first';
16289             ret[4].cls += ' fc-prev-last';
16290             ret[5].cls += ' fc-last';
16291             return ret;
16292             
16293         };
16294         
16295         var cal_table = {
16296             tag: 'table',
16297             cls: 'fc-border-separate',
16298             style : 'width:100%',
16299             cellspacing  : 0,
16300             cn : [
16301                 { 
16302                     tag: 'thead',
16303                     cn : [
16304                         { 
16305                             tag: 'tr',
16306                             cls : 'fc-first fc-last',
16307                             cn : cal_heads()
16308                         }
16309                     ]
16310                 },
16311                 { 
16312                     tag: 'tbody',
16313                     cn : cal_rows()
16314                 }
16315                   
16316             ]
16317         };
16318          
16319          var cfg = {
16320             cls : 'fc fc-ltr',
16321             cn : [
16322                 header,
16323                 {
16324                     cls : 'fc-content',
16325                     style : "position: relative;",
16326                     cn : [
16327                         {
16328                             cls : 'fc-view fc-view-month fc-grid',
16329                             style : 'position: relative',
16330                             unselectable : 'on',
16331                             cn : [
16332                                 {
16333                                     cls : 'fc-event-container',
16334                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16335                                 },
16336                                 cal_table
16337                             ]
16338                         }
16339                     ]
16340     
16341                 }
16342            ] 
16343             
16344         };
16345         
16346          
16347         
16348         return cfg;
16349     },
16350     
16351     
16352     initEvents : function()
16353     {
16354         if(!this.store){
16355             throw "can not find store for calendar";
16356         }
16357         
16358         var mark = {
16359             tag: "div",
16360             cls:"x-dlg-mask",
16361             style: "text-align:center",
16362             cn: [
16363                 {
16364                     tag: "div",
16365                     style: "background-color:white;width:50%;margin:250 auto",
16366                     cn: [
16367                         {
16368                             tag: "img",
16369                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16370                         },
16371                         {
16372                             tag: "span",
16373                             html: "Loading"
16374                         }
16375                         
16376                     ]
16377                 }
16378             ]
16379         };
16380         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16381         
16382         var size = this.el.select('.fc-content', true).first().getSize();
16383         this.maskEl.setSize(size.width, size.height);
16384         this.maskEl.enableDisplayMode("block");
16385         if(!this.loadMask){
16386             this.maskEl.hide();
16387         }
16388         
16389         this.store = Roo.factory(this.store, Roo.data);
16390         this.store.on('load', this.onLoad, this);
16391         this.store.on('beforeload', this.onBeforeLoad, this);
16392         
16393         this.resize();
16394         
16395         this.cells = this.el.select('.fc-day',true);
16396         //Roo.log(this.cells);
16397         this.textNodes = this.el.query('.fc-day-number');
16398         this.cells.addClassOnOver('fc-state-hover');
16399         
16400         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16401         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16402         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16403         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16404         
16405         this.on('monthchange', this.onMonthChange, this);
16406         
16407         this.update(new Date().clearTime());
16408     },
16409     
16410     resize : function() {
16411         var sz  = this.el.getSize();
16412         
16413         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16414         this.el.select('.fc-day-content div',true).setHeight(34);
16415     },
16416     
16417     
16418     // private
16419     showPrevMonth : function(e){
16420         this.update(this.activeDate.add("mo", -1));
16421     },
16422     showToday : function(e){
16423         this.update(new Date().clearTime());
16424     },
16425     // private
16426     showNextMonth : function(e){
16427         this.update(this.activeDate.add("mo", 1));
16428     },
16429
16430     // private
16431     showPrevYear : function(){
16432         this.update(this.activeDate.add("y", -1));
16433     },
16434
16435     // private
16436     showNextYear : function(){
16437         this.update(this.activeDate.add("y", 1));
16438     },
16439
16440     
16441    // private
16442     update : function(date)
16443     {
16444         var vd = this.activeDate;
16445         this.activeDate = date;
16446 //        if(vd && this.el){
16447 //            var t = date.getTime();
16448 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16449 //                Roo.log('using add remove');
16450 //                
16451 //                this.fireEvent('monthchange', this, date);
16452 //                
16453 //                this.cells.removeClass("fc-state-highlight");
16454 //                this.cells.each(function(c){
16455 //                   if(c.dateValue == t){
16456 //                       c.addClass("fc-state-highlight");
16457 //                       setTimeout(function(){
16458 //                            try{c.dom.firstChild.focus();}catch(e){}
16459 //                       }, 50);
16460 //                       return false;
16461 //                   }
16462 //                   return true;
16463 //                });
16464 //                return;
16465 //            }
16466 //        }
16467         
16468         var days = date.getDaysInMonth();
16469         
16470         var firstOfMonth = date.getFirstDateOfMonth();
16471         var startingPos = firstOfMonth.getDay()-this.startDay;
16472         
16473         if(startingPos < this.startDay){
16474             startingPos += 7;
16475         }
16476         
16477         var pm = date.add(Date.MONTH, -1);
16478         var prevStart = pm.getDaysInMonth()-startingPos;
16479 //        
16480         this.cells = this.el.select('.fc-day',true);
16481         this.textNodes = this.el.query('.fc-day-number');
16482         this.cells.addClassOnOver('fc-state-hover');
16483         
16484         var cells = this.cells.elements;
16485         var textEls = this.textNodes;
16486         
16487         Roo.each(cells, function(cell){
16488             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16489         });
16490         
16491         days += startingPos;
16492
16493         // convert everything to numbers so it's fast
16494         var day = 86400000;
16495         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16496         //Roo.log(d);
16497         //Roo.log(pm);
16498         //Roo.log(prevStart);
16499         
16500         var today = new Date().clearTime().getTime();
16501         var sel = date.clearTime().getTime();
16502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16504         var ddMatch = this.disabledDatesRE;
16505         var ddText = this.disabledDatesText;
16506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16507         var ddaysText = this.disabledDaysText;
16508         var format = this.format;
16509         
16510         var setCellClass = function(cal, cell){
16511             cell.row = 0;
16512             cell.events = [];
16513             cell.more = [];
16514             //Roo.log('set Cell Class');
16515             cell.title = "";
16516             var t = d.getTime();
16517             
16518             //Roo.log(d);
16519             
16520             cell.dateValue = t;
16521             if(t == today){
16522                 cell.className += " fc-today";
16523                 cell.className += " fc-state-highlight";
16524                 cell.title = cal.todayText;
16525             }
16526             if(t == sel){
16527                 // disable highlight in other month..
16528                 //cell.className += " fc-state-highlight";
16529                 
16530             }
16531             // disabling
16532             if(t < min) {
16533                 cell.className = " fc-state-disabled";
16534                 cell.title = cal.minText;
16535                 return;
16536             }
16537             if(t > max) {
16538                 cell.className = " fc-state-disabled";
16539                 cell.title = cal.maxText;
16540                 return;
16541             }
16542             if(ddays){
16543                 if(ddays.indexOf(d.getDay()) != -1){
16544                     cell.title = ddaysText;
16545                     cell.className = " fc-state-disabled";
16546                 }
16547             }
16548             if(ddMatch && format){
16549                 var fvalue = d.dateFormat(format);
16550                 if(ddMatch.test(fvalue)){
16551                     cell.title = ddText.replace("%0", fvalue);
16552                     cell.className = " fc-state-disabled";
16553                 }
16554             }
16555             
16556             if (!cell.initialClassName) {
16557                 cell.initialClassName = cell.dom.className;
16558             }
16559             
16560             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16561         };
16562
16563         var i = 0;
16564         
16565         for(; i < startingPos; i++) {
16566             textEls[i].innerHTML = (++prevStart);
16567             d.setDate(d.getDate()+1);
16568             
16569             cells[i].className = "fc-past fc-other-month";
16570             setCellClass(this, cells[i]);
16571         }
16572         
16573         var intDay = 0;
16574         
16575         for(; i < days; i++){
16576             intDay = i - startingPos + 1;
16577             textEls[i].innerHTML = (intDay);
16578             d.setDate(d.getDate()+1);
16579             
16580             cells[i].className = ''; // "x-date-active";
16581             setCellClass(this, cells[i]);
16582         }
16583         var extraDays = 0;
16584         
16585         for(; i < 42; i++) {
16586             textEls[i].innerHTML = (++extraDays);
16587             d.setDate(d.getDate()+1);
16588             
16589             cells[i].className = "fc-future fc-other-month";
16590             setCellClass(this, cells[i]);
16591         }
16592         
16593         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16594         
16595         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16596         
16597         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16598         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16599         
16600         if(totalRows != 6){
16601             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16602             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16603         }
16604         
16605         this.fireEvent('monthchange', this, date);
16606         
16607         
16608         /*
16609         if(!this.internalRender){
16610             var main = this.el.dom.firstChild;
16611             var w = main.offsetWidth;
16612             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16613             Roo.fly(main).setWidth(w);
16614             this.internalRender = true;
16615             // opera does not respect the auto grow header center column
16616             // then, after it gets a width opera refuses to recalculate
16617             // without a second pass
16618             if(Roo.isOpera && !this.secondPass){
16619                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16620                 this.secondPass = true;
16621                 this.update.defer(10, this, [date]);
16622             }
16623         }
16624         */
16625         
16626     },
16627     
16628     findCell : function(dt) {
16629         dt = dt.clearTime().getTime();
16630         var ret = false;
16631         this.cells.each(function(c){
16632             //Roo.log("check " +c.dateValue + '?=' + dt);
16633             if(c.dateValue == dt){
16634                 ret = c;
16635                 return false;
16636             }
16637             return true;
16638         });
16639         
16640         return ret;
16641     },
16642     
16643     findCells : function(ev) {
16644         var s = ev.start.clone().clearTime().getTime();
16645        // Roo.log(s);
16646         var e= ev.end.clone().clearTime().getTime();
16647        // Roo.log(e);
16648         var ret = [];
16649         this.cells.each(function(c){
16650              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16651             
16652             if(c.dateValue > e){
16653                 return ;
16654             }
16655             if(c.dateValue < s){
16656                 return ;
16657             }
16658             ret.push(c);
16659         });
16660         
16661         return ret;    
16662     },
16663     
16664 //    findBestRow: function(cells)
16665 //    {
16666 //        var ret = 0;
16667 //        
16668 //        for (var i =0 ; i < cells.length;i++) {
16669 //            ret  = Math.max(cells[i].rows || 0,ret);
16670 //        }
16671 //        return ret;
16672 //        
16673 //    },
16674     
16675     
16676     addItem : function(ev)
16677     {
16678         // look for vertical location slot in
16679         var cells = this.findCells(ev);
16680         
16681 //        ev.row = this.findBestRow(cells);
16682         
16683         // work out the location.
16684         
16685         var crow = false;
16686         var rows = [];
16687         for(var i =0; i < cells.length; i++) {
16688             
16689             cells[i].row = cells[0].row;
16690             
16691             if(i == 0){
16692                 cells[i].row = cells[i].row + 1;
16693             }
16694             
16695             if (!crow) {
16696                 crow = {
16697                     start : cells[i],
16698                     end :  cells[i]
16699                 };
16700                 continue;
16701             }
16702             if (crow.start.getY() == cells[i].getY()) {
16703                 // on same row.
16704                 crow.end = cells[i];
16705                 continue;
16706             }
16707             // different row.
16708             rows.push(crow);
16709             crow = {
16710                 start: cells[i],
16711                 end : cells[i]
16712             };
16713             
16714         }
16715         
16716         rows.push(crow);
16717         ev.els = [];
16718         ev.rows = rows;
16719         ev.cells = cells;
16720         
16721         cells[0].events.push(ev);
16722         
16723         this.calevents.push(ev);
16724     },
16725     
16726     clearEvents: function() {
16727         
16728         if(!this.calevents){
16729             return;
16730         }
16731         
16732         Roo.each(this.cells.elements, function(c){
16733             c.row = 0;
16734             c.events = [];
16735             c.more = [];
16736         });
16737         
16738         Roo.each(this.calevents, function(e) {
16739             Roo.each(e.els, function(el) {
16740                 el.un('mouseenter' ,this.onEventEnter, this);
16741                 el.un('mouseleave' ,this.onEventLeave, this);
16742                 el.remove();
16743             },this);
16744         },this);
16745         
16746         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16747             e.remove();
16748         });
16749         
16750     },
16751     
16752     renderEvents: function()
16753     {   
16754         var _this = this;
16755         
16756         this.cells.each(function(c) {
16757             
16758             if(c.row < 5){
16759                 return;
16760             }
16761             
16762             var ev = c.events;
16763             
16764             var r = 4;
16765             if(c.row != c.events.length){
16766                 r = 4 - (4 - (c.row - c.events.length));
16767             }
16768             
16769             c.events = ev.slice(0, r);
16770             c.more = ev.slice(r);
16771             
16772             if(c.more.length && c.more.length == 1){
16773                 c.events.push(c.more.pop());
16774             }
16775             
16776             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16777             
16778         });
16779             
16780         this.cells.each(function(c) {
16781             
16782             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16783             
16784             
16785             for (var e = 0; e < c.events.length; e++){
16786                 var ev = c.events[e];
16787                 var rows = ev.rows;
16788                 
16789                 for(var i = 0; i < rows.length; i++) {
16790                 
16791                     // how many rows should it span..
16792
16793                     var  cfg = {
16794                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16795                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16796
16797                         unselectable : "on",
16798                         cn : [
16799                             {
16800                                 cls: 'fc-event-inner',
16801                                 cn : [
16802     //                                {
16803     //                                  tag:'span',
16804     //                                  cls: 'fc-event-time',
16805     //                                  html : cells.length > 1 ? '' : ev.time
16806     //                                },
16807                                     {
16808                                       tag:'span',
16809                                       cls: 'fc-event-title',
16810                                       html : String.format('{0}', ev.title)
16811                                     }
16812
16813
16814                                 ]
16815                             },
16816                             {
16817                                 cls: 'ui-resizable-handle ui-resizable-e',
16818                                 html : '&nbsp;&nbsp;&nbsp'
16819                             }
16820
16821                         ]
16822                     };
16823
16824                     if (i == 0) {
16825                         cfg.cls += ' fc-event-start';
16826                     }
16827                     if ((i+1) == rows.length) {
16828                         cfg.cls += ' fc-event-end';
16829                     }
16830
16831                     var ctr = _this.el.select('.fc-event-container',true).first();
16832                     var cg = ctr.createChild(cfg);
16833
16834                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16835                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16836
16837                     var r = (c.more.length) ? 1 : 0;
16838                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16839                     cg.setWidth(ebox.right - sbox.x -2);
16840
16841                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16842                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16843                     cg.on('click', _this.onEventClick, _this, ev);
16844
16845                     ev.els.push(cg);
16846                     
16847                 }
16848                 
16849             }
16850             
16851             
16852             if(c.more.length){
16853                 var  cfg = {
16854                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16855                     style : 'position: absolute',
16856                     unselectable : "on",
16857                     cn : [
16858                         {
16859                             cls: 'fc-event-inner',
16860                             cn : [
16861                                 {
16862                                   tag:'span',
16863                                   cls: 'fc-event-title',
16864                                   html : 'More'
16865                                 }
16866
16867
16868                             ]
16869                         },
16870                         {
16871                             cls: 'ui-resizable-handle ui-resizable-e',
16872                             html : '&nbsp;&nbsp;&nbsp'
16873                         }
16874
16875                     ]
16876                 };
16877
16878                 var ctr = _this.el.select('.fc-event-container',true).first();
16879                 var cg = ctr.createChild(cfg);
16880
16881                 var sbox = c.select('.fc-day-content',true).first().getBox();
16882                 var ebox = c.select('.fc-day-content',true).first().getBox();
16883                 //Roo.log(cg);
16884                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16885                 cg.setWidth(ebox.right - sbox.x -2);
16886
16887                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16888                 
16889             }
16890             
16891         });
16892         
16893         
16894         
16895     },
16896     
16897     onEventEnter: function (e, el,event,d) {
16898         this.fireEvent('evententer', this, el, event);
16899     },
16900     
16901     onEventLeave: function (e, el,event,d) {
16902         this.fireEvent('eventleave', this, el, event);
16903     },
16904     
16905     onEventClick: function (e, el,event,d) {
16906         this.fireEvent('eventclick', this, el, event);
16907     },
16908     
16909     onMonthChange: function () {
16910         this.store.load();
16911     },
16912     
16913     onMoreEventClick: function(e, el, more)
16914     {
16915         var _this = this;
16916         
16917         this.calpopover.placement = 'right';
16918         this.calpopover.setTitle('More');
16919         
16920         this.calpopover.setContent('');
16921         
16922         var ctr = this.calpopover.el.select('.popover-content', true).first();
16923         
16924         Roo.each(more, function(m){
16925             var cfg = {
16926                 cls : 'fc-event-hori fc-event-draggable',
16927                 html : m.title
16928             };
16929             var cg = ctr.createChild(cfg);
16930             
16931             cg.on('click', _this.onEventClick, _this, m);
16932         });
16933         
16934         this.calpopover.show(el);
16935         
16936         
16937     },
16938     
16939     onLoad: function () 
16940     {   
16941         this.calevents = [];
16942         var cal = this;
16943         
16944         if(this.store.getCount() > 0){
16945             this.store.data.each(function(d){
16946                cal.addItem({
16947                     id : d.data.id,
16948                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16949                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16950                     time : d.data.start_time,
16951                     title : d.data.title,
16952                     description : d.data.description,
16953                     venue : d.data.venue
16954                 });
16955             });
16956         }
16957         
16958         this.renderEvents();
16959         
16960         if(this.calevents.length && this.loadMask){
16961             this.maskEl.hide();
16962         }
16963     },
16964     
16965     onBeforeLoad: function()
16966     {
16967         this.clearEvents();
16968         if(this.loadMask){
16969             this.maskEl.show();
16970         }
16971     }
16972 });
16973
16974  
16975  /*
16976  * - LGPL
16977  *
16978  * element
16979  * 
16980  */
16981
16982 /**
16983  * @class Roo.bootstrap.Popover
16984  * @extends Roo.bootstrap.Component
16985  * Bootstrap Popover class
16986  * @cfg {String} html contents of the popover   (or false to use children..)
16987  * @cfg {String} title of popover (or false to hide)
16988  * @cfg {String} placement how it is placed
16989  * @cfg {String} trigger click || hover (or false to trigger manually)
16990  * @cfg {String} over what (parent or false to trigger manually.)
16991  * @cfg {Number} delay - delay before showing
16992  
16993  * @constructor
16994  * Create a new Popover
16995  * @param {Object} config The config object
16996  */
16997
16998 Roo.bootstrap.Popover = function(config){
16999     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17000     
17001     this.addEvents({
17002         // raw events
17003          /**
17004          * @event show
17005          * After the popover show
17006          * 
17007          * @param {Roo.bootstrap.Popover} this
17008          */
17009         "show" : true,
17010         /**
17011          * @event hide
17012          * After the popover hide
17013          * 
17014          * @param {Roo.bootstrap.Popover} this
17015          */
17016         "hide" : true
17017     });
17018 };
17019
17020 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17021     
17022     title: 'Fill in a title',
17023     html: false,
17024     
17025     placement : 'right',
17026     trigger : 'hover', // hover
17027     
17028     delay : 0,
17029     
17030     over: 'parent',
17031     
17032     can_build_overlaid : false,
17033     
17034     getChildContainer : function()
17035     {
17036         return this.el.select('.popover-content',true).first();
17037     },
17038     
17039     getAutoCreate : function(){
17040          
17041         var cfg = {
17042            cls : 'popover roo-dynamic',
17043            style: 'display:block',
17044            cn : [
17045                 {
17046                     cls : 'arrow'
17047                 },
17048                 {
17049                     cls : 'popover-inner',
17050                     cn : [
17051                         {
17052                             tag: 'h3',
17053                             cls: 'popover-title',
17054                             html : this.title
17055                         },
17056                         {
17057                             cls : 'popover-content',
17058                             html : this.html
17059                         }
17060                     ]
17061                     
17062                 }
17063            ]
17064         };
17065         
17066         return cfg;
17067     },
17068     setTitle: function(str)
17069     {
17070         this.title = str;
17071         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17072     },
17073     setContent: function(str)
17074     {
17075         this.html = str;
17076         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17077     },
17078     // as it get's added to the bottom of the page.
17079     onRender : function(ct, position)
17080     {
17081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17082         if(!this.el){
17083             var cfg = Roo.apply({},  this.getAutoCreate());
17084             cfg.id = Roo.id();
17085             
17086             if (this.cls) {
17087                 cfg.cls += ' ' + this.cls;
17088             }
17089             if (this.style) {
17090                 cfg.style = this.style;
17091             }
17092             //Roo.log("adding to ");
17093             this.el = Roo.get(document.body).createChild(cfg, position);
17094 //            Roo.log(this.el);
17095         }
17096         this.initEvents();
17097     },
17098     
17099     initEvents : function()
17100     {
17101         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17102         this.el.enableDisplayMode('block');
17103         this.el.hide();
17104         if (this.over === false) {
17105             return; 
17106         }
17107         if (this.triggers === false) {
17108             return;
17109         }
17110         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17111         var triggers = this.trigger ? this.trigger.split(' ') : [];
17112         Roo.each(triggers, function(trigger) {
17113         
17114             if (trigger == 'click') {
17115                 on_el.on('click', this.toggle, this);
17116             } else if (trigger != 'manual') {
17117                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17118                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17119       
17120                 on_el.on(eventIn  ,this.enter, this);
17121                 on_el.on(eventOut, this.leave, this);
17122             }
17123         }, this);
17124         
17125     },
17126     
17127     
17128     // private
17129     timeout : null,
17130     hoverState : null,
17131     
17132     toggle : function () {
17133         this.hoverState == 'in' ? this.leave() : this.enter();
17134     },
17135     
17136     enter : function () {
17137         
17138         clearTimeout(this.timeout);
17139     
17140         this.hoverState = 'in';
17141     
17142         if (!this.delay || !this.delay.show) {
17143             this.show();
17144             return;
17145         }
17146         var _t = this;
17147         this.timeout = setTimeout(function () {
17148             if (_t.hoverState == 'in') {
17149                 _t.show();
17150             }
17151         }, this.delay.show)
17152     },
17153     
17154     leave : function() {
17155         clearTimeout(this.timeout);
17156     
17157         this.hoverState = 'out';
17158     
17159         if (!this.delay || !this.delay.hide) {
17160             this.hide();
17161             return;
17162         }
17163         var _t = this;
17164         this.timeout = setTimeout(function () {
17165             if (_t.hoverState == 'out') {
17166                 _t.hide();
17167             }
17168         }, this.delay.hide)
17169     },
17170     
17171     show : function (on_el)
17172     {
17173         if (!on_el) {
17174             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17175         }
17176         
17177         // set content.
17178         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17179         if (this.html !== false) {
17180             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17181         }
17182         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17183         if (!this.title.length) {
17184             this.el.select('.popover-title',true).hide();
17185         }
17186         
17187         var placement = typeof this.placement == 'function' ?
17188             this.placement.call(this, this.el, on_el) :
17189             this.placement;
17190             
17191         var autoToken = /\s?auto?\s?/i;
17192         var autoPlace = autoToken.test(placement);
17193         if (autoPlace) {
17194             placement = placement.replace(autoToken, '') || 'top';
17195         }
17196         
17197         //this.el.detach()
17198         //this.el.setXY([0,0]);
17199         this.el.show();
17200         this.el.dom.style.display='block';
17201         this.el.addClass(placement);
17202         
17203         //this.el.appendTo(on_el);
17204         
17205         var p = this.getPosition();
17206         var box = this.el.getBox();
17207         
17208         if (autoPlace) {
17209             // fixme..
17210         }
17211         var align = Roo.bootstrap.Popover.alignment[placement];
17212         this.el.alignTo(on_el, align[0],align[1]);
17213         //var arrow = this.el.select('.arrow',true).first();
17214         //arrow.set(align[2], 
17215         
17216         this.el.addClass('in');
17217         
17218         
17219         if (this.el.hasClass('fade')) {
17220             // fade it?
17221         }
17222         
17223         this.hoverState = 'in';
17224         
17225         this.fireEvent('show', this);
17226         
17227     },
17228     hide : function()
17229     {
17230         this.el.setXY([0,0]);
17231         this.el.removeClass('in');
17232         this.el.hide();
17233         this.hoverState = null;
17234         
17235         this.fireEvent('hide', this);
17236     }
17237     
17238 });
17239
17240 Roo.bootstrap.Popover.alignment = {
17241     'left' : ['r-l', [-10,0], 'right'],
17242     'right' : ['l-r', [10,0], 'left'],
17243     'bottom' : ['t-b', [0,10], 'top'],
17244     'top' : [ 'b-t', [0,-10], 'bottom']
17245 };
17246
17247  /*
17248  * - LGPL
17249  *
17250  * Progress
17251  * 
17252  */
17253
17254 /**
17255  * @class Roo.bootstrap.Progress
17256  * @extends Roo.bootstrap.Component
17257  * Bootstrap Progress class
17258  * @cfg {Boolean} striped striped of the progress bar
17259  * @cfg {Boolean} active animated of the progress bar
17260  * 
17261  * 
17262  * @constructor
17263  * Create a new Progress
17264  * @param {Object} config The config object
17265  */
17266
17267 Roo.bootstrap.Progress = function(config){
17268     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17269 };
17270
17271 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17272     
17273     striped : false,
17274     active: false,
17275     
17276     getAutoCreate : function(){
17277         var cfg = {
17278             tag: 'div',
17279             cls: 'progress'
17280         };
17281         
17282         
17283         if(this.striped){
17284             cfg.cls += ' progress-striped';
17285         }
17286       
17287         if(this.active){
17288             cfg.cls += ' active';
17289         }
17290         
17291         
17292         return cfg;
17293     }
17294    
17295 });
17296
17297  
17298
17299  /*
17300  * - LGPL
17301  *
17302  * ProgressBar
17303  * 
17304  */
17305
17306 /**
17307  * @class Roo.bootstrap.ProgressBar
17308  * @extends Roo.bootstrap.Component
17309  * Bootstrap ProgressBar class
17310  * @cfg {Number} aria_valuenow aria-value now
17311  * @cfg {Number} aria_valuemin aria-value min
17312  * @cfg {Number} aria_valuemax aria-value max
17313  * @cfg {String} label label for the progress bar
17314  * @cfg {String} panel (success | info | warning | danger )
17315  * @cfg {String} role role of the progress bar
17316  * @cfg {String} sr_only text
17317  * 
17318  * 
17319  * @constructor
17320  * Create a new ProgressBar
17321  * @param {Object} config The config object
17322  */
17323
17324 Roo.bootstrap.ProgressBar = function(config){
17325     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17326 };
17327
17328 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17329     
17330     aria_valuenow : 0,
17331     aria_valuemin : 0,
17332     aria_valuemax : 100,
17333     label : false,
17334     panel : false,
17335     role : false,
17336     sr_only: false,
17337     
17338     getAutoCreate : function()
17339     {
17340         
17341         var cfg = {
17342             tag: 'div',
17343             cls: 'progress-bar',
17344             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17345         };
17346         
17347         if(this.sr_only){
17348             cfg.cn = {
17349                 tag: 'span',
17350                 cls: 'sr-only',
17351                 html: this.sr_only
17352             }
17353         }
17354         
17355         if(this.role){
17356             cfg.role = this.role;
17357         }
17358         
17359         if(this.aria_valuenow){
17360             cfg['aria-valuenow'] = this.aria_valuenow;
17361         }
17362         
17363         if(this.aria_valuemin){
17364             cfg['aria-valuemin'] = this.aria_valuemin;
17365         }
17366         
17367         if(this.aria_valuemax){
17368             cfg['aria-valuemax'] = this.aria_valuemax;
17369         }
17370         
17371         if(this.label && !this.sr_only){
17372             cfg.html = this.label;
17373         }
17374         
17375         if(this.panel){
17376             cfg.cls += ' progress-bar-' + this.panel;
17377         }
17378         
17379         return cfg;
17380     },
17381     
17382     update : function(aria_valuenow)
17383     {
17384         this.aria_valuenow = aria_valuenow;
17385         
17386         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17387     }
17388    
17389 });
17390
17391  
17392
17393  /*
17394  * - LGPL
17395  *
17396  * column
17397  * 
17398  */
17399
17400 /**
17401  * @class Roo.bootstrap.TabGroup
17402  * @extends Roo.bootstrap.Column
17403  * Bootstrap Column class
17404  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17405  * @cfg {Boolean} carousel true to make the group behave like a carousel
17406  * @cfg {Boolean} bullets show bullets for the panels
17407  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17408  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17409  * @cfg {Boolean} showarrow (true|false) show arrow default true
17410  * 
17411  * @constructor
17412  * Create a new TabGroup
17413  * @param {Object} config The config object
17414  */
17415
17416 Roo.bootstrap.TabGroup = function(config){
17417     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17418     if (!this.navId) {
17419         this.navId = Roo.id();
17420     }
17421     this.tabs = [];
17422     Roo.bootstrap.TabGroup.register(this);
17423     
17424 };
17425
17426 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17427     
17428     carousel : false,
17429     transition : false,
17430     bullets : 0,
17431     timer : 0,
17432     autoslide : false,
17433     slideFn : false,
17434     slideOnTouch : false,
17435     showarrow : true,
17436     
17437     getAutoCreate : function()
17438     {
17439         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17440         
17441         cfg.cls += ' tab-content';
17442         
17443         if (this.carousel) {
17444             cfg.cls += ' carousel slide';
17445             
17446             cfg.cn = [{
17447                cls : 'carousel-inner',
17448                cn : []
17449             }];
17450         
17451             if(this.bullets  && !Roo.isTouch){
17452                 
17453                 var bullets = {
17454                     cls : 'carousel-bullets',
17455                     cn : []
17456                 };
17457                
17458                 if(this.bullets_cls){
17459                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17460                 }
17461                 
17462                 bullets.cn.push({
17463                     cls : 'clear'
17464                 });
17465                 
17466                 cfg.cn[0].cn.push(bullets);
17467             }
17468             
17469             if(this.showarrow){
17470                 cfg.cn[0].cn.push({
17471                     tag : 'div',
17472                     class : 'carousel-arrow',
17473                     cn : [
17474                         {
17475                             tag : 'div',
17476                             class : 'carousel-prev',
17477                             cn : [
17478                                 {
17479                                     tag : 'i',
17480                                     class : 'fa fa-chevron-left'
17481                                 }
17482                             ]
17483                         },
17484                         {
17485                             tag : 'div',
17486                             class : 'carousel-next',
17487                             cn : [
17488                                 {
17489                                     tag : 'i',
17490                                     class : 'fa fa-chevron-right'
17491                                 }
17492                             ]
17493                         }
17494                     ]
17495                 });
17496             }
17497             
17498         }
17499         
17500         return cfg;
17501     },
17502     
17503     initEvents:  function()
17504     {
17505 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17506 //            this.el.on("touchstart", this.onTouchStart, this);
17507 //        }
17508         
17509         if(this.autoslide){
17510             var _this = this;
17511             
17512             this.slideFn = window.setInterval(function() {
17513                 _this.showPanelNext();
17514             }, this.timer);
17515         }
17516         
17517         if(this.showarrow){
17518             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17519             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17520         }
17521         
17522         
17523     },
17524     
17525 //    onTouchStart : function(e, el, o)
17526 //    {
17527 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17528 //            return;
17529 //        }
17530 //        
17531 //        this.showPanelNext();
17532 //    },
17533     
17534     
17535     getChildContainer : function()
17536     {
17537         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17538     },
17539     
17540     /**
17541     * register a Navigation item
17542     * @param {Roo.bootstrap.NavItem} the navitem to add
17543     */
17544     register : function(item)
17545     {
17546         this.tabs.push( item);
17547         item.navId = this.navId; // not really needed..
17548         this.addBullet();
17549     
17550     },
17551     
17552     getActivePanel : function()
17553     {
17554         var r = false;
17555         Roo.each(this.tabs, function(t) {
17556             if (t.active) {
17557                 r = t;
17558                 return false;
17559             }
17560             return null;
17561         });
17562         return r;
17563         
17564     },
17565     getPanelByName : function(n)
17566     {
17567         var r = false;
17568         Roo.each(this.tabs, function(t) {
17569             if (t.tabId == n) {
17570                 r = t;
17571                 return false;
17572             }
17573             return null;
17574         });
17575         return r;
17576     },
17577     indexOfPanel : function(p)
17578     {
17579         var r = false;
17580         Roo.each(this.tabs, function(t,i) {
17581             if (t.tabId == p.tabId) {
17582                 r = i;
17583                 return false;
17584             }
17585             return null;
17586         });
17587         return r;
17588     },
17589     /**
17590      * show a specific panel
17591      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17592      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17593      */
17594     showPanel : function (pan)
17595     {
17596         if(this.transition || typeof(pan) == 'undefined'){
17597             Roo.log("waiting for the transitionend");
17598             return;
17599         }
17600         
17601         if (typeof(pan) == 'number') {
17602             pan = this.tabs[pan];
17603         }
17604         
17605         if (typeof(pan) == 'string') {
17606             pan = this.getPanelByName(pan);
17607         }
17608         
17609         var cur = this.getActivePanel();
17610         
17611         if(!pan || !cur){
17612             Roo.log('pan or acitve pan is undefined');
17613             return false;
17614         }
17615         
17616         if (pan.tabId == this.getActivePanel().tabId) {
17617             return true;
17618         }
17619         
17620         if (false === cur.fireEvent('beforedeactivate')) {
17621             return false;
17622         }
17623         
17624         if(this.bullets > 0 && !Roo.isTouch){
17625             this.setActiveBullet(this.indexOfPanel(pan));
17626         }
17627         
17628         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17629             
17630             this.transition = true;
17631             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17632             var lr = dir == 'next' ? 'left' : 'right';
17633             pan.el.addClass(dir); // or prev
17634             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17635             cur.el.addClass(lr); // or right
17636             pan.el.addClass(lr);
17637             
17638             var _this = this;
17639             cur.el.on('transitionend', function() {
17640                 Roo.log("trans end?");
17641                 
17642                 pan.el.removeClass([lr,dir]);
17643                 pan.setActive(true);
17644                 
17645                 cur.el.removeClass([lr]);
17646                 cur.setActive(false);
17647                 
17648                 _this.transition = false;
17649                 
17650             }, this, { single:  true } );
17651             
17652             return true;
17653         }
17654         
17655         cur.setActive(false);
17656         pan.setActive(true);
17657         
17658         return true;
17659         
17660     },
17661     showPanelNext : function()
17662     {
17663         var i = this.indexOfPanel(this.getActivePanel());
17664         
17665         if (i >= this.tabs.length - 1 && !this.autoslide) {
17666             return;
17667         }
17668         
17669         if (i >= this.tabs.length - 1 && this.autoslide) {
17670             i = -1;
17671         }
17672         
17673         this.showPanel(this.tabs[i+1]);
17674     },
17675     
17676     showPanelPrev : function()
17677     {
17678         var i = this.indexOfPanel(this.getActivePanel());
17679         
17680         if (i  < 1 && !this.autoslide) {
17681             return;
17682         }
17683         
17684         if (i < 1 && this.autoslide) {
17685             i = this.tabs.length;
17686         }
17687         
17688         this.showPanel(this.tabs[i-1]);
17689     },
17690     
17691     
17692     addBullet: function()
17693     {
17694         if(!this.bullets || Roo.isTouch){
17695             return;
17696         }
17697         var ctr = this.el.select('.carousel-bullets',true).first();
17698         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17699         var bullet = ctr.createChild({
17700             cls : 'bullet bullet-' + i
17701         },ctr.dom.lastChild);
17702         
17703         
17704         var _this = this;
17705         
17706         bullet.on('click', (function(e, el, o, ii, t){
17707
17708             e.preventDefault();
17709
17710             this.showPanel(ii);
17711
17712             if(this.autoslide && this.slideFn){
17713                 clearInterval(this.slideFn);
17714                 this.slideFn = window.setInterval(function() {
17715                     _this.showPanelNext();
17716                 }, this.timer);
17717             }
17718
17719         }).createDelegate(this, [i, bullet], true));
17720                 
17721         
17722     },
17723      
17724     setActiveBullet : function(i)
17725     {
17726         if(Roo.isTouch){
17727             return;
17728         }
17729         
17730         Roo.each(this.el.select('.bullet', true).elements, function(el){
17731             el.removeClass('selected');
17732         });
17733
17734         var bullet = this.el.select('.bullet-' + i, true).first();
17735         
17736         if(!bullet){
17737             return;
17738         }
17739         
17740         bullet.addClass('selected');
17741     }
17742     
17743     
17744   
17745 });
17746
17747  
17748
17749  
17750  
17751 Roo.apply(Roo.bootstrap.TabGroup, {
17752     
17753     groups: {},
17754      /**
17755     * register a Navigation Group
17756     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17757     */
17758     register : function(navgrp)
17759     {
17760         this.groups[navgrp.navId] = navgrp;
17761         
17762     },
17763     /**
17764     * fetch a Navigation Group based on the navigation ID
17765     * if one does not exist , it will get created.
17766     * @param {string} the navgroup to add
17767     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17768     */
17769     get: function(navId) {
17770         if (typeof(this.groups[navId]) == 'undefined') {
17771             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17772         }
17773         return this.groups[navId] ;
17774     }
17775     
17776     
17777     
17778 });
17779
17780  /*
17781  * - LGPL
17782  *
17783  * TabPanel
17784  * 
17785  */
17786
17787 /**
17788  * @class Roo.bootstrap.TabPanel
17789  * @extends Roo.bootstrap.Component
17790  * Bootstrap TabPanel class
17791  * @cfg {Boolean} active panel active
17792  * @cfg {String} html panel content
17793  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17794  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17795  * @cfg {String} href click to link..
17796  * 
17797  * 
17798  * @constructor
17799  * Create a new TabPanel
17800  * @param {Object} config The config object
17801  */
17802
17803 Roo.bootstrap.TabPanel = function(config){
17804     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17805     this.addEvents({
17806         /**
17807              * @event changed
17808              * Fires when the active status changes
17809              * @param {Roo.bootstrap.TabPanel} this
17810              * @param {Boolean} state the new state
17811             
17812          */
17813         'changed': true,
17814         /**
17815              * @event beforedeactivate
17816              * Fires before a tab is de-activated - can be used to do validation on a form.
17817              * @param {Roo.bootstrap.TabPanel} this
17818              * @return {Boolean} false if there is an error
17819             
17820          */
17821         'beforedeactivate': true
17822      });
17823     
17824     this.tabId = this.tabId || Roo.id();
17825   
17826 };
17827
17828 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17829     
17830     active: false,
17831     html: false,
17832     tabId: false,
17833     navId : false,
17834     href : '',
17835     
17836     getAutoCreate : function(){
17837         var cfg = {
17838             tag: 'div',
17839             // item is needed for carousel - not sure if it has any effect otherwise
17840             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17841             html: this.html || ''
17842         };
17843         
17844         if(this.active){
17845             cfg.cls += ' active';
17846         }
17847         
17848         if(this.tabId){
17849             cfg.tabId = this.tabId;
17850         }
17851         
17852         
17853         return cfg;
17854     },
17855     
17856     initEvents:  function()
17857     {
17858         var p = this.parent();
17859         
17860         this.navId = this.navId || p.navId;
17861         
17862         if (typeof(this.navId) != 'undefined') {
17863             // not really needed.. but just in case.. parent should be a NavGroup.
17864             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17865             
17866             tg.register(this);
17867             
17868             var i = tg.tabs.length - 1;
17869             
17870             if(this.active && tg.bullets > 0 && i < tg.bullets){
17871                 tg.setActiveBullet(i);
17872             }
17873         }
17874         
17875         this.el.on('click', this.onClick, this);
17876         
17877         if(Roo.isTouch){
17878             this.el.on("touchstart", this.onTouchStart, this);
17879             this.el.on("touchmove", this.onTouchMove, this);
17880             this.el.on("touchend", this.onTouchEnd, this);
17881         }
17882         
17883     },
17884     
17885     onRender : function(ct, position)
17886     {
17887         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17888     },
17889     
17890     setActive : function(state)
17891     {
17892         Roo.log("panel - set active " + this.tabId + "=" + state);
17893         
17894         this.active = state;
17895         if (!state) {
17896             this.el.removeClass('active');
17897             
17898         } else  if (!this.el.hasClass('active')) {
17899             this.el.addClass('active');
17900         }
17901         
17902         this.fireEvent('changed', this, state);
17903     },
17904     
17905     onClick : function(e)
17906     {
17907         e.preventDefault();
17908         
17909         if(!this.href.length){
17910             return;
17911         }
17912         
17913         window.location.href = this.href;
17914     },
17915     
17916     startX : 0,
17917     startY : 0,
17918     endX : 0,
17919     endY : 0,
17920     swiping : false,
17921     
17922     onTouchStart : function(e)
17923     {
17924         this.swiping = false;
17925         
17926         this.startX = e.browserEvent.touches[0].clientX;
17927         this.startY = e.browserEvent.touches[0].clientY;
17928     },
17929     
17930     onTouchMove : function(e)
17931     {
17932         this.swiping = true;
17933         
17934         this.endX = e.browserEvent.touches[0].clientX;
17935         this.endY = e.browserEvent.touches[0].clientY;
17936     },
17937     
17938     onTouchEnd : function(e)
17939     {
17940         if(!this.swiping){
17941             this.onClick(e);
17942             return;
17943         }
17944         
17945         var tabGroup = this.parent();
17946         
17947         if(this.endX > this.startX){ // swiping right
17948             tabGroup.showPanelPrev();
17949             return;
17950         }
17951         
17952         if(this.startX > this.endX){ // swiping left
17953             tabGroup.showPanelNext();
17954             return;
17955         }
17956     }
17957     
17958     
17959 });
17960  
17961
17962  
17963
17964  /*
17965  * - LGPL
17966  *
17967  * DateField
17968  * 
17969  */
17970
17971 /**
17972  * @class Roo.bootstrap.DateField
17973  * @extends Roo.bootstrap.Input
17974  * Bootstrap DateField class
17975  * @cfg {Number} weekStart default 0
17976  * @cfg {String} viewMode default empty, (months|years)
17977  * @cfg {String} minViewMode default empty, (months|years)
17978  * @cfg {Number} startDate default -Infinity
17979  * @cfg {Number} endDate default Infinity
17980  * @cfg {Boolean} todayHighlight default false
17981  * @cfg {Boolean} todayBtn default false
17982  * @cfg {Boolean} calendarWeeks default false
17983  * @cfg {Object} daysOfWeekDisabled default empty
17984  * @cfg {Boolean} singleMode default false (true | false)
17985  * 
17986  * @cfg {Boolean} keyboardNavigation default true
17987  * @cfg {String} language default en
17988  * 
17989  * @constructor
17990  * Create a new DateField
17991  * @param {Object} config The config object
17992  */
17993
17994 Roo.bootstrap.DateField = function(config){
17995     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17996      this.addEvents({
17997             /**
17998              * @event show
17999              * Fires when this field show.
18000              * @param {Roo.bootstrap.DateField} this
18001              * @param {Mixed} date The date value
18002              */
18003             show : true,
18004             /**
18005              * @event show
18006              * Fires when this field hide.
18007              * @param {Roo.bootstrap.DateField} this
18008              * @param {Mixed} date The date value
18009              */
18010             hide : true,
18011             /**
18012              * @event select
18013              * Fires when select a date.
18014              * @param {Roo.bootstrap.DateField} this
18015              * @param {Mixed} date The date value
18016              */
18017             select : true,
18018             /**
18019              * @event beforeselect
18020              * Fires when before select a date.
18021              * @param {Roo.bootstrap.DateField} this
18022              * @param {Mixed} date The date value
18023              */
18024             beforeselect : true
18025         });
18026 };
18027
18028 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18029     
18030     /**
18031      * @cfg {String} format
18032      * The default date format string which can be overriden for localization support.  The format must be
18033      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18034      */
18035     format : "m/d/y",
18036     /**
18037      * @cfg {String} altFormats
18038      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18039      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18040      */
18041     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18042     
18043     weekStart : 0,
18044     
18045     viewMode : '',
18046     
18047     minViewMode : '',
18048     
18049     todayHighlight : false,
18050     
18051     todayBtn: false,
18052     
18053     language: 'en',
18054     
18055     keyboardNavigation: true,
18056     
18057     calendarWeeks: false,
18058     
18059     startDate: -Infinity,
18060     
18061     endDate: Infinity,
18062     
18063     daysOfWeekDisabled: [],
18064     
18065     _events: [],
18066     
18067     singleMode : false,
18068     
18069     UTCDate: function()
18070     {
18071         return new Date(Date.UTC.apply(Date, arguments));
18072     },
18073     
18074     UTCToday: function()
18075     {
18076         var today = new Date();
18077         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18078     },
18079     
18080     getDate: function() {
18081             var d = this.getUTCDate();
18082             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18083     },
18084     
18085     getUTCDate: function() {
18086             return this.date;
18087     },
18088     
18089     setDate: function(d) {
18090             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18091     },
18092     
18093     setUTCDate: function(d) {
18094             this.date = d;
18095             this.setValue(this.formatDate(this.date));
18096     },
18097         
18098     onRender: function(ct, position)
18099     {
18100         
18101         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18102         
18103         this.language = this.language || 'en';
18104         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18105         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18106         
18107         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18108         this.format = this.format || 'm/d/y';
18109         this.isInline = false;
18110         this.isInput = true;
18111         this.component = this.el.select('.add-on', true).first() || false;
18112         this.component = (this.component && this.component.length === 0) ? false : this.component;
18113         this.hasInput = this.component && this.inputEl().length;
18114         
18115         if (typeof(this.minViewMode === 'string')) {
18116             switch (this.minViewMode) {
18117                 case 'months':
18118                     this.minViewMode = 1;
18119                     break;
18120                 case 'years':
18121                     this.minViewMode = 2;
18122                     break;
18123                 default:
18124                     this.minViewMode = 0;
18125                     break;
18126             }
18127         }
18128         
18129         if (typeof(this.viewMode === 'string')) {
18130             switch (this.viewMode) {
18131                 case 'months':
18132                     this.viewMode = 1;
18133                     break;
18134                 case 'years':
18135                     this.viewMode = 2;
18136                     break;
18137                 default:
18138                     this.viewMode = 0;
18139                     break;
18140             }
18141         }
18142                 
18143         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18144         
18145 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18146         
18147         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18148         
18149         this.picker().on('mousedown', this.onMousedown, this);
18150         this.picker().on('click', this.onClick, this);
18151         
18152         this.picker().addClass('datepicker-dropdown');
18153         
18154         this.startViewMode = this.viewMode;
18155         
18156         if(this.singleMode){
18157             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18158                 v.setVisibilityMode(Roo.Element.DISPLAY);
18159                 v.hide();
18160             });
18161             
18162             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18163                 v.setStyle('width', '189px');
18164             });
18165         }
18166         
18167         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18168             if(!this.calendarWeeks){
18169                 v.remove();
18170                 return;
18171             }
18172             
18173             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18174             v.attr('colspan', function(i, val){
18175                 return parseInt(val) + 1;
18176             });
18177         });
18178                         
18179         
18180         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18181         
18182         this.setStartDate(this.startDate);
18183         this.setEndDate(this.endDate);
18184         
18185         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18186         
18187         this.fillDow();
18188         this.fillMonths();
18189         this.update();
18190         this.showMode();
18191         
18192         if(this.isInline) {
18193             this.show();
18194         }
18195     },
18196     
18197     picker : function()
18198     {
18199         return this.pickerEl;
18200 //        return this.el.select('.datepicker', true).first();
18201     },
18202     
18203     fillDow: function()
18204     {
18205         var dowCnt = this.weekStart;
18206         
18207         var dow = {
18208             tag: 'tr',
18209             cn: [
18210                 
18211             ]
18212         };
18213         
18214         if(this.calendarWeeks){
18215             dow.cn.push({
18216                 tag: 'th',
18217                 cls: 'cw',
18218                 html: '&nbsp;'
18219             })
18220         }
18221         
18222         while (dowCnt < this.weekStart + 7) {
18223             dow.cn.push({
18224                 tag: 'th',
18225                 cls: 'dow',
18226                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18227             });
18228         }
18229         
18230         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18231     },
18232     
18233     fillMonths: function()
18234     {    
18235         var i = 0;
18236         var months = this.picker().select('>.datepicker-months td', true).first();
18237         
18238         months.dom.innerHTML = '';
18239         
18240         while (i < 12) {
18241             var month = {
18242                 tag: 'span',
18243                 cls: 'month',
18244                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18245             };
18246             
18247             months.createChild(month);
18248         }
18249         
18250     },
18251     
18252     update: function()
18253     {
18254         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;
18255         
18256         if (this.date < this.startDate) {
18257             this.viewDate = new Date(this.startDate);
18258         } else if (this.date > this.endDate) {
18259             this.viewDate = new Date(this.endDate);
18260         } else {
18261             this.viewDate = new Date(this.date);
18262         }
18263         
18264         this.fill();
18265     },
18266     
18267     fill: function() 
18268     {
18269         var d = new Date(this.viewDate),
18270                 year = d.getUTCFullYear(),
18271                 month = d.getUTCMonth(),
18272                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18273                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18274                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18275                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18276                 currentDate = this.date && this.date.valueOf(),
18277                 today = this.UTCToday();
18278         
18279         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18280         
18281 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18282         
18283 //        this.picker.select('>tfoot th.today').
18284 //                                              .text(dates[this.language].today)
18285 //                                              .toggle(this.todayBtn !== false);
18286     
18287         this.updateNavArrows();
18288         this.fillMonths();
18289                                                 
18290         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18291         
18292         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18293          
18294         prevMonth.setUTCDate(day);
18295         
18296         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18297         
18298         var nextMonth = new Date(prevMonth);
18299         
18300         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18301         
18302         nextMonth = nextMonth.valueOf();
18303         
18304         var fillMonths = false;
18305         
18306         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18307         
18308         while(prevMonth.valueOf() < nextMonth) {
18309             var clsName = '';
18310             
18311             if (prevMonth.getUTCDay() === this.weekStart) {
18312                 if(fillMonths){
18313                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18314                 }
18315                     
18316                 fillMonths = {
18317                     tag: 'tr',
18318                     cn: []
18319                 };
18320                 
18321                 if(this.calendarWeeks){
18322                     // ISO 8601: First week contains first thursday.
18323                     // ISO also states week starts on Monday, but we can be more abstract here.
18324                     var
18325                     // Start of current week: based on weekstart/current date
18326                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18327                     // Thursday of this week
18328                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18329                     // First Thursday of year, year from thursday
18330                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18331                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18332                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18333                     
18334                     fillMonths.cn.push({
18335                         tag: 'td',
18336                         cls: 'cw',
18337                         html: calWeek
18338                     });
18339                 }
18340             }
18341             
18342             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18343                 clsName += ' old';
18344             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18345                 clsName += ' new';
18346             }
18347             if (this.todayHighlight &&
18348                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18349                 prevMonth.getUTCMonth() == today.getMonth() &&
18350                 prevMonth.getUTCDate() == today.getDate()) {
18351                 clsName += ' today';
18352             }
18353             
18354             if (currentDate && prevMonth.valueOf() === currentDate) {
18355                 clsName += ' active';
18356             }
18357             
18358             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18359                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18360                     clsName += ' disabled';
18361             }
18362             
18363             fillMonths.cn.push({
18364                 tag: 'td',
18365                 cls: 'day ' + clsName,
18366                 html: prevMonth.getDate()
18367             });
18368             
18369             prevMonth.setDate(prevMonth.getDate()+1);
18370         }
18371           
18372         var currentYear = this.date && this.date.getUTCFullYear();
18373         var currentMonth = this.date && this.date.getUTCMonth();
18374         
18375         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18376         
18377         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18378             v.removeClass('active');
18379             
18380             if(currentYear === year && k === currentMonth){
18381                 v.addClass('active');
18382             }
18383             
18384             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18385                 v.addClass('disabled');
18386             }
18387             
18388         });
18389         
18390         
18391         year = parseInt(year/10, 10) * 10;
18392         
18393         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18394         
18395         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18396         
18397         year -= 1;
18398         for (var i = -1; i < 11; i++) {
18399             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18400                 tag: 'span',
18401                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18402                 html: year
18403             });
18404             
18405             year += 1;
18406         }
18407     },
18408     
18409     showMode: function(dir) 
18410     {
18411         if (dir) {
18412             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18413         }
18414         
18415         Roo.each(this.picker().select('>div',true).elements, function(v){
18416             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18417             v.hide();
18418         });
18419         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18420     },
18421     
18422     place: function()
18423     {
18424         if(this.isInline) {
18425             return;
18426         }
18427         
18428         this.picker().removeClass(['bottom', 'top']);
18429         
18430         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18431             /*
18432              * place to the top of element!
18433              *
18434              */
18435             
18436             this.picker().addClass('top');
18437             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18438             
18439             return;
18440         }
18441         
18442         this.picker().addClass('bottom');
18443         
18444         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18445     },
18446     
18447     parseDate : function(value)
18448     {
18449         if(!value || value instanceof Date){
18450             return value;
18451         }
18452         var v = Date.parseDate(value, this.format);
18453         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18454             v = Date.parseDate(value, 'Y-m-d');
18455         }
18456         if(!v && this.altFormats){
18457             if(!this.altFormatsArray){
18458                 this.altFormatsArray = this.altFormats.split("|");
18459             }
18460             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18461                 v = Date.parseDate(value, this.altFormatsArray[i]);
18462             }
18463         }
18464         return v;
18465     },
18466     
18467     formatDate : function(date, fmt)
18468     {   
18469         return (!date || !(date instanceof Date)) ?
18470         date : date.dateFormat(fmt || this.format);
18471     },
18472     
18473     onFocus : function()
18474     {
18475         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18476         this.show();
18477     },
18478     
18479     onBlur : function()
18480     {
18481         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18482         
18483         var d = this.inputEl().getValue();
18484         
18485         this.setValue(d);
18486                 
18487         this.hide();
18488     },
18489     
18490     show : function()
18491     {
18492         this.picker().show();
18493         this.update();
18494         this.place();
18495         
18496         this.fireEvent('show', this, this.date);
18497     },
18498     
18499     hide : function()
18500     {
18501         if(this.isInline) {
18502             return;
18503         }
18504         this.picker().hide();
18505         this.viewMode = this.startViewMode;
18506         this.showMode();
18507         
18508         this.fireEvent('hide', this, this.date);
18509         
18510     },
18511     
18512     onMousedown: function(e)
18513     {
18514         e.stopPropagation();
18515         e.preventDefault();
18516     },
18517     
18518     keyup: function(e)
18519     {
18520         Roo.bootstrap.DateField.superclass.keyup.call(this);
18521         this.update();
18522     },
18523
18524     setValue: function(v)
18525     {
18526         if(this.fireEvent('beforeselect', this, v) !== false){
18527             var d = new Date(this.parseDate(v) ).clearTime();
18528         
18529             if(isNaN(d.getTime())){
18530                 this.date = this.viewDate = '';
18531                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18532                 return;
18533             }
18534
18535             v = this.formatDate(d);
18536
18537             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18538
18539             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18540
18541             this.update();
18542
18543             this.fireEvent('select', this, this.date);
18544         }
18545     },
18546     
18547     getValue: function()
18548     {
18549         return this.formatDate(this.date);
18550     },
18551     
18552     fireKey: function(e)
18553     {
18554         if (!this.picker().isVisible()){
18555             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18556                 this.show();
18557             }
18558             return;
18559         }
18560         
18561         var dateChanged = false,
18562         dir, day, month,
18563         newDate, newViewDate;
18564         
18565         switch(e.keyCode){
18566             case 27: // escape
18567                 this.hide();
18568                 e.preventDefault();
18569                 break;
18570             case 37: // left
18571             case 39: // right
18572                 if (!this.keyboardNavigation) {
18573                     break;
18574                 }
18575                 dir = e.keyCode == 37 ? -1 : 1;
18576                 
18577                 if (e.ctrlKey){
18578                     newDate = this.moveYear(this.date, dir);
18579                     newViewDate = this.moveYear(this.viewDate, dir);
18580                 } else if (e.shiftKey){
18581                     newDate = this.moveMonth(this.date, dir);
18582                     newViewDate = this.moveMonth(this.viewDate, dir);
18583                 } else {
18584                     newDate = new Date(this.date);
18585                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18586                     newViewDate = new Date(this.viewDate);
18587                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18588                 }
18589                 if (this.dateWithinRange(newDate)){
18590                     this.date = newDate;
18591                     this.viewDate = newViewDate;
18592                     this.setValue(this.formatDate(this.date));
18593 //                    this.update();
18594                     e.preventDefault();
18595                     dateChanged = true;
18596                 }
18597                 break;
18598             case 38: // up
18599             case 40: // down
18600                 if (!this.keyboardNavigation) {
18601                     break;
18602                 }
18603                 dir = e.keyCode == 38 ? -1 : 1;
18604                 if (e.ctrlKey){
18605                     newDate = this.moveYear(this.date, dir);
18606                     newViewDate = this.moveYear(this.viewDate, dir);
18607                 } else if (e.shiftKey){
18608                     newDate = this.moveMonth(this.date, dir);
18609                     newViewDate = this.moveMonth(this.viewDate, dir);
18610                 } else {
18611                     newDate = new Date(this.date);
18612                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18613                     newViewDate = new Date(this.viewDate);
18614                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18615                 }
18616                 if (this.dateWithinRange(newDate)){
18617                     this.date = newDate;
18618                     this.viewDate = newViewDate;
18619                     this.setValue(this.formatDate(this.date));
18620 //                    this.update();
18621                     e.preventDefault();
18622                     dateChanged = true;
18623                 }
18624                 break;
18625             case 13: // enter
18626                 this.setValue(this.formatDate(this.date));
18627                 this.hide();
18628                 e.preventDefault();
18629                 break;
18630             case 9: // tab
18631                 this.setValue(this.formatDate(this.date));
18632                 this.hide();
18633                 break;
18634             case 16: // shift
18635             case 17: // ctrl
18636             case 18: // alt
18637                 break;
18638             default :
18639                 this.hide();
18640                 
18641         }
18642     },
18643     
18644     
18645     onClick: function(e) 
18646     {
18647         e.stopPropagation();
18648         e.preventDefault();
18649         
18650         var target = e.getTarget();
18651         
18652         if(target.nodeName.toLowerCase() === 'i'){
18653             target = Roo.get(target).dom.parentNode;
18654         }
18655         
18656         var nodeName = target.nodeName;
18657         var className = target.className;
18658         var html = target.innerHTML;
18659         //Roo.log(nodeName);
18660         
18661         switch(nodeName.toLowerCase()) {
18662             case 'th':
18663                 switch(className) {
18664                     case 'switch':
18665                         this.showMode(1);
18666                         break;
18667                     case 'prev':
18668                     case 'next':
18669                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18670                         switch(this.viewMode){
18671                                 case 0:
18672                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18673                                         break;
18674                                 case 1:
18675                                 case 2:
18676                                         this.viewDate = this.moveYear(this.viewDate, dir);
18677                                         break;
18678                         }
18679                         this.fill();
18680                         break;
18681                     case 'today':
18682                         var date = new Date();
18683                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18684 //                        this.fill()
18685                         this.setValue(this.formatDate(this.date));
18686                         
18687                         this.hide();
18688                         break;
18689                 }
18690                 break;
18691             case 'span':
18692                 if (className.indexOf('disabled') < 0) {
18693                     this.viewDate.setUTCDate(1);
18694                     if (className.indexOf('month') > -1) {
18695                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18696                     } else {
18697                         var year = parseInt(html, 10) || 0;
18698                         this.viewDate.setUTCFullYear(year);
18699                         
18700                     }
18701                     
18702                     if(this.singleMode){
18703                         this.setValue(this.formatDate(this.viewDate));
18704                         this.hide();
18705                         return;
18706                     }
18707                     
18708                     this.showMode(-1);
18709                     this.fill();
18710                 }
18711                 break;
18712                 
18713             case 'td':
18714                 //Roo.log(className);
18715                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18716                     var day = parseInt(html, 10) || 1;
18717                     var year = this.viewDate.getUTCFullYear(),
18718                         month = this.viewDate.getUTCMonth();
18719
18720                     if (className.indexOf('old') > -1) {
18721                         if(month === 0 ){
18722                             month = 11;
18723                             year -= 1;
18724                         }else{
18725                             month -= 1;
18726                         }
18727                     } else if (className.indexOf('new') > -1) {
18728                         if (month == 11) {
18729                             month = 0;
18730                             year += 1;
18731                         } else {
18732                             month += 1;
18733                         }
18734                     }
18735                     //Roo.log([year,month,day]);
18736                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18737                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18738 //                    this.fill();
18739                     //Roo.log(this.formatDate(this.date));
18740                     this.setValue(this.formatDate(this.date));
18741                     this.hide();
18742                 }
18743                 break;
18744         }
18745     },
18746     
18747     setStartDate: function(startDate)
18748     {
18749         this.startDate = startDate || -Infinity;
18750         if (this.startDate !== -Infinity) {
18751             this.startDate = this.parseDate(this.startDate);
18752         }
18753         this.update();
18754         this.updateNavArrows();
18755     },
18756
18757     setEndDate: function(endDate)
18758     {
18759         this.endDate = endDate || Infinity;
18760         if (this.endDate !== Infinity) {
18761             this.endDate = this.parseDate(this.endDate);
18762         }
18763         this.update();
18764         this.updateNavArrows();
18765     },
18766     
18767     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18768     {
18769         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18770         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18771             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18772         }
18773         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18774             return parseInt(d, 10);
18775         });
18776         this.update();
18777         this.updateNavArrows();
18778     },
18779     
18780     updateNavArrows: function() 
18781     {
18782         if(this.singleMode){
18783             return;
18784         }
18785         
18786         var d = new Date(this.viewDate),
18787         year = d.getUTCFullYear(),
18788         month = d.getUTCMonth();
18789         
18790         Roo.each(this.picker().select('.prev', true).elements, function(v){
18791             v.show();
18792             switch (this.viewMode) {
18793                 case 0:
18794
18795                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18796                         v.hide();
18797                     }
18798                     break;
18799                 case 1:
18800                 case 2:
18801                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18802                         v.hide();
18803                     }
18804                     break;
18805             }
18806         });
18807         
18808         Roo.each(this.picker().select('.next', true).elements, function(v){
18809             v.show();
18810             switch (this.viewMode) {
18811                 case 0:
18812
18813                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18814                         v.hide();
18815                     }
18816                     break;
18817                 case 1:
18818                 case 2:
18819                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18820                         v.hide();
18821                     }
18822                     break;
18823             }
18824         })
18825     },
18826     
18827     moveMonth: function(date, dir)
18828     {
18829         if (!dir) {
18830             return date;
18831         }
18832         var new_date = new Date(date.valueOf()),
18833         day = new_date.getUTCDate(),
18834         month = new_date.getUTCMonth(),
18835         mag = Math.abs(dir),
18836         new_month, test;
18837         dir = dir > 0 ? 1 : -1;
18838         if (mag == 1){
18839             test = dir == -1
18840             // If going back one month, make sure month is not current month
18841             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18842             ? function(){
18843                 return new_date.getUTCMonth() == month;
18844             }
18845             // If going forward one month, make sure month is as expected
18846             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18847             : function(){
18848                 return new_date.getUTCMonth() != new_month;
18849             };
18850             new_month = month + dir;
18851             new_date.setUTCMonth(new_month);
18852             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18853             if (new_month < 0 || new_month > 11) {
18854                 new_month = (new_month + 12) % 12;
18855             }
18856         } else {
18857             // For magnitudes >1, move one month at a time...
18858             for (var i=0; i<mag; i++) {
18859                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18860                 new_date = this.moveMonth(new_date, dir);
18861             }
18862             // ...then reset the day, keeping it in the new month
18863             new_month = new_date.getUTCMonth();
18864             new_date.setUTCDate(day);
18865             test = function(){
18866                 return new_month != new_date.getUTCMonth();
18867             };
18868         }
18869         // Common date-resetting loop -- if date is beyond end of month, make it
18870         // end of month
18871         while (test()){
18872             new_date.setUTCDate(--day);
18873             new_date.setUTCMonth(new_month);
18874         }
18875         return new_date;
18876     },
18877
18878     moveYear: function(date, dir)
18879     {
18880         return this.moveMonth(date, dir*12);
18881     },
18882
18883     dateWithinRange: function(date)
18884     {
18885         return date >= this.startDate && date <= this.endDate;
18886     },
18887
18888     
18889     remove: function() 
18890     {
18891         this.picker().remove();
18892     },
18893     
18894     validateValue : function(value)
18895     {
18896         if(value.length < 1)  {
18897             if(this.allowBlank){
18898                 return true;
18899             }
18900             return false;
18901         }
18902         
18903         if(value.length < this.minLength){
18904             return false;
18905         }
18906         if(value.length > this.maxLength){
18907             return false;
18908         }
18909         if(this.vtype){
18910             var vt = Roo.form.VTypes;
18911             if(!vt[this.vtype](value, this)){
18912                 return false;
18913             }
18914         }
18915         if(typeof this.validator == "function"){
18916             var msg = this.validator(value);
18917             if(msg !== true){
18918                 return false;
18919             }
18920         }
18921         
18922         if(this.regex && !this.regex.test(value)){
18923             return false;
18924         }
18925         
18926         if(typeof(this.parseDate(value)) == 'undefined'){
18927             return false;
18928         }
18929         
18930         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18931             return false;
18932         }      
18933         
18934         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18935             return false;
18936         } 
18937         
18938         
18939         return true;
18940     }
18941    
18942 });
18943
18944 Roo.apply(Roo.bootstrap.DateField,  {
18945     
18946     head : {
18947         tag: 'thead',
18948         cn: [
18949         {
18950             tag: 'tr',
18951             cn: [
18952             {
18953                 tag: 'th',
18954                 cls: 'prev',
18955                 html: '<i class="fa fa-arrow-left"/>'
18956             },
18957             {
18958                 tag: 'th',
18959                 cls: 'switch',
18960                 colspan: '5'
18961             },
18962             {
18963                 tag: 'th',
18964                 cls: 'next',
18965                 html: '<i class="fa fa-arrow-right"/>'
18966             }
18967
18968             ]
18969         }
18970         ]
18971     },
18972     
18973     content : {
18974         tag: 'tbody',
18975         cn: [
18976         {
18977             tag: 'tr',
18978             cn: [
18979             {
18980                 tag: 'td',
18981                 colspan: '7'
18982             }
18983             ]
18984         }
18985         ]
18986     },
18987     
18988     footer : {
18989         tag: 'tfoot',
18990         cn: [
18991         {
18992             tag: 'tr',
18993             cn: [
18994             {
18995                 tag: 'th',
18996                 colspan: '7',
18997                 cls: 'today'
18998             }
18999                     
19000             ]
19001         }
19002         ]
19003     },
19004     
19005     dates:{
19006         en: {
19007             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19008             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19009             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19010             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19011             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19012             today: "Today"
19013         }
19014     },
19015     
19016     modes: [
19017     {
19018         clsName: 'days',
19019         navFnc: 'Month',
19020         navStep: 1
19021     },
19022     {
19023         clsName: 'months',
19024         navFnc: 'FullYear',
19025         navStep: 1
19026     },
19027     {
19028         clsName: 'years',
19029         navFnc: 'FullYear',
19030         navStep: 10
19031     }]
19032 });
19033
19034 Roo.apply(Roo.bootstrap.DateField,  {
19035   
19036     template : {
19037         tag: 'div',
19038         cls: 'datepicker dropdown-menu roo-dynamic',
19039         cn: [
19040         {
19041             tag: 'div',
19042             cls: 'datepicker-days',
19043             cn: [
19044             {
19045                 tag: 'table',
19046                 cls: 'table-condensed',
19047                 cn:[
19048                 Roo.bootstrap.DateField.head,
19049                 {
19050                     tag: 'tbody'
19051                 },
19052                 Roo.bootstrap.DateField.footer
19053                 ]
19054             }
19055             ]
19056         },
19057         {
19058             tag: 'div',
19059             cls: 'datepicker-months',
19060             cn: [
19061             {
19062                 tag: 'table',
19063                 cls: 'table-condensed',
19064                 cn:[
19065                 Roo.bootstrap.DateField.head,
19066                 Roo.bootstrap.DateField.content,
19067                 Roo.bootstrap.DateField.footer
19068                 ]
19069             }
19070             ]
19071         },
19072         {
19073             tag: 'div',
19074             cls: 'datepicker-years',
19075             cn: [
19076             {
19077                 tag: 'table',
19078                 cls: 'table-condensed',
19079                 cn:[
19080                 Roo.bootstrap.DateField.head,
19081                 Roo.bootstrap.DateField.content,
19082                 Roo.bootstrap.DateField.footer
19083                 ]
19084             }
19085             ]
19086         }
19087         ]
19088     }
19089 });
19090
19091  
19092
19093  /*
19094  * - LGPL
19095  *
19096  * TimeField
19097  * 
19098  */
19099
19100 /**
19101  * @class Roo.bootstrap.TimeField
19102  * @extends Roo.bootstrap.Input
19103  * Bootstrap DateField class
19104  * 
19105  * 
19106  * @constructor
19107  * Create a new TimeField
19108  * @param {Object} config The config object
19109  */
19110
19111 Roo.bootstrap.TimeField = function(config){
19112     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19113     this.addEvents({
19114             /**
19115              * @event show
19116              * Fires when this field show.
19117              * @param {Roo.bootstrap.DateField} thisthis
19118              * @param {Mixed} date The date value
19119              */
19120             show : true,
19121             /**
19122              * @event show
19123              * Fires when this field hide.
19124              * @param {Roo.bootstrap.DateField} this
19125              * @param {Mixed} date The date value
19126              */
19127             hide : true,
19128             /**
19129              * @event select
19130              * Fires when select a date.
19131              * @param {Roo.bootstrap.DateField} this
19132              * @param {Mixed} date The date value
19133              */
19134             select : true
19135         });
19136 };
19137
19138 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19139     
19140     /**
19141      * @cfg {String} format
19142      * The default time format string which can be overriden for localization support.  The format must be
19143      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19144      */
19145     format : "H:i",
19146        
19147     onRender: function(ct, position)
19148     {
19149         
19150         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19151                 
19152         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19153         
19154         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19155         
19156         this.pop = this.picker().select('>.datepicker-time',true).first();
19157         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19158         
19159         this.picker().on('mousedown', this.onMousedown, this);
19160         this.picker().on('click', this.onClick, this);
19161         
19162         this.picker().addClass('datepicker-dropdown');
19163     
19164         this.fillTime();
19165         this.update();
19166             
19167         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19168         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19169         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19170         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19171         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19172         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19173
19174     },
19175     
19176     fireKey: function(e){
19177         if (!this.picker().isVisible()){
19178             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19179                 this.show();
19180             }
19181             return;
19182         }
19183
19184         e.preventDefault();
19185         
19186         switch(e.keyCode){
19187             case 27: // escape
19188                 this.hide();
19189                 break;
19190             case 37: // left
19191             case 39: // right
19192                 this.onTogglePeriod();
19193                 break;
19194             case 38: // up
19195                 this.onIncrementMinutes();
19196                 break;
19197             case 40: // down
19198                 this.onDecrementMinutes();
19199                 break;
19200             case 13: // enter
19201             case 9: // tab
19202                 this.setTime();
19203                 break;
19204         }
19205     },
19206     
19207     onClick: function(e) {
19208         e.stopPropagation();
19209         e.preventDefault();
19210     },
19211     
19212     picker : function()
19213     {
19214         return this.el.select('.datepicker', true).first();
19215     },
19216     
19217     fillTime: function()
19218     {    
19219         var time = this.pop.select('tbody', true).first();
19220         
19221         time.dom.innerHTML = '';
19222         
19223         time.createChild({
19224             tag: 'tr',
19225             cn: [
19226                 {
19227                     tag: 'td',
19228                     cn: [
19229                         {
19230                             tag: 'a',
19231                             href: '#',
19232                             cls: 'btn',
19233                             cn: [
19234                                 {
19235                                     tag: 'span',
19236                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19237                                 }
19238                             ]
19239                         } 
19240                     ]
19241                 },
19242                 {
19243                     tag: 'td',
19244                     cls: 'separator'
19245                 },
19246                 {
19247                     tag: 'td',
19248                     cn: [
19249                         {
19250                             tag: 'a',
19251                             href: '#',
19252                             cls: 'btn',
19253                             cn: [
19254                                 {
19255                                     tag: 'span',
19256                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19257                                 }
19258                             ]
19259                         }
19260                     ]
19261                 },
19262                 {
19263                     tag: 'td',
19264                     cls: 'separator'
19265                 }
19266             ]
19267         });
19268         
19269         time.createChild({
19270             tag: 'tr',
19271             cn: [
19272                 {
19273                     tag: 'td',
19274                     cn: [
19275                         {
19276                             tag: 'span',
19277                             cls: 'timepicker-hour',
19278                             html: '00'
19279                         }  
19280                     ]
19281                 },
19282                 {
19283                     tag: 'td',
19284                     cls: 'separator',
19285                     html: ':'
19286                 },
19287                 {
19288                     tag: 'td',
19289                     cn: [
19290                         {
19291                             tag: 'span',
19292                             cls: 'timepicker-minute',
19293                             html: '00'
19294                         }  
19295                     ]
19296                 },
19297                 {
19298                     tag: 'td',
19299                     cls: 'separator'
19300                 },
19301                 {
19302                     tag: 'td',
19303                     cn: [
19304                         {
19305                             tag: 'button',
19306                             type: 'button',
19307                             cls: 'btn btn-primary period',
19308                             html: 'AM'
19309                             
19310                         }
19311                     ]
19312                 }
19313             ]
19314         });
19315         
19316         time.createChild({
19317             tag: 'tr',
19318             cn: [
19319                 {
19320                     tag: 'td',
19321                     cn: [
19322                         {
19323                             tag: 'a',
19324                             href: '#',
19325                             cls: 'btn',
19326                             cn: [
19327                                 {
19328                                     tag: 'span',
19329                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19330                                 }
19331                             ]
19332                         }
19333                     ]
19334                 },
19335                 {
19336                     tag: 'td',
19337                     cls: 'separator'
19338                 },
19339                 {
19340                     tag: 'td',
19341                     cn: [
19342                         {
19343                             tag: 'a',
19344                             href: '#',
19345                             cls: 'btn',
19346                             cn: [
19347                                 {
19348                                     tag: 'span',
19349                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19350                                 }
19351                             ]
19352                         }
19353                     ]
19354                 },
19355                 {
19356                     tag: 'td',
19357                     cls: 'separator'
19358                 }
19359             ]
19360         });
19361         
19362     },
19363     
19364     update: function()
19365     {
19366         
19367         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19368         
19369         this.fill();
19370     },
19371     
19372     fill: function() 
19373     {
19374         var hours = this.time.getHours();
19375         var minutes = this.time.getMinutes();
19376         var period = 'AM';
19377         
19378         if(hours > 11){
19379             period = 'PM';
19380         }
19381         
19382         if(hours == 0){
19383             hours = 12;
19384         }
19385         
19386         
19387         if(hours > 12){
19388             hours = hours - 12;
19389         }
19390         
19391         if(hours < 10){
19392             hours = '0' + hours;
19393         }
19394         
19395         if(minutes < 10){
19396             minutes = '0' + minutes;
19397         }
19398         
19399         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19400         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19401         this.pop.select('button', true).first().dom.innerHTML = period;
19402         
19403     },
19404     
19405     place: function()
19406     {   
19407         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19408         
19409         var cls = ['bottom'];
19410         
19411         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19412             cls.pop();
19413             cls.push('top');
19414         }
19415         
19416         cls.push('right');
19417         
19418         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19419             cls.pop();
19420             cls.push('left');
19421         }
19422         
19423         this.picker().addClass(cls.join('-'));
19424         
19425         var _this = this;
19426         
19427         Roo.each(cls, function(c){
19428             if(c == 'bottom'){
19429                 _this.picker().setTop(_this.inputEl().getHeight());
19430                 return;
19431             }
19432             if(c == 'top'){
19433                 _this.picker().setTop(0 - _this.picker().getHeight());
19434                 return;
19435             }
19436             
19437             if(c == 'left'){
19438                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19439                 return;
19440             }
19441             if(c == 'right'){
19442                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19443                 return;
19444             }
19445         });
19446         
19447     },
19448   
19449     onFocus : function()
19450     {
19451         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19452         this.show();
19453     },
19454     
19455     onBlur : function()
19456     {
19457         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19458         this.hide();
19459     },
19460     
19461     show : function()
19462     {
19463         this.picker().show();
19464         this.pop.show();
19465         this.update();
19466         this.place();
19467         
19468         this.fireEvent('show', this, this.date);
19469     },
19470     
19471     hide : function()
19472     {
19473         this.picker().hide();
19474         this.pop.hide();
19475         
19476         this.fireEvent('hide', this, this.date);
19477     },
19478     
19479     setTime : function()
19480     {
19481         this.hide();
19482         this.setValue(this.time.format(this.format));
19483         
19484         this.fireEvent('select', this, this.date);
19485         
19486         
19487     },
19488     
19489     onMousedown: function(e){
19490         e.stopPropagation();
19491         e.preventDefault();
19492     },
19493     
19494     onIncrementHours: function()
19495     {
19496         Roo.log('onIncrementHours');
19497         this.time = this.time.add(Date.HOUR, 1);
19498         this.update();
19499         
19500     },
19501     
19502     onDecrementHours: function()
19503     {
19504         Roo.log('onDecrementHours');
19505         this.time = this.time.add(Date.HOUR, -1);
19506         this.update();
19507     },
19508     
19509     onIncrementMinutes: function()
19510     {
19511         Roo.log('onIncrementMinutes');
19512         this.time = this.time.add(Date.MINUTE, 1);
19513         this.update();
19514     },
19515     
19516     onDecrementMinutes: function()
19517     {
19518         Roo.log('onDecrementMinutes');
19519         this.time = this.time.add(Date.MINUTE, -1);
19520         this.update();
19521     },
19522     
19523     onTogglePeriod: function()
19524     {
19525         Roo.log('onTogglePeriod');
19526         this.time = this.time.add(Date.HOUR, 12);
19527         this.update();
19528     }
19529     
19530    
19531 });
19532
19533 Roo.apply(Roo.bootstrap.TimeField,  {
19534     
19535     content : {
19536         tag: 'tbody',
19537         cn: [
19538             {
19539                 tag: 'tr',
19540                 cn: [
19541                 {
19542                     tag: 'td',
19543                     colspan: '7'
19544                 }
19545                 ]
19546             }
19547         ]
19548     },
19549     
19550     footer : {
19551         tag: 'tfoot',
19552         cn: [
19553             {
19554                 tag: 'tr',
19555                 cn: [
19556                 {
19557                     tag: 'th',
19558                     colspan: '7',
19559                     cls: '',
19560                     cn: [
19561                         {
19562                             tag: 'button',
19563                             cls: 'btn btn-info ok',
19564                             html: 'OK'
19565                         }
19566                     ]
19567                 }
19568
19569                 ]
19570             }
19571         ]
19572     }
19573 });
19574
19575 Roo.apply(Roo.bootstrap.TimeField,  {
19576   
19577     template : {
19578         tag: 'div',
19579         cls: 'datepicker dropdown-menu',
19580         cn: [
19581             {
19582                 tag: 'div',
19583                 cls: 'datepicker-time',
19584                 cn: [
19585                 {
19586                     tag: 'table',
19587                     cls: 'table-condensed',
19588                     cn:[
19589                     Roo.bootstrap.TimeField.content,
19590                     Roo.bootstrap.TimeField.footer
19591                     ]
19592                 }
19593                 ]
19594             }
19595         ]
19596     }
19597 });
19598
19599  
19600
19601  /*
19602  * - LGPL
19603  *
19604  * MonthField
19605  * 
19606  */
19607
19608 /**
19609  * @class Roo.bootstrap.MonthField
19610  * @extends Roo.bootstrap.Input
19611  * Bootstrap MonthField class
19612  * 
19613  * @cfg {String} language default en
19614  * 
19615  * @constructor
19616  * Create a new MonthField
19617  * @param {Object} config The config object
19618  */
19619
19620 Roo.bootstrap.MonthField = function(config){
19621     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19622     
19623     this.addEvents({
19624         /**
19625          * @event show
19626          * Fires when this field show.
19627          * @param {Roo.bootstrap.MonthField} this
19628          * @param {Mixed} date The date value
19629          */
19630         show : true,
19631         /**
19632          * @event show
19633          * Fires when this field hide.
19634          * @param {Roo.bootstrap.MonthField} this
19635          * @param {Mixed} date The date value
19636          */
19637         hide : true,
19638         /**
19639          * @event select
19640          * Fires when select a date.
19641          * @param {Roo.bootstrap.MonthField} this
19642          * @param {String} oldvalue The old value
19643          * @param {String} newvalue The new value
19644          */
19645         select : true
19646     });
19647 };
19648
19649 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19650     
19651     onRender: function(ct, position)
19652     {
19653         
19654         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19655         
19656         this.language = this.language || 'en';
19657         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19658         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19659         
19660         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19661         this.isInline = false;
19662         this.isInput = true;
19663         this.component = this.el.select('.add-on', true).first() || false;
19664         this.component = (this.component && this.component.length === 0) ? false : this.component;
19665         this.hasInput = this.component && this.inputEL().length;
19666         
19667         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19668         
19669         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19670         
19671         this.picker().on('mousedown', this.onMousedown, this);
19672         this.picker().on('click', this.onClick, this);
19673         
19674         this.picker().addClass('datepicker-dropdown');
19675         
19676         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19677             v.setStyle('width', '189px');
19678         });
19679         
19680         this.fillMonths();
19681         
19682         this.update();
19683         
19684         if(this.isInline) {
19685             this.show();
19686         }
19687         
19688     },
19689     
19690     setValue: function(v, suppressEvent)
19691     {   
19692         var o = this.getValue();
19693         
19694         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19695         
19696         this.update();
19697
19698         if(suppressEvent !== true){
19699             this.fireEvent('select', this, o, v);
19700         }
19701         
19702     },
19703     
19704     getValue: function()
19705     {
19706         return this.value;
19707     },
19708     
19709     onClick: function(e) 
19710     {
19711         e.stopPropagation();
19712         e.preventDefault();
19713         
19714         var target = e.getTarget();
19715         
19716         if(target.nodeName.toLowerCase() === 'i'){
19717             target = Roo.get(target).dom.parentNode;
19718         }
19719         
19720         var nodeName = target.nodeName;
19721         var className = target.className;
19722         var html = target.innerHTML;
19723         
19724         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19725             return;
19726         }
19727         
19728         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19729         
19730         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19731         
19732         this.hide();
19733                         
19734     },
19735     
19736     picker : function()
19737     {
19738         return this.pickerEl;
19739     },
19740     
19741     fillMonths: function()
19742     {    
19743         var i = 0;
19744         var months = this.picker().select('>.datepicker-months td', true).first();
19745         
19746         months.dom.innerHTML = '';
19747         
19748         while (i < 12) {
19749             var month = {
19750                 tag: 'span',
19751                 cls: 'month',
19752                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19753             };
19754             
19755             months.createChild(month);
19756         }
19757         
19758     },
19759     
19760     update: function()
19761     {
19762         var _this = this;
19763         
19764         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19765             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19766         }
19767         
19768         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19769             e.removeClass('active');
19770             
19771             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19772                 e.addClass('active');
19773             }
19774         })
19775     },
19776     
19777     place: function()
19778     {
19779         if(this.isInline) {
19780             return;
19781         }
19782         
19783         this.picker().removeClass(['bottom', 'top']);
19784         
19785         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19786             /*
19787              * place to the top of element!
19788              *
19789              */
19790             
19791             this.picker().addClass('top');
19792             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19793             
19794             return;
19795         }
19796         
19797         this.picker().addClass('bottom');
19798         
19799         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19800     },
19801     
19802     onFocus : function()
19803     {
19804         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19805         this.show();
19806     },
19807     
19808     onBlur : function()
19809     {
19810         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19811         
19812         var d = this.inputEl().getValue();
19813         
19814         this.setValue(d);
19815                 
19816         this.hide();
19817     },
19818     
19819     show : function()
19820     {
19821         this.picker().show();
19822         this.picker().select('>.datepicker-months', true).first().show();
19823         this.update();
19824         this.place();
19825         
19826         this.fireEvent('show', this, this.date);
19827     },
19828     
19829     hide : function()
19830     {
19831         if(this.isInline) {
19832             return;
19833         }
19834         this.picker().hide();
19835         this.fireEvent('hide', this, this.date);
19836         
19837     },
19838     
19839     onMousedown: function(e)
19840     {
19841         e.stopPropagation();
19842         e.preventDefault();
19843     },
19844     
19845     keyup: function(e)
19846     {
19847         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19848         this.update();
19849     },
19850
19851     fireKey: function(e)
19852     {
19853         if (!this.picker().isVisible()){
19854             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19855                 this.show();
19856             }
19857             return;
19858         }
19859         
19860         var dir;
19861         
19862         switch(e.keyCode){
19863             case 27: // escape
19864                 this.hide();
19865                 e.preventDefault();
19866                 break;
19867             case 37: // left
19868             case 39: // right
19869                 dir = e.keyCode == 37 ? -1 : 1;
19870                 
19871                 this.vIndex = this.vIndex + dir;
19872                 
19873                 if(this.vIndex < 0){
19874                     this.vIndex = 0;
19875                 }
19876                 
19877                 if(this.vIndex > 11){
19878                     this.vIndex = 11;
19879                 }
19880                 
19881                 if(isNaN(this.vIndex)){
19882                     this.vIndex = 0;
19883                 }
19884                 
19885                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19886                 
19887                 break;
19888             case 38: // up
19889             case 40: // down
19890                 
19891                 dir = e.keyCode == 38 ? -1 : 1;
19892                 
19893                 this.vIndex = this.vIndex + dir * 4;
19894                 
19895                 if(this.vIndex < 0){
19896                     this.vIndex = 0;
19897                 }
19898                 
19899                 if(this.vIndex > 11){
19900                     this.vIndex = 11;
19901                 }
19902                 
19903                 if(isNaN(this.vIndex)){
19904                     this.vIndex = 0;
19905                 }
19906                 
19907                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19908                 break;
19909                 
19910             case 13: // enter
19911                 
19912                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19913                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19914                 }
19915                 
19916                 this.hide();
19917                 e.preventDefault();
19918                 break;
19919             case 9: // tab
19920                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19921                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19922                 }
19923                 this.hide();
19924                 break;
19925             case 16: // shift
19926             case 17: // ctrl
19927             case 18: // alt
19928                 break;
19929             default :
19930                 this.hide();
19931                 
19932         }
19933     },
19934     
19935     remove: function() 
19936     {
19937         this.picker().remove();
19938     }
19939    
19940 });
19941
19942 Roo.apply(Roo.bootstrap.MonthField,  {
19943     
19944     content : {
19945         tag: 'tbody',
19946         cn: [
19947         {
19948             tag: 'tr',
19949             cn: [
19950             {
19951                 tag: 'td',
19952                 colspan: '7'
19953             }
19954             ]
19955         }
19956         ]
19957     },
19958     
19959     dates:{
19960         en: {
19961             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19962             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19963         }
19964     }
19965 });
19966
19967 Roo.apply(Roo.bootstrap.MonthField,  {
19968   
19969     template : {
19970         tag: 'div',
19971         cls: 'datepicker dropdown-menu roo-dynamic',
19972         cn: [
19973             {
19974                 tag: 'div',
19975                 cls: 'datepicker-months',
19976                 cn: [
19977                 {
19978                     tag: 'table',
19979                     cls: 'table-condensed',
19980                     cn:[
19981                         Roo.bootstrap.DateField.content
19982                     ]
19983                 }
19984                 ]
19985             }
19986         ]
19987     }
19988 });
19989
19990  
19991
19992  
19993  /*
19994  * - LGPL
19995  *
19996  * CheckBox
19997  * 
19998  */
19999
20000 /**
20001  * @class Roo.bootstrap.CheckBox
20002  * @extends Roo.bootstrap.Input
20003  * Bootstrap CheckBox class
20004  * 
20005  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20006  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20007  * @cfg {String} boxLabel The text that appears beside the checkbox
20008  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20009  * @cfg {Boolean} checked initnal the element
20010  * @cfg {Boolean} inline inline the element (default false)
20011  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20012  * 
20013  * @constructor
20014  * Create a new CheckBox
20015  * @param {Object} config The config object
20016  */
20017
20018 Roo.bootstrap.CheckBox = function(config){
20019     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20020    
20021     this.addEvents({
20022         /**
20023         * @event check
20024         * Fires when the element is checked or unchecked.
20025         * @param {Roo.bootstrap.CheckBox} this This input
20026         * @param {Boolean} checked The new checked value
20027         */
20028        check : true
20029     });
20030     
20031 };
20032
20033 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20034   
20035     inputType: 'checkbox',
20036     inputValue: 1,
20037     valueOff: 0,
20038     boxLabel: false,
20039     checked: false,
20040     weight : false,
20041     inline: false,
20042     
20043     getAutoCreate : function()
20044     {
20045         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20046         
20047         var id = Roo.id();
20048         
20049         var cfg = {};
20050         
20051         cfg.cls = 'form-group ' + this.inputType; //input-group
20052         
20053         if(this.inline){
20054             cfg.cls += ' ' + this.inputType + '-inline';
20055         }
20056         
20057         var input =  {
20058             tag: 'input',
20059             id : id,
20060             type : this.inputType,
20061             value : this.inputValue,
20062             cls : 'roo-' + this.inputType, //'form-box',
20063             placeholder : this.placeholder || ''
20064             
20065         };
20066         
20067         if(this.inputType != 'radio'){
20068             var hidden =  {
20069                 tag: 'input',
20070                 type : 'hidden',
20071                 cls : 'roo-hidden-value',
20072                 value : this.checked ? this.valueOff : this.inputValue
20073             };
20074         }
20075         
20076             
20077         if (this.weight) { // Validity check?
20078             cfg.cls += " " + this.inputType + "-" + this.weight;
20079         }
20080         
20081         if (this.disabled) {
20082             input.disabled=true;
20083         }
20084         
20085         if(this.checked){
20086             input.checked = this.checked;
20087             
20088         }
20089         
20090         
20091         if (this.name) {
20092             
20093             input.name = this.name;
20094             
20095             if(this.inputType != 'radio'){
20096                 hidden.name = this.name;
20097                 input.name = '_hidden_' + this.name;
20098             }
20099         }
20100         
20101         if (this.size) {
20102             input.cls += ' input-' + this.size;
20103         }
20104         
20105         var settings=this;
20106         
20107         ['xs','sm','md','lg'].map(function(size){
20108             if (settings[size]) {
20109                 cfg.cls += ' col-' + size + '-' + settings[size];
20110             }
20111         });
20112         
20113         var inputblock = input;
20114          
20115         if (this.before || this.after) {
20116             
20117             inputblock = {
20118                 cls : 'input-group',
20119                 cn :  [] 
20120             };
20121             
20122             if (this.before) {
20123                 inputblock.cn.push({
20124                     tag :'span',
20125                     cls : 'input-group-addon',
20126                     html : this.before
20127                 });
20128             }
20129             
20130             inputblock.cn.push(input);
20131             
20132             if(this.inputType != 'radio'){
20133                 inputblock.cn.push(hidden);
20134             }
20135             
20136             if (this.after) {
20137                 inputblock.cn.push({
20138                     tag :'span',
20139                     cls : 'input-group-addon',
20140                     html : this.after
20141                 });
20142             }
20143             
20144         }
20145         
20146         if (align ==='left' && this.fieldLabel.length) {
20147 //                Roo.log("left and has label");
20148             cfg.cn = [
20149                 {
20150                     tag: 'label',
20151                     'for' :  id,
20152                     cls : 'control-label',
20153                     html : this.fieldLabel
20154
20155                 },
20156                 {
20157                     cls : "", 
20158                     cn: [
20159                         inputblock
20160                     ]
20161                 }
20162             ];
20163             
20164             if(this.labelWidth > 12){
20165                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20166             }
20167             
20168             if(this.labelWidth < 13 && this.labelmd == 0){
20169                 this.labelmd = this.labelWidth;
20170             }
20171             
20172             if(this.labellg > 0){
20173                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20174                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20175             }
20176             
20177             if(this.labelmd > 0){
20178                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20179                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20180             }
20181             
20182             if(this.labelsm > 0){
20183                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20184                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20185             }
20186             
20187             if(this.labelxs > 0){
20188                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20189                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20190             }
20191             
20192         } else if ( this.fieldLabel.length) {
20193 //                Roo.log(" label");
20194                 cfg.cn = [
20195                    
20196                     {
20197                         tag: this.boxLabel ? 'span' : 'label',
20198                         'for': id,
20199                         cls: 'control-label box-input-label',
20200                         //cls : 'input-group-addon',
20201                         html : this.fieldLabel
20202                         
20203                     },
20204                     
20205                     inputblock
20206                     
20207                 ];
20208
20209         } else {
20210             
20211 //                Roo.log(" no label && no align");
20212                 cfg.cn = [  inputblock ] ;
20213                 
20214                 
20215         }
20216         
20217         if(this.boxLabel){
20218              var boxLabelCfg = {
20219                 tag: 'label',
20220                 //'for': id, // box label is handled by onclick - so no for...
20221                 cls: 'box-label',
20222                 html: this.boxLabel
20223             };
20224             
20225             if(this.tooltip){
20226                 boxLabelCfg.tooltip = this.tooltip;
20227             }
20228              
20229             cfg.cn.push(boxLabelCfg);
20230         }
20231         
20232         if(this.inputType != 'radio'){
20233             cfg.cn.push(hidden);
20234         }
20235         
20236         return cfg;
20237         
20238     },
20239     
20240     /**
20241      * return the real input element.
20242      */
20243     inputEl: function ()
20244     {
20245         return this.el.select('input.roo-' + this.inputType,true).first();
20246     },
20247     hiddenEl: function ()
20248     {
20249         return this.el.select('input.roo-hidden-value',true).first();
20250     },
20251     
20252     labelEl: function()
20253     {
20254         return this.el.select('label.control-label',true).first();
20255     },
20256     /* depricated... */
20257     
20258     label: function()
20259     {
20260         return this.labelEl();
20261     },
20262     
20263     boxLabelEl: function()
20264     {
20265         return this.el.select('label.box-label',true).first();
20266     },
20267     
20268     initEvents : function()
20269     {
20270 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20271         
20272         this.inputEl().on('click', this.onClick,  this);
20273         
20274         if (this.boxLabel) { 
20275             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20276         }
20277         
20278         this.startValue = this.getValue();
20279         
20280         if(this.groupId){
20281             Roo.bootstrap.CheckBox.register(this);
20282         }
20283     },
20284     
20285     onClick : function()
20286     {   
20287         this.setChecked(!this.checked);
20288     },
20289     
20290     setChecked : function(state,suppressEvent)
20291     {
20292         this.startValue = this.getValue();
20293
20294         if(this.inputType == 'radio'){
20295             
20296             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20297                 e.dom.checked = false;
20298             });
20299             
20300             this.inputEl().dom.checked = true;
20301             
20302             this.inputEl().dom.value = this.inputValue;
20303             
20304             if(suppressEvent !== true){
20305                 this.fireEvent('check', this, true);
20306             }
20307             
20308             this.validate();
20309             
20310             return;
20311         }
20312         
20313         this.checked = state;
20314         
20315         this.inputEl().dom.checked = state;
20316         
20317         
20318         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20319         
20320         if(suppressEvent !== true){
20321             this.fireEvent('check', this, state);
20322         }
20323         
20324         this.validate();
20325     },
20326     
20327     getValue : function()
20328     {
20329         if(this.inputType == 'radio'){
20330             return this.getGroupValue();
20331         }
20332         
20333         return this.hiddenEl().dom.value;
20334         
20335     },
20336     
20337     getGroupValue : function()
20338     {
20339         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20340             return '';
20341         }
20342         
20343         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20344     },
20345     
20346     setValue : function(v,suppressEvent)
20347     {
20348         if(this.inputType == 'radio'){
20349             this.setGroupValue(v, suppressEvent);
20350             return;
20351         }
20352         
20353         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20354         
20355         this.validate();
20356     },
20357     
20358     setGroupValue : function(v, suppressEvent)
20359     {
20360         this.startValue = this.getValue();
20361         
20362         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20363             e.dom.checked = false;
20364             
20365             if(e.dom.value == v){
20366                 e.dom.checked = true;
20367             }
20368         });
20369         
20370         if(suppressEvent !== true){
20371             this.fireEvent('check', this, true);
20372         }
20373
20374         this.validate();
20375         
20376         return;
20377     },
20378     
20379     validate : function()
20380     {
20381         if(
20382                 this.disabled || 
20383                 (this.inputType == 'radio' && this.validateRadio()) ||
20384                 (this.inputType == 'checkbox' && this.validateCheckbox())
20385         ){
20386             this.markValid();
20387             return true;
20388         }
20389         
20390         this.markInvalid();
20391         return false;
20392     },
20393     
20394     validateRadio : function()
20395     {
20396         if(this.allowBlank){
20397             return true;
20398         }
20399         
20400         var valid = false;
20401         
20402         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20403             if(!e.dom.checked){
20404                 return;
20405             }
20406             
20407             valid = true;
20408             
20409             return false;
20410         });
20411         
20412         return valid;
20413     },
20414     
20415     validateCheckbox : function()
20416     {
20417         if(!this.groupId){
20418             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20419             //return (this.getValue() == this.inputValue) ? true : false;
20420         }
20421         
20422         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20423         
20424         if(!group){
20425             return false;
20426         }
20427         
20428         var r = false;
20429         
20430         for(var i in group){
20431             if(r){
20432                 break;
20433             }
20434             
20435             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20436         }
20437         
20438         return r;
20439     },
20440     
20441     /**
20442      * Mark this field as valid
20443      */
20444     markValid : function()
20445     {
20446         var _this = this;
20447         
20448         this.fireEvent('valid', this);
20449         
20450         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20451         
20452         if(this.groupId){
20453             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20454         }
20455         
20456         if(label){
20457             label.markValid();
20458         }
20459
20460         if(this.inputType == 'radio'){
20461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20462                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20463                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20464             });
20465             
20466             return;
20467         }
20468
20469         if(!this.groupId){
20470             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20471             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20472             return;
20473         }
20474         
20475         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20476         
20477         if(!group){
20478             return;
20479         }
20480         
20481         for(var i in group){
20482             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20483             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20484         }
20485     },
20486     
20487      /**
20488      * Mark this field as invalid
20489      * @param {String} msg The validation message
20490      */
20491     markInvalid : function(msg)
20492     {
20493         if(this.allowBlank){
20494             return;
20495         }
20496         
20497         var _this = this;
20498         
20499         this.fireEvent('invalid', this, msg);
20500         
20501         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20502         
20503         if(this.groupId){
20504             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20505         }
20506         
20507         if(label){
20508             label.markInvalid();
20509         }
20510             
20511         if(this.inputType == 'radio'){
20512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20513                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20514                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20515             });
20516             
20517             return;
20518         }
20519         
20520         if(!this.groupId){
20521             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20522             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20523             return;
20524         }
20525         
20526         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20527         
20528         if(!group){
20529             return;
20530         }
20531         
20532         for(var i in group){
20533             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20534             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20535         }
20536         
20537     },
20538     
20539     clearInvalid : function()
20540     {
20541         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20542         
20543         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20544         
20545         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20546         
20547         if (label) {
20548             label.iconEl.removeClass(label.validClass);
20549             label.iconEl.removeClass(label.invalidClass);
20550         }
20551     },
20552     
20553     disable : function()
20554     {
20555         if(this.inputType != 'radio'){
20556             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20557             return;
20558         }
20559         
20560         var _this = this;
20561         
20562         if(this.rendered){
20563             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20564                 _this.getActionEl().addClass(this.disabledClass);
20565                 e.dom.disabled = true;
20566             });
20567         }
20568         
20569         this.disabled = true;
20570         this.fireEvent("disable", this);
20571         return this;
20572     },
20573
20574     enable : function()
20575     {
20576         if(this.inputType != 'radio'){
20577             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20578             return;
20579         }
20580         
20581         var _this = this;
20582         
20583         if(this.rendered){
20584             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20585                 _this.getActionEl().removeClass(this.disabledClass);
20586                 e.dom.disabled = false;
20587             });
20588         }
20589         
20590         this.disabled = false;
20591         this.fireEvent("enable", this);
20592         return this;
20593     }
20594
20595 });
20596
20597 Roo.apply(Roo.bootstrap.CheckBox, {
20598     
20599     groups: {},
20600     
20601      /**
20602     * register a CheckBox Group
20603     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20604     */
20605     register : function(checkbox)
20606     {
20607         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20608             this.groups[checkbox.groupId] = {};
20609         }
20610         
20611         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20612             return;
20613         }
20614         
20615         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20616         
20617     },
20618     /**
20619     * fetch a CheckBox Group based on the group ID
20620     * @param {string} the group ID
20621     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20622     */
20623     get: function(groupId) {
20624         if (typeof(this.groups[groupId]) == 'undefined') {
20625             return false;
20626         }
20627         
20628         return this.groups[groupId] ;
20629     }
20630     
20631     
20632 });
20633 /*
20634  * - LGPL
20635  *
20636  * RadioItem
20637  * 
20638  */
20639
20640 /**
20641  * @class Roo.bootstrap.Radio
20642  * @extends Roo.bootstrap.Component
20643  * Bootstrap Radio class
20644  * @cfg {String} boxLabel - the label associated
20645  * @cfg {String} value - the value of radio
20646  * 
20647  * @constructor
20648  * Create a new Radio
20649  * @param {Object} config The config object
20650  */
20651 Roo.bootstrap.Radio = function(config){
20652     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20653     
20654 };
20655
20656 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20657     
20658     boxLabel : '',
20659     
20660     value : '',
20661     
20662     getAutoCreate : function()
20663     {
20664         var cfg = {
20665             tag : 'div',
20666             cls : 'form-group radio',
20667             cn : [
20668                 {
20669                     tag : 'label',
20670                     cls : 'box-label',
20671                     html : this.boxLabel
20672                 }
20673             ]
20674         };
20675         
20676         return cfg;
20677     },
20678     
20679     initEvents : function() 
20680     {
20681         this.parent().register(this);
20682         
20683         this.el.on('click', this.onClick, this);
20684         
20685     },
20686     
20687     onClick : function()
20688     {
20689         this.setChecked(true);
20690     },
20691     
20692     setChecked : function(state, suppressEvent)
20693     {
20694         this.parent().setValue(this.value, suppressEvent);
20695         
20696     }
20697     
20698 });
20699  
20700
20701  /*
20702  * - LGPL
20703  *
20704  * Input
20705  * 
20706  */
20707
20708 /**
20709  * @class Roo.bootstrap.SecurePass
20710  * @extends Roo.bootstrap.Input
20711  * Bootstrap SecurePass class
20712  *
20713  * 
20714  * @constructor
20715  * Create a new SecurePass
20716  * @param {Object} config The config object
20717  */
20718  
20719 Roo.bootstrap.SecurePass = function (config) {
20720     // these go here, so the translation tool can replace them..
20721     this.errors = {
20722         PwdEmpty: "Please type a password, and then retype it to confirm.",
20723         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20724         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20725         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20726         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20727         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20728         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20729         TooWeak: "Your password is Too Weak."
20730     },
20731     this.meterLabel = "Password strength:";
20732     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20733     this.meterClass = [
20734         "roo-password-meter-tooweak", 
20735         "roo-password-meter-weak", 
20736         "roo-password-meter-medium", 
20737         "roo-password-meter-strong", 
20738         "roo-password-meter-grey"
20739     ];
20740     
20741     this.errors = {};
20742     
20743     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20744 }
20745
20746 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20747     /**
20748      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20749      * {
20750      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20751      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20752      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20753      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20754      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20755      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20756      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20757      * })
20758      */
20759     // private
20760     
20761     meterWidth: 300,
20762     errorMsg :'',    
20763     errors: false,
20764     imageRoot: '/',
20765     /**
20766      * @cfg {String/Object} Label for the strength meter (defaults to
20767      * 'Password strength:')
20768      */
20769     // private
20770     meterLabel: '',
20771     /**
20772      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20773      * ['Weak', 'Medium', 'Strong'])
20774      */
20775     // private    
20776     pwdStrengths: false,    
20777     // private
20778     strength: 0,
20779     // private
20780     _lastPwd: null,
20781     // private
20782     kCapitalLetter: 0,
20783     kSmallLetter: 1,
20784     kDigit: 2,
20785     kPunctuation: 3,
20786     
20787     insecure: false,
20788     // private
20789     initEvents: function ()
20790     {
20791         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20792
20793         if (this.el.is('input[type=password]') && Roo.isSafari) {
20794             this.el.on('keydown', this.SafariOnKeyDown, this);
20795         }
20796
20797         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20798     },
20799     // private
20800     onRender: function (ct, position)
20801     {
20802         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20803         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20804         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20805
20806         this.trigger.createChild({
20807                    cn: [
20808                     {
20809                     //id: 'PwdMeter',
20810                     tag: 'div',
20811                     cls: 'roo-password-meter-grey col-xs-12',
20812                     style: {
20813                         //width: 0,
20814                         //width: this.meterWidth + 'px'                                                
20815                         }
20816                     },
20817                     {                            
20818                          cls: 'roo-password-meter-text'                          
20819                     }
20820                 ]            
20821         });
20822
20823          
20824         if (this.hideTrigger) {
20825             this.trigger.setDisplayed(false);
20826         }
20827         this.setSize(this.width || '', this.height || '');
20828     },
20829     // private
20830     onDestroy: function ()
20831     {
20832         if (this.trigger) {
20833             this.trigger.removeAllListeners();
20834             this.trigger.remove();
20835         }
20836         if (this.wrap) {
20837             this.wrap.remove();
20838         }
20839         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20840     },
20841     // private
20842     checkStrength: function ()
20843     {
20844         var pwd = this.inputEl().getValue();
20845         if (pwd == this._lastPwd) {
20846             return;
20847         }
20848
20849         var strength;
20850         if (this.ClientSideStrongPassword(pwd)) {
20851             strength = 3;
20852         } else if (this.ClientSideMediumPassword(pwd)) {
20853             strength = 2;
20854         } else if (this.ClientSideWeakPassword(pwd)) {
20855             strength = 1;
20856         } else {
20857             strength = 0;
20858         }
20859         
20860         Roo.log('strength1: ' + strength);
20861         
20862         //var pm = this.trigger.child('div/div/div').dom;
20863         var pm = this.trigger.child('div/div');
20864         pm.removeClass(this.meterClass);
20865         pm.addClass(this.meterClass[strength]);
20866                 
20867         
20868         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20869                 
20870         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20871         
20872         this._lastPwd = pwd;
20873     },
20874     reset: function ()
20875     {
20876         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20877         
20878         this._lastPwd = '';
20879         
20880         var pm = this.trigger.child('div/div');
20881         pm.removeClass(this.meterClass);
20882         pm.addClass('roo-password-meter-grey');        
20883         
20884         
20885         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20886         
20887         pt.innerHTML = '';
20888         this.inputEl().dom.type='password';
20889     },
20890     // private
20891     validateValue: function (value)
20892     {
20893         
20894         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20895             return false;
20896         }
20897         if (value.length == 0) {
20898             if (this.allowBlank) {
20899                 this.clearInvalid();
20900                 return true;
20901             }
20902
20903             this.markInvalid(this.errors.PwdEmpty);
20904             this.errorMsg = this.errors.PwdEmpty;
20905             return false;
20906         }
20907         
20908         if(this.insecure){
20909             return true;
20910         }
20911         
20912         if ('[\x21-\x7e]*'.match(value)) {
20913             this.markInvalid(this.errors.PwdBadChar);
20914             this.errorMsg = this.errors.PwdBadChar;
20915             return false;
20916         }
20917         if (value.length < 6) {
20918             this.markInvalid(this.errors.PwdShort);
20919             this.errorMsg = this.errors.PwdShort;
20920             return false;
20921         }
20922         if (value.length > 16) {
20923             this.markInvalid(this.errors.PwdLong);
20924             this.errorMsg = this.errors.PwdLong;
20925             return false;
20926         }
20927         var strength;
20928         if (this.ClientSideStrongPassword(value)) {
20929             strength = 3;
20930         } else if (this.ClientSideMediumPassword(value)) {
20931             strength = 2;
20932         } else if (this.ClientSideWeakPassword(value)) {
20933             strength = 1;
20934         } else {
20935             strength = 0;
20936         }
20937
20938         
20939         if (strength < 2) {
20940             //this.markInvalid(this.errors.TooWeak);
20941             this.errorMsg = this.errors.TooWeak;
20942             //return false;
20943         }
20944         
20945         
20946         console.log('strength2: ' + strength);
20947         
20948         //var pm = this.trigger.child('div/div/div').dom;
20949         
20950         var pm = this.trigger.child('div/div');
20951         pm.removeClass(this.meterClass);
20952         pm.addClass(this.meterClass[strength]);
20953                 
20954         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20955                 
20956         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20957         
20958         this.errorMsg = ''; 
20959         return true;
20960     },
20961     // private
20962     CharacterSetChecks: function (type)
20963     {
20964         this.type = type;
20965         this.fResult = false;
20966     },
20967     // private
20968     isctype: function (character, type)
20969     {
20970         switch (type) {  
20971             case this.kCapitalLetter:
20972                 if (character >= 'A' && character <= 'Z') {
20973                     return true;
20974                 }
20975                 break;
20976             
20977             case this.kSmallLetter:
20978                 if (character >= 'a' && character <= 'z') {
20979                     return true;
20980                 }
20981                 break;
20982             
20983             case this.kDigit:
20984                 if (character >= '0' && character <= '9') {
20985                     return true;
20986                 }
20987                 break;
20988             
20989             case this.kPunctuation:
20990                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
20991                     return true;
20992                 }
20993                 break;
20994             
20995             default:
20996                 return false;
20997         }
20998
20999     },
21000     // private
21001     IsLongEnough: function (pwd, size)
21002     {
21003         return !(pwd == null || isNaN(size) || pwd.length < size);
21004     },
21005     // private
21006     SpansEnoughCharacterSets: function (word, nb)
21007     {
21008         if (!this.IsLongEnough(word, nb))
21009         {
21010             return false;
21011         }
21012
21013         var characterSetChecks = new Array(
21014             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21015             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21016         );
21017         
21018         for (var index = 0; index < word.length; ++index) {
21019             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21020                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21021                     characterSetChecks[nCharSet].fResult = true;
21022                     break;
21023                 }
21024             }
21025         }
21026
21027         var nCharSets = 0;
21028         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21029             if (characterSetChecks[nCharSet].fResult) {
21030                 ++nCharSets;
21031             }
21032         }
21033
21034         if (nCharSets < nb) {
21035             return false;
21036         }
21037         return true;
21038     },
21039     // private
21040     ClientSideStrongPassword: function (pwd)
21041     {
21042         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21043     },
21044     // private
21045     ClientSideMediumPassword: function (pwd)
21046     {
21047         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21048     },
21049     // private
21050     ClientSideWeakPassword: function (pwd)
21051     {
21052         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21053     }
21054           
21055 })//<script type="text/javascript">
21056
21057 /*
21058  * Based  Ext JS Library 1.1.1
21059  * Copyright(c) 2006-2007, Ext JS, LLC.
21060  * LGPL
21061  *
21062  */
21063  
21064 /**
21065  * @class Roo.HtmlEditorCore
21066  * @extends Roo.Component
21067  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21068  *
21069  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21070  */
21071
21072 Roo.HtmlEditorCore = function(config){
21073     
21074     
21075     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21076     
21077     
21078     this.addEvents({
21079         /**
21080          * @event initialize
21081          * Fires when the editor is fully initialized (including the iframe)
21082          * @param {Roo.HtmlEditorCore} this
21083          */
21084         initialize: true,
21085         /**
21086          * @event activate
21087          * Fires when the editor is first receives the focus. Any insertion must wait
21088          * until after this event.
21089          * @param {Roo.HtmlEditorCore} this
21090          */
21091         activate: true,
21092          /**
21093          * @event beforesync
21094          * Fires before the textarea is updated with content from the editor iframe. Return false
21095          * to cancel the sync.
21096          * @param {Roo.HtmlEditorCore} this
21097          * @param {String} html
21098          */
21099         beforesync: true,
21100          /**
21101          * @event beforepush
21102          * Fires before the iframe editor is updated with content from the textarea. Return false
21103          * to cancel the push.
21104          * @param {Roo.HtmlEditorCore} this
21105          * @param {String} html
21106          */
21107         beforepush: true,
21108          /**
21109          * @event sync
21110          * Fires when the textarea is updated with content from the editor iframe.
21111          * @param {Roo.HtmlEditorCore} this
21112          * @param {String} html
21113          */
21114         sync: true,
21115          /**
21116          * @event push
21117          * Fires when the iframe editor is updated with content from the textarea.
21118          * @param {Roo.HtmlEditorCore} this
21119          * @param {String} html
21120          */
21121         push: true,
21122         
21123         /**
21124          * @event editorevent
21125          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21126          * @param {Roo.HtmlEditorCore} this
21127          */
21128         editorevent: true
21129         
21130     });
21131     
21132     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21133     
21134     // defaults : white / black...
21135     this.applyBlacklists();
21136     
21137     
21138     
21139 };
21140
21141
21142 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21143
21144
21145      /**
21146      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21147      */
21148     
21149     owner : false,
21150     
21151      /**
21152      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21153      *                        Roo.resizable.
21154      */
21155     resizable : false,
21156      /**
21157      * @cfg {Number} height (in pixels)
21158      */   
21159     height: 300,
21160    /**
21161      * @cfg {Number} width (in pixels)
21162      */   
21163     width: 500,
21164     
21165     /**
21166      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21167      * 
21168      */
21169     stylesheets: false,
21170     
21171     // id of frame..
21172     frameId: false,
21173     
21174     // private properties
21175     validationEvent : false,
21176     deferHeight: true,
21177     initialized : false,
21178     activated : false,
21179     sourceEditMode : false,
21180     onFocus : Roo.emptyFn,
21181     iframePad:3,
21182     hideMode:'offsets',
21183     
21184     clearUp: true,
21185     
21186     // blacklist + whitelisted elements..
21187     black: false,
21188     white: false,
21189      
21190     
21191
21192     /**
21193      * Protected method that will not generally be called directly. It
21194      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21195      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21196      */
21197     getDocMarkup : function(){
21198         // body styles..
21199         var st = '';
21200         
21201         // inherit styels from page...?? 
21202         if (this.stylesheets === false) {
21203             
21204             Roo.get(document.head).select('style').each(function(node) {
21205                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21206             });
21207             
21208             Roo.get(document.head).select('link').each(function(node) { 
21209                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21210             });
21211             
21212         } else if (!this.stylesheets.length) {
21213                 // simple..
21214                 st = '<style type="text/css">' +
21215                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21216                    '</style>';
21217         } else { 
21218             
21219         }
21220         
21221         st +=  '<style type="text/css">' +
21222             'IMG { cursor: pointer } ' +
21223         '</style>';
21224
21225         
21226         return '<html><head>' + st  +
21227             //<style type="text/css">' +
21228             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21229             //'</style>' +
21230             ' </head><body class="roo-htmleditor-body"></body></html>';
21231     },
21232
21233     // private
21234     onRender : function(ct, position)
21235     {
21236         var _t = this;
21237         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21238         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21239         
21240         
21241         this.el.dom.style.border = '0 none';
21242         this.el.dom.setAttribute('tabIndex', -1);
21243         this.el.addClass('x-hidden hide');
21244         
21245         
21246         
21247         if(Roo.isIE){ // fix IE 1px bogus margin
21248             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21249         }
21250        
21251         
21252         this.frameId = Roo.id();
21253         
21254          
21255         
21256         var iframe = this.owner.wrap.createChild({
21257             tag: 'iframe',
21258             cls: 'form-control', // bootstrap..
21259             id: this.frameId,
21260             name: this.frameId,
21261             frameBorder : 'no',
21262             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21263         }, this.el
21264         );
21265         
21266         
21267         this.iframe = iframe.dom;
21268
21269          this.assignDocWin();
21270         
21271         this.doc.designMode = 'on';
21272        
21273         this.doc.open();
21274         this.doc.write(this.getDocMarkup());
21275         this.doc.close();
21276
21277         
21278         var task = { // must defer to wait for browser to be ready
21279             run : function(){
21280                 //console.log("run task?" + this.doc.readyState);
21281                 this.assignDocWin();
21282                 if(this.doc.body || this.doc.readyState == 'complete'){
21283                     try {
21284                         this.doc.designMode="on";
21285                     } catch (e) {
21286                         return;
21287                     }
21288                     Roo.TaskMgr.stop(task);
21289                     this.initEditor.defer(10, this);
21290                 }
21291             },
21292             interval : 10,
21293             duration: 10000,
21294             scope: this
21295         };
21296         Roo.TaskMgr.start(task);
21297
21298     },
21299
21300     // private
21301     onResize : function(w, h)
21302     {
21303          Roo.log('resize: ' +w + ',' + h );
21304         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21305         if(!this.iframe){
21306             return;
21307         }
21308         if(typeof w == 'number'){
21309             
21310             this.iframe.style.width = w + 'px';
21311         }
21312         if(typeof h == 'number'){
21313             
21314             this.iframe.style.height = h + 'px';
21315             if(this.doc){
21316                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21317             }
21318         }
21319         
21320     },
21321
21322     /**
21323      * Toggles the editor between standard and source edit mode.
21324      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21325      */
21326     toggleSourceEdit : function(sourceEditMode){
21327         
21328         this.sourceEditMode = sourceEditMode === true;
21329         
21330         if(this.sourceEditMode){
21331  
21332             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21333             
21334         }else{
21335             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21336             //this.iframe.className = '';
21337             this.deferFocus();
21338         }
21339         //this.setSize(this.owner.wrap.getSize());
21340         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21341     },
21342
21343     
21344   
21345
21346     /**
21347      * Protected method that will not generally be called directly. If you need/want
21348      * custom HTML cleanup, this is the method you should override.
21349      * @param {String} html The HTML to be cleaned
21350      * return {String} The cleaned HTML
21351      */
21352     cleanHtml : function(html){
21353         html = String(html);
21354         if(html.length > 5){
21355             if(Roo.isSafari){ // strip safari nonsense
21356                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21357             }
21358         }
21359         if(html == '&nbsp;'){
21360             html = '';
21361         }
21362         return html;
21363     },
21364
21365     /**
21366      * HTML Editor -> Textarea
21367      * Protected method that will not generally be called directly. Syncs the contents
21368      * of the editor iframe with the textarea.
21369      */
21370     syncValue : function(){
21371         if(this.initialized){
21372             var bd = (this.doc.body || this.doc.documentElement);
21373             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21374             var html = bd.innerHTML;
21375             if(Roo.isSafari){
21376                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21377                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21378                 if(m && m[1]){
21379                     html = '<div style="'+m[0]+'">' + html + '</div>';
21380                 }
21381             }
21382             html = this.cleanHtml(html);
21383             // fix up the special chars.. normaly like back quotes in word...
21384             // however we do not want to do this with chinese..
21385             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21386                 var cc = b.charCodeAt();
21387                 if (
21388                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21389                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21390                     (cc >= 0xf900 && cc < 0xfb00 )
21391                 ) {
21392                         return b;
21393                 }
21394                 return "&#"+cc+";" 
21395             });
21396             if(this.owner.fireEvent('beforesync', this, html) !== false){
21397                 this.el.dom.value = html;
21398                 this.owner.fireEvent('sync', this, html);
21399             }
21400         }
21401     },
21402
21403     /**
21404      * Protected method that will not generally be called directly. Pushes the value of the textarea
21405      * into the iframe editor.
21406      */
21407     pushValue : function(){
21408         if(this.initialized){
21409             var v = this.el.dom.value.trim();
21410             
21411 //            if(v.length < 1){
21412 //                v = '&#160;';
21413 //            }
21414             
21415             if(this.owner.fireEvent('beforepush', this, v) !== false){
21416                 var d = (this.doc.body || this.doc.documentElement);
21417                 d.innerHTML = v;
21418                 this.cleanUpPaste();
21419                 this.el.dom.value = d.innerHTML;
21420                 this.owner.fireEvent('push', this, v);
21421             }
21422         }
21423     },
21424
21425     // private
21426     deferFocus : function(){
21427         this.focus.defer(10, this);
21428     },
21429
21430     // doc'ed in Field
21431     focus : function(){
21432         if(this.win && !this.sourceEditMode){
21433             this.win.focus();
21434         }else{
21435             this.el.focus();
21436         }
21437     },
21438     
21439     assignDocWin: function()
21440     {
21441         var iframe = this.iframe;
21442         
21443          if(Roo.isIE){
21444             this.doc = iframe.contentWindow.document;
21445             this.win = iframe.contentWindow;
21446         } else {
21447 //            if (!Roo.get(this.frameId)) {
21448 //                return;
21449 //            }
21450 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21451 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21452             
21453             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21454                 return;
21455             }
21456             
21457             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21458             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21459         }
21460     },
21461     
21462     // private
21463     initEditor : function(){
21464         //console.log("INIT EDITOR");
21465         this.assignDocWin();
21466         
21467         
21468         
21469         this.doc.designMode="on";
21470         this.doc.open();
21471         this.doc.write(this.getDocMarkup());
21472         this.doc.close();
21473         
21474         var dbody = (this.doc.body || this.doc.documentElement);
21475         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21476         // this copies styles from the containing element into thsi one..
21477         // not sure why we need all of this..
21478         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21479         
21480         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21481         //ss['background-attachment'] = 'fixed'; // w3c
21482         dbody.bgProperties = 'fixed'; // ie
21483         //Roo.DomHelper.applyStyles(dbody, ss);
21484         Roo.EventManager.on(this.doc, {
21485             //'mousedown': this.onEditorEvent,
21486             'mouseup': this.onEditorEvent,
21487             'dblclick': this.onEditorEvent,
21488             'click': this.onEditorEvent,
21489             'keyup': this.onEditorEvent,
21490             buffer:100,
21491             scope: this
21492         });
21493         if(Roo.isGecko){
21494             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21495         }
21496         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21497             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21498         }
21499         this.initialized = true;
21500
21501         this.owner.fireEvent('initialize', this);
21502         this.pushValue();
21503     },
21504
21505     // private
21506     onDestroy : function(){
21507         
21508         
21509         
21510         if(this.rendered){
21511             
21512             //for (var i =0; i < this.toolbars.length;i++) {
21513             //    // fixme - ask toolbars for heights?
21514             //    this.toolbars[i].onDestroy();
21515            // }
21516             
21517             //this.wrap.dom.innerHTML = '';
21518             //this.wrap.remove();
21519         }
21520     },
21521
21522     // private
21523     onFirstFocus : function(){
21524         
21525         this.assignDocWin();
21526         
21527         
21528         this.activated = true;
21529          
21530     
21531         if(Roo.isGecko){ // prevent silly gecko errors
21532             this.win.focus();
21533             var s = this.win.getSelection();
21534             if(!s.focusNode || s.focusNode.nodeType != 3){
21535                 var r = s.getRangeAt(0);
21536                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21537                 r.collapse(true);
21538                 this.deferFocus();
21539             }
21540             try{
21541                 this.execCmd('useCSS', true);
21542                 this.execCmd('styleWithCSS', false);
21543             }catch(e){}
21544         }
21545         this.owner.fireEvent('activate', this);
21546     },
21547
21548     // private
21549     adjustFont: function(btn){
21550         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21551         //if(Roo.isSafari){ // safari
21552         //    adjust *= 2;
21553        // }
21554         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21555         if(Roo.isSafari){ // safari
21556             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21557             v =  (v < 10) ? 10 : v;
21558             v =  (v > 48) ? 48 : v;
21559             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21560             
21561         }
21562         
21563         
21564         v = Math.max(1, v+adjust);
21565         
21566         this.execCmd('FontSize', v  );
21567     },
21568
21569     onEditorEvent : function(e)
21570     {
21571         this.owner.fireEvent('editorevent', this, e);
21572       //  this.updateToolbar();
21573         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21574     },
21575
21576     insertTag : function(tg)
21577     {
21578         // could be a bit smarter... -> wrap the current selected tRoo..
21579         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21580             
21581             range = this.createRange(this.getSelection());
21582             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21583             wrappingNode.appendChild(range.extractContents());
21584             range.insertNode(wrappingNode);
21585
21586             return;
21587             
21588             
21589             
21590         }
21591         this.execCmd("formatblock",   tg);
21592         
21593     },
21594     
21595     insertText : function(txt)
21596     {
21597         
21598         
21599         var range = this.createRange();
21600         range.deleteContents();
21601                //alert(Sender.getAttribute('label'));
21602                
21603         range.insertNode(this.doc.createTextNode(txt));
21604     } ,
21605     
21606      
21607
21608     /**
21609      * Executes a Midas editor command on the editor document and performs necessary focus and
21610      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21611      * @param {String} cmd The Midas command
21612      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21613      */
21614     relayCmd : function(cmd, value){
21615         this.win.focus();
21616         this.execCmd(cmd, value);
21617         this.owner.fireEvent('editorevent', this);
21618         //this.updateToolbar();
21619         this.owner.deferFocus();
21620     },
21621
21622     /**
21623      * Executes a Midas editor command directly on the editor document.
21624      * For visual commands, you should use {@link #relayCmd} instead.
21625      * <b>This should only be called after the editor is initialized.</b>
21626      * @param {String} cmd The Midas command
21627      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21628      */
21629     execCmd : function(cmd, value){
21630         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21631         this.syncValue();
21632     },
21633  
21634  
21635    
21636     /**
21637      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21638      * to insert tRoo.
21639      * @param {String} text | dom node.. 
21640      */
21641     insertAtCursor : function(text)
21642     {
21643         
21644         if(!this.activated){
21645             return;
21646         }
21647         /*
21648         if(Roo.isIE){
21649             this.win.focus();
21650             var r = this.doc.selection.createRange();
21651             if(r){
21652                 r.collapse(true);
21653                 r.pasteHTML(text);
21654                 this.syncValue();
21655                 this.deferFocus();
21656             
21657             }
21658             return;
21659         }
21660         */
21661         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21662             this.win.focus();
21663             
21664             
21665             // from jquery ui (MIT licenced)
21666             var range, node;
21667             var win = this.win;
21668             
21669             if (win.getSelection && win.getSelection().getRangeAt) {
21670                 range = win.getSelection().getRangeAt(0);
21671                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21672                 range.insertNode(node);
21673             } else if (win.document.selection && win.document.selection.createRange) {
21674                 // no firefox support
21675                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21676                 win.document.selection.createRange().pasteHTML(txt);
21677             } else {
21678                 // no firefox support
21679                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21680                 this.execCmd('InsertHTML', txt);
21681             } 
21682             
21683             this.syncValue();
21684             
21685             this.deferFocus();
21686         }
21687     },
21688  // private
21689     mozKeyPress : function(e){
21690         if(e.ctrlKey){
21691             var c = e.getCharCode(), cmd;
21692           
21693             if(c > 0){
21694                 c = String.fromCharCode(c).toLowerCase();
21695                 switch(c){
21696                     case 'b':
21697                         cmd = 'bold';
21698                         break;
21699                     case 'i':
21700                         cmd = 'italic';
21701                         break;
21702                     
21703                     case 'u':
21704                         cmd = 'underline';
21705                         break;
21706                     
21707                     case 'v':
21708                         this.cleanUpPaste.defer(100, this);
21709                         return;
21710                         
21711                 }
21712                 if(cmd){
21713                     this.win.focus();
21714                     this.execCmd(cmd);
21715                     this.deferFocus();
21716                     e.preventDefault();
21717                 }
21718                 
21719             }
21720         }
21721     },
21722
21723     // private
21724     fixKeys : function(){ // load time branching for fastest keydown performance
21725         if(Roo.isIE){
21726             return function(e){
21727                 var k = e.getKey(), r;
21728                 if(k == e.TAB){
21729                     e.stopEvent();
21730                     r = this.doc.selection.createRange();
21731                     if(r){
21732                         r.collapse(true);
21733                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21734                         this.deferFocus();
21735                     }
21736                     return;
21737                 }
21738                 
21739                 if(k == e.ENTER){
21740                     r = this.doc.selection.createRange();
21741                     if(r){
21742                         var target = r.parentElement();
21743                         if(!target || target.tagName.toLowerCase() != 'li'){
21744                             e.stopEvent();
21745                             r.pasteHTML('<br />');
21746                             r.collapse(false);
21747                             r.select();
21748                         }
21749                     }
21750                 }
21751                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21752                     this.cleanUpPaste.defer(100, this);
21753                     return;
21754                 }
21755                 
21756                 
21757             };
21758         }else if(Roo.isOpera){
21759             return function(e){
21760                 var k = e.getKey();
21761                 if(k == e.TAB){
21762                     e.stopEvent();
21763                     this.win.focus();
21764                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21765                     this.deferFocus();
21766                 }
21767                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21768                     this.cleanUpPaste.defer(100, this);
21769                     return;
21770                 }
21771                 
21772             };
21773         }else if(Roo.isSafari){
21774             return function(e){
21775                 var k = e.getKey();
21776                 
21777                 if(k == e.TAB){
21778                     e.stopEvent();
21779                     this.execCmd('InsertText','\t');
21780                     this.deferFocus();
21781                     return;
21782                 }
21783                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21784                     this.cleanUpPaste.defer(100, this);
21785                     return;
21786                 }
21787                 
21788              };
21789         }
21790     }(),
21791     
21792     getAllAncestors: function()
21793     {
21794         var p = this.getSelectedNode();
21795         var a = [];
21796         if (!p) {
21797             a.push(p); // push blank onto stack..
21798             p = this.getParentElement();
21799         }
21800         
21801         
21802         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21803             a.push(p);
21804             p = p.parentNode;
21805         }
21806         a.push(this.doc.body);
21807         return a;
21808     },
21809     lastSel : false,
21810     lastSelNode : false,
21811     
21812     
21813     getSelection : function() 
21814     {
21815         this.assignDocWin();
21816         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21817     },
21818     
21819     getSelectedNode: function() 
21820     {
21821         // this may only work on Gecko!!!
21822         
21823         // should we cache this!!!!
21824         
21825         
21826         
21827          
21828         var range = this.createRange(this.getSelection()).cloneRange();
21829         
21830         if (Roo.isIE) {
21831             var parent = range.parentElement();
21832             while (true) {
21833                 var testRange = range.duplicate();
21834                 testRange.moveToElementText(parent);
21835                 if (testRange.inRange(range)) {
21836                     break;
21837                 }
21838                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21839                     break;
21840                 }
21841                 parent = parent.parentElement;
21842             }
21843             return parent;
21844         }
21845         
21846         // is ancestor a text element.
21847         var ac =  range.commonAncestorContainer;
21848         if (ac.nodeType == 3) {
21849             ac = ac.parentNode;
21850         }
21851         
21852         var ar = ac.childNodes;
21853          
21854         var nodes = [];
21855         var other_nodes = [];
21856         var has_other_nodes = false;
21857         for (var i=0;i<ar.length;i++) {
21858             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21859                 continue;
21860             }
21861             // fullly contained node.
21862             
21863             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21864                 nodes.push(ar[i]);
21865                 continue;
21866             }
21867             
21868             // probably selected..
21869             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21870                 other_nodes.push(ar[i]);
21871                 continue;
21872             }
21873             // outer..
21874             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21875                 continue;
21876             }
21877             
21878             
21879             has_other_nodes = true;
21880         }
21881         if (!nodes.length && other_nodes.length) {
21882             nodes= other_nodes;
21883         }
21884         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21885             return false;
21886         }
21887         
21888         return nodes[0];
21889     },
21890     createRange: function(sel)
21891     {
21892         // this has strange effects when using with 
21893         // top toolbar - not sure if it's a great idea.
21894         //this.editor.contentWindow.focus();
21895         if (typeof sel != "undefined") {
21896             try {
21897                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21898             } catch(e) {
21899                 return this.doc.createRange();
21900             }
21901         } else {
21902             return this.doc.createRange();
21903         }
21904     },
21905     getParentElement: function()
21906     {
21907         
21908         this.assignDocWin();
21909         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21910         
21911         var range = this.createRange(sel);
21912          
21913         try {
21914             var p = range.commonAncestorContainer;
21915             while (p.nodeType == 3) { // text node
21916                 p = p.parentNode;
21917             }
21918             return p;
21919         } catch (e) {
21920             return null;
21921         }
21922     
21923     },
21924     /***
21925      *
21926      * Range intersection.. the hard stuff...
21927      *  '-1' = before
21928      *  '0' = hits..
21929      *  '1' = after.
21930      *         [ -- selected range --- ]
21931      *   [fail]                        [fail]
21932      *
21933      *    basically..
21934      *      if end is before start or  hits it. fail.
21935      *      if start is after end or hits it fail.
21936      *
21937      *   if either hits (but other is outside. - then it's not 
21938      *   
21939      *    
21940      **/
21941     
21942     
21943     // @see http://www.thismuchiknow.co.uk/?p=64.
21944     rangeIntersectsNode : function(range, node)
21945     {
21946         var nodeRange = node.ownerDocument.createRange();
21947         try {
21948             nodeRange.selectNode(node);
21949         } catch (e) {
21950             nodeRange.selectNodeContents(node);
21951         }
21952     
21953         var rangeStartRange = range.cloneRange();
21954         rangeStartRange.collapse(true);
21955     
21956         var rangeEndRange = range.cloneRange();
21957         rangeEndRange.collapse(false);
21958     
21959         var nodeStartRange = nodeRange.cloneRange();
21960         nodeStartRange.collapse(true);
21961     
21962         var nodeEndRange = nodeRange.cloneRange();
21963         nodeEndRange.collapse(false);
21964     
21965         return rangeStartRange.compareBoundaryPoints(
21966                  Range.START_TO_START, nodeEndRange) == -1 &&
21967                rangeEndRange.compareBoundaryPoints(
21968                  Range.START_TO_START, nodeStartRange) == 1;
21969         
21970          
21971     },
21972     rangeCompareNode : function(range, node)
21973     {
21974         var nodeRange = node.ownerDocument.createRange();
21975         try {
21976             nodeRange.selectNode(node);
21977         } catch (e) {
21978             nodeRange.selectNodeContents(node);
21979         }
21980         
21981         
21982         range.collapse(true);
21983     
21984         nodeRange.collapse(true);
21985      
21986         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21987         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21988          
21989         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21990         
21991         var nodeIsBefore   =  ss == 1;
21992         var nodeIsAfter    = ee == -1;
21993         
21994         if (nodeIsBefore && nodeIsAfter) {
21995             return 0; // outer
21996         }
21997         if (!nodeIsBefore && nodeIsAfter) {
21998             return 1; //right trailed.
21999         }
22000         
22001         if (nodeIsBefore && !nodeIsAfter) {
22002             return 2;  // left trailed.
22003         }
22004         // fully contined.
22005         return 3;
22006     },
22007
22008     // private? - in a new class?
22009     cleanUpPaste :  function()
22010     {
22011         // cleans up the whole document..
22012         Roo.log('cleanuppaste');
22013         
22014         this.cleanUpChildren(this.doc.body);
22015         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22016         if (clean != this.doc.body.innerHTML) {
22017             this.doc.body.innerHTML = clean;
22018         }
22019         
22020     },
22021     
22022     cleanWordChars : function(input) {// change the chars to hex code
22023         var he = Roo.HtmlEditorCore;
22024         
22025         var output = input;
22026         Roo.each(he.swapCodes, function(sw) { 
22027             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22028             
22029             output = output.replace(swapper, sw[1]);
22030         });
22031         
22032         return output;
22033     },
22034     
22035     
22036     cleanUpChildren : function (n)
22037     {
22038         if (!n.childNodes.length) {
22039             return;
22040         }
22041         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22042            this.cleanUpChild(n.childNodes[i]);
22043         }
22044     },
22045     
22046     
22047         
22048     
22049     cleanUpChild : function (node)
22050     {
22051         var ed = this;
22052         //console.log(node);
22053         if (node.nodeName == "#text") {
22054             // clean up silly Windows -- stuff?
22055             return; 
22056         }
22057         if (node.nodeName == "#comment") {
22058             node.parentNode.removeChild(node);
22059             // clean up silly Windows -- stuff?
22060             return; 
22061         }
22062         var lcname = node.tagName.toLowerCase();
22063         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22064         // whitelist of tags..
22065         
22066         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22067             // remove node.
22068             node.parentNode.removeChild(node);
22069             return;
22070             
22071         }
22072         
22073         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22074         
22075         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22076         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22077         
22078         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22079         //    remove_keep_children = true;
22080         //}
22081         
22082         if (remove_keep_children) {
22083             this.cleanUpChildren(node);
22084             // inserts everything just before this node...
22085             while (node.childNodes.length) {
22086                 var cn = node.childNodes[0];
22087                 node.removeChild(cn);
22088                 node.parentNode.insertBefore(cn, node);
22089             }
22090             node.parentNode.removeChild(node);
22091             return;
22092         }
22093         
22094         if (!node.attributes || !node.attributes.length) {
22095             this.cleanUpChildren(node);
22096             return;
22097         }
22098         
22099         function cleanAttr(n,v)
22100         {
22101             
22102             if (v.match(/^\./) || v.match(/^\//)) {
22103                 return;
22104             }
22105             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22106                 return;
22107             }
22108             if (v.match(/^#/)) {
22109                 return;
22110             }
22111 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22112             node.removeAttribute(n);
22113             
22114         }
22115         
22116         var cwhite = this.cwhite;
22117         var cblack = this.cblack;
22118             
22119         function cleanStyle(n,v)
22120         {
22121             if (v.match(/expression/)) { //XSS?? should we even bother..
22122                 node.removeAttribute(n);
22123                 return;
22124             }
22125             
22126             var parts = v.split(/;/);
22127             var clean = [];
22128             
22129             Roo.each(parts, function(p) {
22130                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22131                 if (!p.length) {
22132                     return true;
22133                 }
22134                 var l = p.split(':').shift().replace(/\s+/g,'');
22135                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22136                 
22137                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22138 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22139                     //node.removeAttribute(n);
22140                     return true;
22141                 }
22142                 //Roo.log()
22143                 // only allow 'c whitelisted system attributes'
22144                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22145 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22146                     //node.removeAttribute(n);
22147                     return true;
22148                 }
22149                 
22150                 
22151                  
22152                 
22153                 clean.push(p);
22154                 return true;
22155             });
22156             if (clean.length) { 
22157                 node.setAttribute(n, clean.join(';'));
22158             } else {
22159                 node.removeAttribute(n);
22160             }
22161             
22162         }
22163         
22164         
22165         for (var i = node.attributes.length-1; i > -1 ; i--) {
22166             var a = node.attributes[i];
22167             //console.log(a);
22168             
22169             if (a.name.toLowerCase().substr(0,2)=='on')  {
22170                 node.removeAttribute(a.name);
22171                 continue;
22172             }
22173             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22174                 node.removeAttribute(a.name);
22175                 continue;
22176             }
22177             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22178                 cleanAttr(a.name,a.value); // fixme..
22179                 continue;
22180             }
22181             if (a.name == 'style') {
22182                 cleanStyle(a.name,a.value);
22183                 continue;
22184             }
22185             /// clean up MS crap..
22186             // tecnically this should be a list of valid class'es..
22187             
22188             
22189             if (a.name == 'class') {
22190                 if (a.value.match(/^Mso/)) {
22191                     node.className = '';
22192                 }
22193                 
22194                 if (a.value.match(/^body$/)) {
22195                     node.className = '';
22196                 }
22197                 continue;
22198             }
22199             
22200             // style cleanup!?
22201             // class cleanup?
22202             
22203         }
22204         
22205         
22206         this.cleanUpChildren(node);
22207         
22208         
22209     },
22210     
22211     /**
22212      * Clean up MS wordisms...
22213      */
22214     cleanWord : function(node)
22215     {
22216         
22217         
22218         if (!node) {
22219             this.cleanWord(this.doc.body);
22220             return;
22221         }
22222         if (node.nodeName == "#text") {
22223             // clean up silly Windows -- stuff?
22224             return; 
22225         }
22226         if (node.nodeName == "#comment") {
22227             node.parentNode.removeChild(node);
22228             // clean up silly Windows -- stuff?
22229             return; 
22230         }
22231         
22232         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22233             node.parentNode.removeChild(node);
22234             return;
22235         }
22236         
22237         // remove - but keep children..
22238         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22239             while (node.childNodes.length) {
22240                 var cn = node.childNodes[0];
22241                 node.removeChild(cn);
22242                 node.parentNode.insertBefore(cn, node);
22243             }
22244             node.parentNode.removeChild(node);
22245             this.iterateChildren(node, this.cleanWord);
22246             return;
22247         }
22248         // clean styles
22249         if (node.className.length) {
22250             
22251             var cn = node.className.split(/\W+/);
22252             var cna = [];
22253             Roo.each(cn, function(cls) {
22254                 if (cls.match(/Mso[a-zA-Z]+/)) {
22255                     return;
22256                 }
22257                 cna.push(cls);
22258             });
22259             node.className = cna.length ? cna.join(' ') : '';
22260             if (!cna.length) {
22261                 node.removeAttribute("class");
22262             }
22263         }
22264         
22265         if (node.hasAttribute("lang")) {
22266             node.removeAttribute("lang");
22267         }
22268         
22269         if (node.hasAttribute("style")) {
22270             
22271             var styles = node.getAttribute("style").split(";");
22272             var nstyle = [];
22273             Roo.each(styles, function(s) {
22274                 if (!s.match(/:/)) {
22275                     return;
22276                 }
22277                 var kv = s.split(":");
22278                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22279                     return;
22280                 }
22281                 // what ever is left... we allow.
22282                 nstyle.push(s);
22283             });
22284             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22285             if (!nstyle.length) {
22286                 node.removeAttribute('style');
22287             }
22288         }
22289         this.iterateChildren(node, this.cleanWord);
22290         
22291         
22292         
22293     },
22294     /**
22295      * iterateChildren of a Node, calling fn each time, using this as the scole..
22296      * @param {DomNode} node node to iterate children of.
22297      * @param {Function} fn method of this class to call on each item.
22298      */
22299     iterateChildren : function(node, fn)
22300     {
22301         if (!node.childNodes.length) {
22302                 return;
22303         }
22304         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22305            fn.call(this, node.childNodes[i])
22306         }
22307     },
22308     
22309     
22310     /**
22311      * cleanTableWidths.
22312      *
22313      * Quite often pasting from word etc.. results in tables with column and widths.
22314      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22315      *
22316      */
22317     cleanTableWidths : function(node)
22318     {
22319          
22320          
22321         if (!node) {
22322             this.cleanTableWidths(this.doc.body);
22323             return;
22324         }
22325         
22326         // ignore list...
22327         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22328             return; 
22329         }
22330         Roo.log(node.tagName);
22331         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22332             this.iterateChildren(node, this.cleanTableWidths);
22333             return;
22334         }
22335         if (node.hasAttribute('width')) {
22336             node.removeAttribute('width');
22337         }
22338         
22339          
22340         if (node.hasAttribute("style")) {
22341             // pretty basic...
22342             
22343             var styles = node.getAttribute("style").split(";");
22344             var nstyle = [];
22345             Roo.each(styles, function(s) {
22346                 if (!s.match(/:/)) {
22347                     return;
22348                 }
22349                 var kv = s.split(":");
22350                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22351                     return;
22352                 }
22353                 // what ever is left... we allow.
22354                 nstyle.push(s);
22355             });
22356             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22357             if (!nstyle.length) {
22358                 node.removeAttribute('style');
22359             }
22360         }
22361         
22362         this.iterateChildren(node, this.cleanTableWidths);
22363         
22364         
22365     },
22366     
22367     
22368     
22369     
22370     domToHTML : function(currentElement, depth, nopadtext) {
22371         
22372         depth = depth || 0;
22373         nopadtext = nopadtext || false;
22374     
22375         if (!currentElement) {
22376             return this.domToHTML(this.doc.body);
22377         }
22378         
22379         //Roo.log(currentElement);
22380         var j;
22381         var allText = false;
22382         var nodeName = currentElement.nodeName;
22383         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22384         
22385         if  (nodeName == '#text') {
22386             
22387             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22388         }
22389         
22390         
22391         var ret = '';
22392         if (nodeName != 'BODY') {
22393              
22394             var i = 0;
22395             // Prints the node tagName, such as <A>, <IMG>, etc
22396             if (tagName) {
22397                 var attr = [];
22398                 for(i = 0; i < currentElement.attributes.length;i++) {
22399                     // quoting?
22400                     var aname = currentElement.attributes.item(i).name;
22401                     if (!currentElement.attributes.item(i).value.length) {
22402                         continue;
22403                     }
22404                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22405                 }
22406                 
22407                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22408             } 
22409             else {
22410                 
22411                 // eack
22412             }
22413         } else {
22414             tagName = false;
22415         }
22416         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22417             return ret;
22418         }
22419         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22420             nopadtext = true;
22421         }
22422         
22423         
22424         // Traverse the tree
22425         i = 0;
22426         var currentElementChild = currentElement.childNodes.item(i);
22427         var allText = true;
22428         var innerHTML  = '';
22429         lastnode = '';
22430         while (currentElementChild) {
22431             // Formatting code (indent the tree so it looks nice on the screen)
22432             var nopad = nopadtext;
22433             if (lastnode == 'SPAN') {
22434                 nopad  = true;
22435             }
22436             // text
22437             if  (currentElementChild.nodeName == '#text') {
22438                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22439                 toadd = nopadtext ? toadd : toadd.trim();
22440                 if (!nopad && toadd.length > 80) {
22441                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22442                 }
22443                 innerHTML  += toadd;
22444                 
22445                 i++;
22446                 currentElementChild = currentElement.childNodes.item(i);
22447                 lastNode = '';
22448                 continue;
22449             }
22450             allText = false;
22451             
22452             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22453                 
22454             // Recursively traverse the tree structure of the child node
22455             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22456             lastnode = currentElementChild.nodeName;
22457             i++;
22458             currentElementChild=currentElement.childNodes.item(i);
22459         }
22460         
22461         ret += innerHTML;
22462         
22463         if (!allText) {
22464                 // The remaining code is mostly for formatting the tree
22465             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22466         }
22467         
22468         
22469         if (tagName) {
22470             ret+= "</"+tagName+">";
22471         }
22472         return ret;
22473         
22474     },
22475         
22476     applyBlacklists : function()
22477     {
22478         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22479         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22480         
22481         this.white = [];
22482         this.black = [];
22483         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22484             if (b.indexOf(tag) > -1) {
22485                 return;
22486             }
22487             this.white.push(tag);
22488             
22489         }, this);
22490         
22491         Roo.each(w, function(tag) {
22492             if (b.indexOf(tag) > -1) {
22493                 return;
22494             }
22495             if (this.white.indexOf(tag) > -1) {
22496                 return;
22497             }
22498             this.white.push(tag);
22499             
22500         }, this);
22501         
22502         
22503         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22504             if (w.indexOf(tag) > -1) {
22505                 return;
22506             }
22507             this.black.push(tag);
22508             
22509         }, this);
22510         
22511         Roo.each(b, function(tag) {
22512             if (w.indexOf(tag) > -1) {
22513                 return;
22514             }
22515             if (this.black.indexOf(tag) > -1) {
22516                 return;
22517             }
22518             this.black.push(tag);
22519             
22520         }, this);
22521         
22522         
22523         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22524         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22525         
22526         this.cwhite = [];
22527         this.cblack = [];
22528         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22529             if (b.indexOf(tag) > -1) {
22530                 return;
22531             }
22532             this.cwhite.push(tag);
22533             
22534         }, this);
22535         
22536         Roo.each(w, function(tag) {
22537             if (b.indexOf(tag) > -1) {
22538                 return;
22539             }
22540             if (this.cwhite.indexOf(tag) > -1) {
22541                 return;
22542             }
22543             this.cwhite.push(tag);
22544             
22545         }, this);
22546         
22547         
22548         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22549             if (w.indexOf(tag) > -1) {
22550                 return;
22551             }
22552             this.cblack.push(tag);
22553             
22554         }, this);
22555         
22556         Roo.each(b, function(tag) {
22557             if (w.indexOf(tag) > -1) {
22558                 return;
22559             }
22560             if (this.cblack.indexOf(tag) > -1) {
22561                 return;
22562             }
22563             this.cblack.push(tag);
22564             
22565         }, this);
22566     },
22567     
22568     setStylesheets : function(stylesheets)
22569     {
22570         if(typeof(stylesheets) == 'string'){
22571             Roo.get(this.iframe.contentDocument.head).createChild({
22572                 tag : 'link',
22573                 rel : 'stylesheet',
22574                 type : 'text/css',
22575                 href : stylesheets
22576             });
22577             
22578             return;
22579         }
22580         var _this = this;
22581      
22582         Roo.each(stylesheets, function(s) {
22583             if(!s.length){
22584                 return;
22585             }
22586             
22587             Roo.get(_this.iframe.contentDocument.head).createChild({
22588                 tag : 'link',
22589                 rel : 'stylesheet',
22590                 type : 'text/css',
22591                 href : s
22592             });
22593         });
22594
22595         
22596     },
22597     
22598     removeStylesheets : function()
22599     {
22600         var _this = this;
22601         
22602         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22603             s.remove();
22604         });
22605     }
22606     
22607     // hide stuff that is not compatible
22608     /**
22609      * @event blur
22610      * @hide
22611      */
22612     /**
22613      * @event change
22614      * @hide
22615      */
22616     /**
22617      * @event focus
22618      * @hide
22619      */
22620     /**
22621      * @event specialkey
22622      * @hide
22623      */
22624     /**
22625      * @cfg {String} fieldClass @hide
22626      */
22627     /**
22628      * @cfg {String} focusClass @hide
22629      */
22630     /**
22631      * @cfg {String} autoCreate @hide
22632      */
22633     /**
22634      * @cfg {String} inputType @hide
22635      */
22636     /**
22637      * @cfg {String} invalidClass @hide
22638      */
22639     /**
22640      * @cfg {String} invalidText @hide
22641      */
22642     /**
22643      * @cfg {String} msgFx @hide
22644      */
22645     /**
22646      * @cfg {String} validateOnBlur @hide
22647      */
22648 });
22649
22650 Roo.HtmlEditorCore.white = [
22651         'area', 'br', 'img', 'input', 'hr', 'wbr',
22652         
22653        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22654        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22655        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22656        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22657        'table',   'ul',         'xmp', 
22658        
22659        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22660       'thead',   'tr', 
22661      
22662       'dir', 'menu', 'ol', 'ul', 'dl',
22663        
22664       'embed',  'object'
22665 ];
22666
22667
22668 Roo.HtmlEditorCore.black = [
22669     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22670         'applet', // 
22671         'base',   'basefont', 'bgsound', 'blink',  'body', 
22672         'frame',  'frameset', 'head',    'html',   'ilayer', 
22673         'iframe', 'layer',  'link',     'meta',    'object',   
22674         'script', 'style' ,'title',  'xml' // clean later..
22675 ];
22676 Roo.HtmlEditorCore.clean = [
22677     'script', 'style', 'title', 'xml'
22678 ];
22679 Roo.HtmlEditorCore.remove = [
22680     'font'
22681 ];
22682 // attributes..
22683
22684 Roo.HtmlEditorCore.ablack = [
22685     'on'
22686 ];
22687     
22688 Roo.HtmlEditorCore.aclean = [ 
22689     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22690 ];
22691
22692 // protocols..
22693 Roo.HtmlEditorCore.pwhite= [
22694         'http',  'https',  'mailto'
22695 ];
22696
22697 // white listed style attributes.
22698 Roo.HtmlEditorCore.cwhite= [
22699       //  'text-align', /// default is to allow most things..
22700       
22701          
22702 //        'font-size'//??
22703 ];
22704
22705 // black listed style attributes.
22706 Roo.HtmlEditorCore.cblack= [
22707       //  'font-size' -- this can be set by the project 
22708 ];
22709
22710
22711 Roo.HtmlEditorCore.swapCodes   =[ 
22712     [    8211, "--" ], 
22713     [    8212, "--" ], 
22714     [    8216,  "'" ],  
22715     [    8217, "'" ],  
22716     [    8220, '"' ],  
22717     [    8221, '"' ],  
22718     [    8226, "*" ],  
22719     [    8230, "..." ]
22720 ]; 
22721
22722     /*
22723  * - LGPL
22724  *
22725  * HtmlEditor
22726  * 
22727  */
22728
22729 /**
22730  * @class Roo.bootstrap.HtmlEditor
22731  * @extends Roo.bootstrap.TextArea
22732  * Bootstrap HtmlEditor class
22733
22734  * @constructor
22735  * Create a new HtmlEditor
22736  * @param {Object} config The config object
22737  */
22738
22739 Roo.bootstrap.HtmlEditor = function(config){
22740     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22741     if (!this.toolbars) {
22742         this.toolbars = [];
22743     }
22744     
22745     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22746     this.addEvents({
22747             /**
22748              * @event initialize
22749              * Fires when the editor is fully initialized (including the iframe)
22750              * @param {HtmlEditor} this
22751              */
22752             initialize: true,
22753             /**
22754              * @event activate
22755              * Fires when the editor is first receives the focus. Any insertion must wait
22756              * until after this event.
22757              * @param {HtmlEditor} this
22758              */
22759             activate: true,
22760              /**
22761              * @event beforesync
22762              * Fires before the textarea is updated with content from the editor iframe. Return false
22763              * to cancel the sync.
22764              * @param {HtmlEditor} this
22765              * @param {String} html
22766              */
22767             beforesync: true,
22768              /**
22769              * @event beforepush
22770              * Fires before the iframe editor is updated with content from the textarea. Return false
22771              * to cancel the push.
22772              * @param {HtmlEditor} this
22773              * @param {String} html
22774              */
22775             beforepush: true,
22776              /**
22777              * @event sync
22778              * Fires when the textarea is updated with content from the editor iframe.
22779              * @param {HtmlEditor} this
22780              * @param {String} html
22781              */
22782             sync: true,
22783              /**
22784              * @event push
22785              * Fires when the iframe editor is updated with content from the textarea.
22786              * @param {HtmlEditor} this
22787              * @param {String} html
22788              */
22789             push: true,
22790              /**
22791              * @event editmodechange
22792              * Fires when the editor switches edit modes
22793              * @param {HtmlEditor} this
22794              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22795              */
22796             editmodechange: true,
22797             /**
22798              * @event editorevent
22799              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22800              * @param {HtmlEditor} this
22801              */
22802             editorevent: true,
22803             /**
22804              * @event firstfocus
22805              * Fires when on first focus - needed by toolbars..
22806              * @param {HtmlEditor} this
22807              */
22808             firstfocus: true,
22809             /**
22810              * @event autosave
22811              * Auto save the htmlEditor value as a file into Events
22812              * @param {HtmlEditor} this
22813              */
22814             autosave: true,
22815             /**
22816              * @event savedpreview
22817              * preview the saved version of htmlEditor
22818              * @param {HtmlEditor} this
22819              */
22820             savedpreview: true
22821         });
22822 };
22823
22824
22825 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22826     
22827     
22828       /**
22829      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22830      */
22831     toolbars : false,
22832     
22833      /**
22834     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22835     */
22836     btns : [],
22837    
22838      /**
22839      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22840      *                        Roo.resizable.
22841      */
22842     resizable : false,
22843      /**
22844      * @cfg {Number} height (in pixels)
22845      */   
22846     height: 300,
22847    /**
22848      * @cfg {Number} width (in pixels)
22849      */   
22850     width: false,
22851     
22852     /**
22853      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22854      * 
22855      */
22856     stylesheets: false,
22857     
22858     // id of frame..
22859     frameId: false,
22860     
22861     // private properties
22862     validationEvent : false,
22863     deferHeight: true,
22864     initialized : false,
22865     activated : false,
22866     
22867     onFocus : Roo.emptyFn,
22868     iframePad:3,
22869     hideMode:'offsets',
22870     
22871     tbContainer : false,
22872     
22873     toolbarContainer :function() {
22874         return this.wrap.select('.x-html-editor-tb',true).first();
22875     },
22876
22877     /**
22878      * Protected method that will not generally be called directly. It
22879      * is called when the editor creates its toolbar. Override this method if you need to
22880      * add custom toolbar buttons.
22881      * @param {HtmlEditor} editor
22882      */
22883     createToolbar : function(){
22884         Roo.log('renewing');
22885         Roo.log("create toolbars");
22886         
22887         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22888         this.toolbars[0].render(this.toolbarContainer());
22889         
22890         return;
22891         
22892 //        if (!editor.toolbars || !editor.toolbars.length) {
22893 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22894 //        }
22895 //        
22896 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22897 //            editor.toolbars[i] = Roo.factory(
22898 //                    typeof(editor.toolbars[i]) == 'string' ?
22899 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22900 //                Roo.bootstrap.HtmlEditor);
22901 //            editor.toolbars[i].init(editor);
22902 //        }
22903     },
22904
22905      
22906     // private
22907     onRender : function(ct, position)
22908     {
22909        // Roo.log("Call onRender: " + this.xtype);
22910         var _t = this;
22911         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22912       
22913         this.wrap = this.inputEl().wrap({
22914             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22915         });
22916         
22917         this.editorcore.onRender(ct, position);
22918          
22919         if (this.resizable) {
22920             this.resizeEl = new Roo.Resizable(this.wrap, {
22921                 pinned : true,
22922                 wrap: true,
22923                 dynamic : true,
22924                 minHeight : this.height,
22925                 height: this.height,
22926                 handles : this.resizable,
22927                 width: this.width,
22928                 listeners : {
22929                     resize : function(r, w, h) {
22930                         _t.onResize(w,h); // -something
22931                     }
22932                 }
22933             });
22934             
22935         }
22936         this.createToolbar(this);
22937        
22938         
22939         if(!this.width && this.resizable){
22940             this.setSize(this.wrap.getSize());
22941         }
22942         if (this.resizeEl) {
22943             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22944             // should trigger onReize..
22945         }
22946         
22947     },
22948
22949     // private
22950     onResize : function(w, h)
22951     {
22952         Roo.log('resize: ' +w + ',' + h );
22953         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22954         var ew = false;
22955         var eh = false;
22956         
22957         if(this.inputEl() ){
22958             if(typeof w == 'number'){
22959                 var aw = w - this.wrap.getFrameWidth('lr');
22960                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22961                 ew = aw;
22962             }
22963             if(typeof h == 'number'){
22964                  var tbh = -11;  // fixme it needs to tool bar size!
22965                 for (var i =0; i < this.toolbars.length;i++) {
22966                     // fixme - ask toolbars for heights?
22967                     tbh += this.toolbars[i].el.getHeight();
22968                     //if (this.toolbars[i].footer) {
22969                     //    tbh += this.toolbars[i].footer.el.getHeight();
22970                     //}
22971                 }
22972               
22973                 
22974                 
22975                 
22976                 
22977                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22978                 ah -= 5; // knock a few pixes off for look..
22979                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22980                 var eh = ah;
22981             }
22982         }
22983         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22984         this.editorcore.onResize(ew,eh);
22985         
22986     },
22987
22988     /**
22989      * Toggles the editor between standard and source edit mode.
22990      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22991      */
22992     toggleSourceEdit : function(sourceEditMode)
22993     {
22994         this.editorcore.toggleSourceEdit(sourceEditMode);
22995         
22996         if(this.editorcore.sourceEditMode){
22997             Roo.log('editor - showing textarea');
22998             
22999 //            Roo.log('in');
23000 //            Roo.log(this.syncValue());
23001             this.syncValue();
23002             this.inputEl().removeClass(['hide', 'x-hidden']);
23003             this.inputEl().dom.removeAttribute('tabIndex');
23004             this.inputEl().focus();
23005         }else{
23006             Roo.log('editor - hiding textarea');
23007 //            Roo.log('out')
23008 //            Roo.log(this.pushValue()); 
23009             this.pushValue();
23010             
23011             this.inputEl().addClass(['hide', 'x-hidden']);
23012             this.inputEl().dom.setAttribute('tabIndex', -1);
23013             //this.deferFocus();
23014         }
23015          
23016         if(this.resizable){
23017             this.setSize(this.wrap.getSize());
23018         }
23019         
23020         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23021     },
23022  
23023     // private (for BoxComponent)
23024     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23025
23026     // private (for BoxComponent)
23027     getResizeEl : function(){
23028         return this.wrap;
23029     },
23030
23031     // private (for BoxComponent)
23032     getPositionEl : function(){
23033         return this.wrap;
23034     },
23035
23036     // private
23037     initEvents : function(){
23038         this.originalValue = this.getValue();
23039     },
23040
23041 //    /**
23042 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23043 //     * @method
23044 //     */
23045 //    markInvalid : Roo.emptyFn,
23046 //    /**
23047 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23048 //     * @method
23049 //     */
23050 //    clearInvalid : Roo.emptyFn,
23051
23052     setValue : function(v){
23053         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23054         this.editorcore.pushValue();
23055     },
23056
23057      
23058     // private
23059     deferFocus : function(){
23060         this.focus.defer(10, this);
23061     },
23062
23063     // doc'ed in Field
23064     focus : function(){
23065         this.editorcore.focus();
23066         
23067     },
23068       
23069
23070     // private
23071     onDestroy : function(){
23072         
23073         
23074         
23075         if(this.rendered){
23076             
23077             for (var i =0; i < this.toolbars.length;i++) {
23078                 // fixme - ask toolbars for heights?
23079                 this.toolbars[i].onDestroy();
23080             }
23081             
23082             this.wrap.dom.innerHTML = '';
23083             this.wrap.remove();
23084         }
23085     },
23086
23087     // private
23088     onFirstFocus : function(){
23089         //Roo.log("onFirstFocus");
23090         this.editorcore.onFirstFocus();
23091          for (var i =0; i < this.toolbars.length;i++) {
23092             this.toolbars[i].onFirstFocus();
23093         }
23094         
23095     },
23096     
23097     // private
23098     syncValue : function()
23099     {   
23100         this.editorcore.syncValue();
23101     },
23102     
23103     pushValue : function()
23104     {   
23105         this.editorcore.pushValue();
23106     }
23107      
23108     
23109     // hide stuff that is not compatible
23110     /**
23111      * @event blur
23112      * @hide
23113      */
23114     /**
23115      * @event change
23116      * @hide
23117      */
23118     /**
23119      * @event focus
23120      * @hide
23121      */
23122     /**
23123      * @event specialkey
23124      * @hide
23125      */
23126     /**
23127      * @cfg {String} fieldClass @hide
23128      */
23129     /**
23130      * @cfg {String} focusClass @hide
23131      */
23132     /**
23133      * @cfg {String} autoCreate @hide
23134      */
23135     /**
23136      * @cfg {String} inputType @hide
23137      */
23138     /**
23139      * @cfg {String} invalidClass @hide
23140      */
23141     /**
23142      * @cfg {String} invalidText @hide
23143      */
23144     /**
23145      * @cfg {String} msgFx @hide
23146      */
23147     /**
23148      * @cfg {String} validateOnBlur @hide
23149      */
23150 });
23151  
23152     
23153    
23154    
23155    
23156       
23157 Roo.namespace('Roo.bootstrap.htmleditor');
23158 /**
23159  * @class Roo.bootstrap.HtmlEditorToolbar1
23160  * Basic Toolbar
23161  * 
23162  * Usage:
23163  *
23164  new Roo.bootstrap.HtmlEditor({
23165     ....
23166     toolbars : [
23167         new Roo.bootstrap.HtmlEditorToolbar1({
23168             disable : { fonts: 1 , format: 1, ..., ... , ...],
23169             btns : [ .... ]
23170         })
23171     }
23172      
23173  * 
23174  * @cfg {Object} disable List of elements to disable..
23175  * @cfg {Array} btns List of additional buttons.
23176  * 
23177  * 
23178  * NEEDS Extra CSS? 
23179  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23180  */
23181  
23182 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23183 {
23184     
23185     Roo.apply(this, config);
23186     
23187     // default disabled, based on 'good practice'..
23188     this.disable = this.disable || {};
23189     Roo.applyIf(this.disable, {
23190         fontSize : true,
23191         colors : true,
23192         specialElements : true
23193     });
23194     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23195     
23196     this.editor = config.editor;
23197     this.editorcore = config.editor.editorcore;
23198     
23199     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23200     
23201     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23202     // dont call parent... till later.
23203 }
23204 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23205      
23206     bar : true,
23207     
23208     editor : false,
23209     editorcore : false,
23210     
23211     
23212     formats : [
23213         "p" ,  
23214         "h1","h2","h3","h4","h5","h6", 
23215         "pre", "code", 
23216         "abbr", "acronym", "address", "cite", "samp", "var",
23217         'div','span'
23218     ],
23219     
23220     onRender : function(ct, position)
23221     {
23222        // Roo.log("Call onRender: " + this.xtype);
23223         
23224        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23225        Roo.log(this.el);
23226        this.el.dom.style.marginBottom = '0';
23227        var _this = this;
23228        var editorcore = this.editorcore;
23229        var editor= this.editor;
23230        
23231        var children = [];
23232        var btn = function(id,cmd , toggle, handler){
23233        
23234             var  event = toggle ? 'toggle' : 'click';
23235        
23236             var a = {
23237                 size : 'sm',
23238                 xtype: 'Button',
23239                 xns: Roo.bootstrap,
23240                 glyphicon : id,
23241                 cmd : id || cmd,
23242                 enableToggle:toggle !== false,
23243                 //html : 'submit'
23244                 pressed : toggle ? false : null,
23245                 listeners : {}
23246             };
23247             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23248                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23249             };
23250             children.push(a);
23251             return a;
23252        }
23253        
23254     //    var cb_box = function...
23255         
23256         var style = {
23257                 xtype: 'Button',
23258                 size : 'sm',
23259                 xns: Roo.bootstrap,
23260                 glyphicon : 'font',
23261                 //html : 'submit'
23262                 menu : {
23263                     xtype: 'Menu',
23264                     xns: Roo.bootstrap,
23265                     items:  []
23266                 }
23267         };
23268         Roo.each(this.formats, function(f) {
23269             style.menu.items.push({
23270                 xtype :'MenuItem',
23271                 xns: Roo.bootstrap,
23272                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23273                 tagname : f,
23274                 listeners : {
23275                     click : function()
23276                     {
23277                         editorcore.insertTag(this.tagname);
23278                         editor.focus();
23279                     }
23280                 }
23281                 
23282             });
23283         });
23284          children.push(style);   
23285         
23286         btn('bold',false,true);
23287         btn('italic',false,true);
23288         btn('align-left', 'justifyleft',true);
23289         btn('align-center', 'justifycenter',true);
23290         btn('align-right' , 'justifyright',true);
23291         btn('link', false, false, function(btn) {
23292             //Roo.log("create link?");
23293             var url = prompt(this.createLinkText, this.defaultLinkValue);
23294             if(url && url != 'http:/'+'/'){
23295                 this.editorcore.relayCmd('createlink', url);
23296             }
23297         }),
23298         btn('list','insertunorderedlist',true);
23299         btn('pencil', false,true, function(btn){
23300                 Roo.log(this);
23301                 this.toggleSourceEdit(btn.pressed);
23302         });
23303         
23304         if (this.editor.btns.length > 0) {
23305             for (var i = 0; i<this.editor.btns.length; i++) {
23306                 btn(this.editor.btns[i].glyphicon,false,true,this.editor.btns[i].listeners.click);
23307             }
23308         }
23309         
23310         /*
23311         var cog = {
23312                 xtype: 'Button',
23313                 size : 'sm',
23314                 xns: Roo.bootstrap,
23315                 glyphicon : 'cog',
23316                 //html : 'submit'
23317                 menu : {
23318                     xtype: 'Menu',
23319                     xns: Roo.bootstrap,
23320                     items:  []
23321                 }
23322         };
23323         
23324         cog.menu.items.push({
23325             xtype :'MenuItem',
23326             xns: Roo.bootstrap,
23327             html : Clean styles,
23328             tagname : f,
23329             listeners : {
23330                 click : function()
23331                 {
23332                     editorcore.insertTag(this.tagname);
23333                     editor.focus();
23334                 }
23335             }
23336             
23337         });
23338        */
23339         
23340          
23341        this.xtype = 'NavSimplebar';
23342         
23343         for(var i=0;i< children.length;i++) {
23344             
23345             this.buttons.add(this.addxtypeChild(children[i]));
23346             
23347         }
23348         
23349         editor.on('editorevent', this.updateToolbar, this);
23350     },
23351     onBtnClick : function(id)
23352     {
23353        this.editorcore.relayCmd(id);
23354        this.editorcore.focus();
23355     },
23356     
23357     /**
23358      * Protected method that will not generally be called directly. It triggers
23359      * a toolbar update by reading the markup state of the current selection in the editor.
23360      */
23361     updateToolbar: function(){
23362
23363         if(!this.editorcore.activated){
23364             this.editor.onFirstFocus(); // is this neeed?
23365             return;
23366         }
23367
23368         var btns = this.buttons; 
23369         var doc = this.editorcore.doc;
23370         btns.get('bold').setActive(doc.queryCommandState('bold'));
23371         btns.get('italic').setActive(doc.queryCommandState('italic'));
23372         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23373         
23374         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23375         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23376         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23377         
23378         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23379         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23380          /*
23381         
23382         var ans = this.editorcore.getAllAncestors();
23383         if (this.formatCombo) {
23384             
23385             
23386             var store = this.formatCombo.store;
23387             this.formatCombo.setValue("");
23388             for (var i =0; i < ans.length;i++) {
23389                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23390                     // select it..
23391                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23392                     break;
23393                 }
23394             }
23395         }
23396         
23397         
23398         
23399         // hides menus... - so this cant be on a menu...
23400         Roo.bootstrap.MenuMgr.hideAll();
23401         */
23402         Roo.bootstrap.MenuMgr.hideAll();
23403         //this.editorsyncValue();
23404     },
23405     onFirstFocus: function() {
23406         this.buttons.each(function(item){
23407            item.enable();
23408         });
23409     },
23410     toggleSourceEdit : function(sourceEditMode){
23411         
23412           
23413         if(sourceEditMode){
23414             Roo.log("disabling buttons");
23415            this.buttons.each( function(item){
23416                 if(item.cmd != 'pencil'){
23417                     item.disable();
23418                 }
23419             });
23420           
23421         }else{
23422             Roo.log("enabling buttons");
23423             if(this.editorcore.initialized){
23424                 this.buttons.each( function(item){
23425                     item.enable();
23426                 });
23427             }
23428             
23429         }
23430         Roo.log("calling toggole on editor");
23431         // tell the editor that it's been pressed..
23432         this.editor.toggleSourceEdit(sourceEditMode);
23433        
23434     }
23435 });
23436
23437
23438
23439
23440
23441 /**
23442  * @class Roo.bootstrap.Table.AbstractSelectionModel
23443  * @extends Roo.util.Observable
23444  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23445  * implemented by descendant classes.  This class should not be directly instantiated.
23446  * @constructor
23447  */
23448 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23449     this.locked = false;
23450     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23451 };
23452
23453
23454 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23455     /** @ignore Called by the grid automatically. Do not call directly. */
23456     init : function(grid){
23457         this.grid = grid;
23458         this.initEvents();
23459     },
23460
23461     /**
23462      * Locks the selections.
23463      */
23464     lock : function(){
23465         this.locked = true;
23466     },
23467
23468     /**
23469      * Unlocks the selections.
23470      */
23471     unlock : function(){
23472         this.locked = false;
23473     },
23474
23475     /**
23476      * Returns true if the selections are locked.
23477      * @return {Boolean}
23478      */
23479     isLocked : function(){
23480         return this.locked;
23481     }
23482 });
23483 /**
23484  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23485  * @class Roo.bootstrap.Table.RowSelectionModel
23486  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23487  * It supports multiple selections and keyboard selection/navigation. 
23488  * @constructor
23489  * @param {Object} config
23490  */
23491
23492 Roo.bootstrap.Table.RowSelectionModel = function(config){
23493     Roo.apply(this, config);
23494     this.selections = new Roo.util.MixedCollection(false, function(o){
23495         return o.id;
23496     });
23497
23498     this.last = false;
23499     this.lastActive = false;
23500
23501     this.addEvents({
23502         /**
23503              * @event selectionchange
23504              * Fires when the selection changes
23505              * @param {SelectionModel} this
23506              */
23507             "selectionchange" : true,
23508         /**
23509              * @event afterselectionchange
23510              * Fires after the selection changes (eg. by key press or clicking)
23511              * @param {SelectionModel} this
23512              */
23513             "afterselectionchange" : true,
23514         /**
23515              * @event beforerowselect
23516              * Fires when a row is selected being selected, return false to cancel.
23517              * @param {SelectionModel} this
23518              * @param {Number} rowIndex The selected index
23519              * @param {Boolean} keepExisting False if other selections will be cleared
23520              */
23521             "beforerowselect" : true,
23522         /**
23523              * @event rowselect
23524              * Fires when a row is selected.
23525              * @param {SelectionModel} this
23526              * @param {Number} rowIndex The selected index
23527              * @param {Roo.data.Record} r The record
23528              */
23529             "rowselect" : true,
23530         /**
23531              * @event rowdeselect
23532              * Fires when a row is deselected.
23533              * @param {SelectionModel} this
23534              * @param {Number} rowIndex The selected index
23535              */
23536         "rowdeselect" : true
23537     });
23538     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23539     this.locked = false;
23540  };
23541
23542 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23543     /**
23544      * @cfg {Boolean} singleSelect
23545      * True to allow selection of only one row at a time (defaults to false)
23546      */
23547     singleSelect : false,
23548
23549     // private
23550     initEvents : function()
23551     {
23552
23553         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23554         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23555         //}else{ // allow click to work like normal
23556          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23557         //}
23558         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23559         this.grid.on("rowclick", this.handleMouseDown, this);
23560         
23561         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23562             "up" : function(e){
23563                 if(!e.shiftKey){
23564                     this.selectPrevious(e.shiftKey);
23565                 }else if(this.last !== false && this.lastActive !== false){
23566                     var last = this.last;
23567                     this.selectRange(this.last,  this.lastActive-1);
23568                     this.grid.getView().focusRow(this.lastActive);
23569                     if(last !== false){
23570                         this.last = last;
23571                     }
23572                 }else{
23573                     this.selectFirstRow();
23574                 }
23575                 this.fireEvent("afterselectionchange", this);
23576             },
23577             "down" : function(e){
23578                 if(!e.shiftKey){
23579                     this.selectNext(e.shiftKey);
23580                 }else if(this.last !== false && this.lastActive !== false){
23581                     var last = this.last;
23582                     this.selectRange(this.last,  this.lastActive+1);
23583                     this.grid.getView().focusRow(this.lastActive);
23584                     if(last !== false){
23585                         this.last = last;
23586                     }
23587                 }else{
23588                     this.selectFirstRow();
23589                 }
23590                 this.fireEvent("afterselectionchange", this);
23591             },
23592             scope: this
23593         });
23594         this.grid.store.on('load', function(){
23595             this.selections.clear();
23596         },this);
23597         /*
23598         var view = this.grid.view;
23599         view.on("refresh", this.onRefresh, this);
23600         view.on("rowupdated", this.onRowUpdated, this);
23601         view.on("rowremoved", this.onRemove, this);
23602         */
23603     },
23604
23605     // private
23606     onRefresh : function()
23607     {
23608         var ds = this.grid.store, i, v = this.grid.view;
23609         var s = this.selections;
23610         s.each(function(r){
23611             if((i = ds.indexOfId(r.id)) != -1){
23612                 v.onRowSelect(i);
23613             }else{
23614                 s.remove(r);
23615             }
23616         });
23617     },
23618
23619     // private
23620     onRemove : function(v, index, r){
23621         this.selections.remove(r);
23622     },
23623
23624     // private
23625     onRowUpdated : function(v, index, r){
23626         if(this.isSelected(r)){
23627             v.onRowSelect(index);
23628         }
23629     },
23630
23631     /**
23632      * Select records.
23633      * @param {Array} records The records to select
23634      * @param {Boolean} keepExisting (optional) True to keep existing selections
23635      */
23636     selectRecords : function(records, keepExisting)
23637     {
23638         if(!keepExisting){
23639             this.clearSelections();
23640         }
23641             var ds = this.grid.store;
23642         for(var i = 0, len = records.length; i < len; i++){
23643             this.selectRow(ds.indexOf(records[i]), true);
23644         }
23645     },
23646
23647     /**
23648      * Gets the number of selected rows.
23649      * @return {Number}
23650      */
23651     getCount : function(){
23652         return this.selections.length;
23653     },
23654
23655     /**
23656      * Selects the first row in the grid.
23657      */
23658     selectFirstRow : function(){
23659         this.selectRow(0);
23660     },
23661
23662     /**
23663      * Select the last row.
23664      * @param {Boolean} keepExisting (optional) True to keep existing selections
23665      */
23666     selectLastRow : function(keepExisting){
23667         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23668         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23669     },
23670
23671     /**
23672      * Selects the row immediately following the last selected row.
23673      * @param {Boolean} keepExisting (optional) True to keep existing selections
23674      */
23675     selectNext : function(keepExisting)
23676     {
23677             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23678             this.selectRow(this.last+1, keepExisting);
23679             this.grid.getView().focusRow(this.last);
23680         }
23681     },
23682
23683     /**
23684      * Selects the row that precedes the last selected row.
23685      * @param {Boolean} keepExisting (optional) True to keep existing selections
23686      */
23687     selectPrevious : function(keepExisting){
23688         if(this.last){
23689             this.selectRow(this.last-1, keepExisting);
23690             this.grid.getView().focusRow(this.last);
23691         }
23692     },
23693
23694     /**
23695      * Returns the selected records
23696      * @return {Array} Array of selected records
23697      */
23698     getSelections : function(){
23699         return [].concat(this.selections.items);
23700     },
23701
23702     /**
23703      * Returns the first selected record.
23704      * @return {Record}
23705      */
23706     getSelected : function(){
23707         return this.selections.itemAt(0);
23708     },
23709
23710
23711     /**
23712      * Clears all selections.
23713      */
23714     clearSelections : function(fast)
23715     {
23716         if(this.locked) {
23717             return;
23718         }
23719         if(fast !== true){
23720                 var ds = this.grid.store;
23721             var s = this.selections;
23722             s.each(function(r){
23723                 this.deselectRow(ds.indexOfId(r.id));
23724             }, this);
23725             s.clear();
23726         }else{
23727             this.selections.clear();
23728         }
23729         this.last = false;
23730     },
23731
23732
23733     /**
23734      * Selects all rows.
23735      */
23736     selectAll : function(){
23737         if(this.locked) {
23738             return;
23739         }
23740         this.selections.clear();
23741         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23742             this.selectRow(i, true);
23743         }
23744     },
23745
23746     /**
23747      * Returns True if there is a selection.
23748      * @return {Boolean}
23749      */
23750     hasSelection : function(){
23751         return this.selections.length > 0;
23752     },
23753
23754     /**
23755      * Returns True if the specified row is selected.
23756      * @param {Number/Record} record The record or index of the record to check
23757      * @return {Boolean}
23758      */
23759     isSelected : function(index){
23760             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23761         return (r && this.selections.key(r.id) ? true : false);
23762     },
23763
23764     /**
23765      * Returns True if the specified record id is selected.
23766      * @param {String} id The id of record to check
23767      * @return {Boolean}
23768      */
23769     isIdSelected : function(id){
23770         return (this.selections.key(id) ? true : false);
23771     },
23772
23773
23774     // private
23775     handleMouseDBClick : function(e, t){
23776         
23777     },
23778     // private
23779     handleMouseDown : function(e, t)
23780     {
23781             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23782         if(this.isLocked() || rowIndex < 0 ){
23783             return;
23784         };
23785         if(e.shiftKey && this.last !== false){
23786             var last = this.last;
23787             this.selectRange(last, rowIndex, e.ctrlKey);
23788             this.last = last; // reset the last
23789             t.focus();
23790     
23791         }else{
23792             var isSelected = this.isSelected(rowIndex);
23793             //Roo.log("select row:" + rowIndex);
23794             if(isSelected){
23795                 this.deselectRow(rowIndex);
23796             } else {
23797                         this.selectRow(rowIndex, true);
23798             }
23799     
23800             /*
23801                 if(e.button !== 0 && isSelected){
23802                 alert('rowIndex 2: ' + rowIndex);
23803                     view.focusRow(rowIndex);
23804                 }else if(e.ctrlKey && isSelected){
23805                     this.deselectRow(rowIndex);
23806                 }else if(!isSelected){
23807                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23808                     view.focusRow(rowIndex);
23809                 }
23810             */
23811         }
23812         this.fireEvent("afterselectionchange", this);
23813     },
23814     // private
23815     handleDragableRowClick :  function(grid, rowIndex, e) 
23816     {
23817         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23818             this.selectRow(rowIndex, false);
23819             grid.view.focusRow(rowIndex);
23820              this.fireEvent("afterselectionchange", this);
23821         }
23822     },
23823     
23824     /**
23825      * Selects multiple rows.
23826      * @param {Array} rows Array of the indexes of the row to select
23827      * @param {Boolean} keepExisting (optional) True to keep existing selections
23828      */
23829     selectRows : function(rows, keepExisting){
23830         if(!keepExisting){
23831             this.clearSelections();
23832         }
23833         for(var i = 0, len = rows.length; i < len; i++){
23834             this.selectRow(rows[i], true);
23835         }
23836     },
23837
23838     /**
23839      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23840      * @param {Number} startRow The index of the first row in the range
23841      * @param {Number} endRow The index of the last row in the range
23842      * @param {Boolean} keepExisting (optional) True to retain existing selections
23843      */
23844     selectRange : function(startRow, endRow, keepExisting){
23845         if(this.locked) {
23846             return;
23847         }
23848         if(!keepExisting){
23849             this.clearSelections();
23850         }
23851         if(startRow <= endRow){
23852             for(var i = startRow; i <= endRow; i++){
23853                 this.selectRow(i, true);
23854             }
23855         }else{
23856             for(var i = startRow; i >= endRow; i--){
23857                 this.selectRow(i, true);
23858             }
23859         }
23860     },
23861
23862     /**
23863      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23864      * @param {Number} startRow The index of the first row in the range
23865      * @param {Number} endRow The index of the last row in the range
23866      */
23867     deselectRange : function(startRow, endRow, preventViewNotify){
23868         if(this.locked) {
23869             return;
23870         }
23871         for(var i = startRow; i <= endRow; i++){
23872             this.deselectRow(i, preventViewNotify);
23873         }
23874     },
23875
23876     /**
23877      * Selects a row.
23878      * @param {Number} row The index of the row to select
23879      * @param {Boolean} keepExisting (optional) True to keep existing selections
23880      */
23881     selectRow : function(index, keepExisting, preventViewNotify)
23882     {
23883             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23884             return;
23885         }
23886         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23887             if(!keepExisting || this.singleSelect){
23888                 this.clearSelections();
23889             }
23890             
23891             var r = this.grid.store.getAt(index);
23892             //console.log('selectRow - record id :' + r.id);
23893             
23894             this.selections.add(r);
23895             this.last = this.lastActive = index;
23896             if(!preventViewNotify){
23897                 var proxy = new Roo.Element(
23898                                 this.grid.getRowDom(index)
23899                 );
23900                 proxy.addClass('bg-info info');
23901             }
23902             this.fireEvent("rowselect", this, index, r);
23903             this.fireEvent("selectionchange", this);
23904         }
23905     },
23906
23907     /**
23908      * Deselects a row.
23909      * @param {Number} row The index of the row to deselect
23910      */
23911     deselectRow : function(index, preventViewNotify)
23912     {
23913         if(this.locked) {
23914             return;
23915         }
23916         if(this.last == index){
23917             this.last = false;
23918         }
23919         if(this.lastActive == index){
23920             this.lastActive = false;
23921         }
23922         
23923         var r = this.grid.store.getAt(index);
23924         if (!r) {
23925             return;
23926         }
23927         
23928         this.selections.remove(r);
23929         //.console.log('deselectRow - record id :' + r.id);
23930         if(!preventViewNotify){
23931         
23932             var proxy = new Roo.Element(
23933                 this.grid.getRowDom(index)
23934             );
23935             proxy.removeClass('bg-info info');
23936         }
23937         this.fireEvent("rowdeselect", this, index);
23938         this.fireEvent("selectionchange", this);
23939     },
23940
23941     // private
23942     restoreLast : function(){
23943         if(this._last){
23944             this.last = this._last;
23945         }
23946     },
23947
23948     // private
23949     acceptsNav : function(row, col, cm){
23950         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23951     },
23952
23953     // private
23954     onEditorKey : function(field, e){
23955         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23956         if(k == e.TAB){
23957             e.stopEvent();
23958             ed.completeEdit();
23959             if(e.shiftKey){
23960                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23961             }else{
23962                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23963             }
23964         }else if(k == e.ENTER && !e.ctrlKey){
23965             e.stopEvent();
23966             ed.completeEdit();
23967             if(e.shiftKey){
23968                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23969             }else{
23970                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23971             }
23972         }else if(k == e.ESC){
23973             ed.cancelEdit();
23974         }
23975         if(newCell){
23976             g.startEditing(newCell[0], newCell[1]);
23977         }
23978     }
23979 });
23980 /*
23981  * Based on:
23982  * Ext JS Library 1.1.1
23983  * Copyright(c) 2006-2007, Ext JS, LLC.
23984  *
23985  * Originally Released Under LGPL - original licence link has changed is not relivant.
23986  *
23987  * Fork - LGPL
23988  * <script type="text/javascript">
23989  */
23990  
23991 /**
23992  * @class Roo.bootstrap.PagingToolbar
23993  * @extends Roo.bootstrap.NavSimplebar
23994  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23995  * @constructor
23996  * Create a new PagingToolbar
23997  * @param {Object} config The config object
23998  * @param {Roo.data.Store} store
23999  */
24000 Roo.bootstrap.PagingToolbar = function(config)
24001 {
24002     // old args format still supported... - xtype is prefered..
24003         // created from xtype...
24004     
24005     this.ds = config.dataSource;
24006     
24007     if (config.store && !this.ds) {
24008         this.store= Roo.factory(config.store, Roo.data);
24009         this.ds = this.store;
24010         this.ds.xmodule = this.xmodule || false;
24011     }
24012     
24013     this.toolbarItems = [];
24014     if (config.items) {
24015         this.toolbarItems = config.items;
24016     }
24017     
24018     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24019     
24020     this.cursor = 0;
24021     
24022     if (this.ds) { 
24023         this.bind(this.ds);
24024     }
24025     
24026     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24027     
24028 };
24029
24030 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24031     /**
24032      * @cfg {Roo.data.Store} dataSource
24033      * The underlying data store providing the paged data
24034      */
24035     /**
24036      * @cfg {String/HTMLElement/Element} container
24037      * container The id or element that will contain the toolbar
24038      */
24039     /**
24040      * @cfg {Boolean} displayInfo
24041      * True to display the displayMsg (defaults to false)
24042      */
24043     /**
24044      * @cfg {Number} pageSize
24045      * The number of records to display per page (defaults to 20)
24046      */
24047     pageSize: 20,
24048     /**
24049      * @cfg {String} displayMsg
24050      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24051      */
24052     displayMsg : 'Displaying {0} - {1} of {2}',
24053     /**
24054      * @cfg {String} emptyMsg
24055      * The message to display when no records are found (defaults to "No data to display")
24056      */
24057     emptyMsg : 'No data to display',
24058     /**
24059      * Customizable piece of the default paging text (defaults to "Page")
24060      * @type String
24061      */
24062     beforePageText : "Page",
24063     /**
24064      * Customizable piece of the default paging text (defaults to "of %0")
24065      * @type String
24066      */
24067     afterPageText : "of {0}",
24068     /**
24069      * Customizable piece of the default paging text (defaults to "First Page")
24070      * @type String
24071      */
24072     firstText : "First Page",
24073     /**
24074      * Customizable piece of the default paging text (defaults to "Previous Page")
24075      * @type String
24076      */
24077     prevText : "Previous Page",
24078     /**
24079      * Customizable piece of the default paging text (defaults to "Next Page")
24080      * @type String
24081      */
24082     nextText : "Next Page",
24083     /**
24084      * Customizable piece of the default paging text (defaults to "Last Page")
24085      * @type String
24086      */
24087     lastText : "Last Page",
24088     /**
24089      * Customizable piece of the default paging text (defaults to "Refresh")
24090      * @type String
24091      */
24092     refreshText : "Refresh",
24093
24094     buttons : false,
24095     // private
24096     onRender : function(ct, position) 
24097     {
24098         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24099         this.navgroup.parentId = this.id;
24100         this.navgroup.onRender(this.el, null);
24101         // add the buttons to the navgroup
24102         
24103         if(this.displayInfo){
24104             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24105             this.displayEl = this.el.select('.x-paging-info', true).first();
24106 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24107 //            this.displayEl = navel.el.select('span',true).first();
24108         }
24109         
24110         var _this = this;
24111         
24112         if(this.buttons){
24113             Roo.each(_this.buttons, function(e){ // this might need to use render????
24114                Roo.factory(e).onRender(_this.el, null);
24115             });
24116         }
24117             
24118         Roo.each(_this.toolbarItems, function(e) {
24119             _this.navgroup.addItem(e);
24120         });
24121         
24122         
24123         this.first = this.navgroup.addItem({
24124             tooltip: this.firstText,
24125             cls: "prev",
24126             icon : 'fa fa-backward',
24127             disabled: true,
24128             preventDefault: true,
24129             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24130         });
24131         
24132         this.prev =  this.navgroup.addItem({
24133             tooltip: this.prevText,
24134             cls: "prev",
24135             icon : 'fa fa-step-backward',
24136             disabled: true,
24137             preventDefault: true,
24138             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24139         });
24140     //this.addSeparator();
24141         
24142         
24143         var field = this.navgroup.addItem( {
24144             tagtype : 'span',
24145             cls : 'x-paging-position',
24146             
24147             html : this.beforePageText  +
24148                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24149                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24150          } ); //?? escaped?
24151         
24152         this.field = field.el.select('input', true).first();
24153         this.field.on("keydown", this.onPagingKeydown, this);
24154         this.field.on("focus", function(){this.dom.select();});
24155     
24156     
24157         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24158         //this.field.setHeight(18);
24159         //this.addSeparator();
24160         this.next = this.navgroup.addItem({
24161             tooltip: this.nextText,
24162             cls: "next",
24163             html : ' <i class="fa fa-step-forward">',
24164             disabled: true,
24165             preventDefault: true,
24166             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24167         });
24168         this.last = this.navgroup.addItem({
24169             tooltip: this.lastText,
24170             icon : 'fa fa-forward',
24171             cls: "next",
24172             disabled: true,
24173             preventDefault: true,
24174             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24175         });
24176     //this.addSeparator();
24177         this.loading = this.navgroup.addItem({
24178             tooltip: this.refreshText,
24179             icon: 'fa fa-refresh',
24180             preventDefault: true,
24181             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24182         });
24183         
24184     },
24185
24186     // private
24187     updateInfo : function(){
24188         if(this.displayEl){
24189             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24190             var msg = count == 0 ?
24191                 this.emptyMsg :
24192                 String.format(
24193                     this.displayMsg,
24194                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24195                 );
24196             this.displayEl.update(msg);
24197         }
24198     },
24199
24200     // private
24201     onLoad : function(ds, r, o){
24202        this.cursor = o.params ? o.params.start : 0;
24203        var d = this.getPageData(),
24204             ap = d.activePage,
24205             ps = d.pages;
24206         
24207        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24208        this.field.dom.value = ap;
24209        this.first.setDisabled(ap == 1);
24210        this.prev.setDisabled(ap == 1);
24211        this.next.setDisabled(ap == ps);
24212        this.last.setDisabled(ap == ps);
24213        this.loading.enable();
24214        this.updateInfo();
24215     },
24216
24217     // private
24218     getPageData : function(){
24219         var total = this.ds.getTotalCount();
24220         return {
24221             total : total,
24222             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24223             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24224         };
24225     },
24226
24227     // private
24228     onLoadError : function(){
24229         this.loading.enable();
24230     },
24231
24232     // private
24233     onPagingKeydown : function(e){
24234         var k = e.getKey();
24235         var d = this.getPageData();
24236         if(k == e.RETURN){
24237             var v = this.field.dom.value, pageNum;
24238             if(!v || isNaN(pageNum = parseInt(v, 10))){
24239                 this.field.dom.value = d.activePage;
24240                 return;
24241             }
24242             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24243             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24244             e.stopEvent();
24245         }
24246         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))
24247         {
24248           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24249           this.field.dom.value = pageNum;
24250           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24251           e.stopEvent();
24252         }
24253         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24254         {
24255           var v = this.field.dom.value, pageNum; 
24256           var increment = (e.shiftKey) ? 10 : 1;
24257           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24258                 increment *= -1;
24259           }
24260           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24261             this.field.dom.value = d.activePage;
24262             return;
24263           }
24264           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24265           {
24266             this.field.dom.value = parseInt(v, 10) + increment;
24267             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24268             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24269           }
24270           e.stopEvent();
24271         }
24272     },
24273
24274     // private
24275     beforeLoad : function(){
24276         if(this.loading){
24277             this.loading.disable();
24278         }
24279     },
24280
24281     // private
24282     onClick : function(which){
24283         
24284         var ds = this.ds;
24285         if (!ds) {
24286             return;
24287         }
24288         
24289         switch(which){
24290             case "first":
24291                 ds.load({params:{start: 0, limit: this.pageSize}});
24292             break;
24293             case "prev":
24294                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24295             break;
24296             case "next":
24297                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24298             break;
24299             case "last":
24300                 var total = ds.getTotalCount();
24301                 var extra = total % this.pageSize;
24302                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24303                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24304             break;
24305             case "refresh":
24306                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24307             break;
24308         }
24309     },
24310
24311     /**
24312      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24313      * @param {Roo.data.Store} store The data store to unbind
24314      */
24315     unbind : function(ds){
24316         ds.un("beforeload", this.beforeLoad, this);
24317         ds.un("load", this.onLoad, this);
24318         ds.un("loadexception", this.onLoadError, this);
24319         ds.un("remove", this.updateInfo, this);
24320         ds.un("add", this.updateInfo, this);
24321         this.ds = undefined;
24322     },
24323
24324     /**
24325      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24326      * @param {Roo.data.Store} store The data store to bind
24327      */
24328     bind : function(ds){
24329         ds.on("beforeload", this.beforeLoad, this);
24330         ds.on("load", this.onLoad, this);
24331         ds.on("loadexception", this.onLoadError, this);
24332         ds.on("remove", this.updateInfo, this);
24333         ds.on("add", this.updateInfo, this);
24334         this.ds = ds;
24335     }
24336 });/*
24337  * - LGPL
24338  *
24339  * element
24340  * 
24341  */
24342
24343 /**
24344  * @class Roo.bootstrap.MessageBar
24345  * @extends Roo.bootstrap.Component
24346  * Bootstrap MessageBar class
24347  * @cfg {String} html contents of the MessageBar
24348  * @cfg {String} weight (info | success | warning | danger) default info
24349  * @cfg {String} beforeClass insert the bar before the given class
24350  * @cfg {Boolean} closable (true | false) default false
24351  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24352  * 
24353  * @constructor
24354  * Create a new Element
24355  * @param {Object} config The config object
24356  */
24357
24358 Roo.bootstrap.MessageBar = function(config){
24359     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24360 };
24361
24362 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24363     
24364     html: '',
24365     weight: 'info',
24366     closable: false,
24367     fixed: false,
24368     beforeClass: 'bootstrap-sticky-wrap',
24369     
24370     getAutoCreate : function(){
24371         
24372         var cfg = {
24373             tag: 'div',
24374             cls: 'alert alert-dismissable alert-' + this.weight,
24375             cn: [
24376                 {
24377                     tag: 'span',
24378                     cls: 'message',
24379                     html: this.html || ''
24380                 }
24381             ]
24382         };
24383         
24384         if(this.fixed){
24385             cfg.cls += ' alert-messages-fixed';
24386         }
24387         
24388         if(this.closable){
24389             cfg.cn.push({
24390                 tag: 'button',
24391                 cls: 'close',
24392                 html: 'x'
24393             });
24394         }
24395         
24396         return cfg;
24397     },
24398     
24399     onRender : function(ct, position)
24400     {
24401         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24402         
24403         if(!this.el){
24404             var cfg = Roo.apply({},  this.getAutoCreate());
24405             cfg.id = Roo.id();
24406             
24407             if (this.cls) {
24408                 cfg.cls += ' ' + this.cls;
24409             }
24410             if (this.style) {
24411                 cfg.style = this.style;
24412             }
24413             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24414             
24415             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24416         }
24417         
24418         this.el.select('>button.close').on('click', this.hide, this);
24419         
24420     },
24421     
24422     show : function()
24423     {
24424         if (!this.rendered) {
24425             this.render();
24426         }
24427         
24428         this.el.show();
24429         
24430         this.fireEvent('show', this);
24431         
24432     },
24433     
24434     hide : function()
24435     {
24436         if (!this.rendered) {
24437             this.render();
24438         }
24439         
24440         this.el.hide();
24441         
24442         this.fireEvent('hide', this);
24443     },
24444     
24445     update : function()
24446     {
24447 //        var e = this.el.dom.firstChild;
24448 //        
24449 //        if(this.closable){
24450 //            e = e.nextSibling;
24451 //        }
24452 //        
24453 //        e.data = this.html || '';
24454
24455         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24456     }
24457    
24458 });
24459
24460  
24461
24462      /*
24463  * - LGPL
24464  *
24465  * Graph
24466  * 
24467  */
24468
24469
24470 /**
24471  * @class Roo.bootstrap.Graph
24472  * @extends Roo.bootstrap.Component
24473  * Bootstrap Graph class
24474 > Prameters
24475  -sm {number} sm 4
24476  -md {number} md 5
24477  @cfg {String} graphtype  bar | vbar | pie
24478  @cfg {number} g_x coodinator | centre x (pie)
24479  @cfg {number} g_y coodinator | centre y (pie)
24480  @cfg {number} g_r radius (pie)
24481  @cfg {number} g_height height of the chart (respected by all elements in the set)
24482  @cfg {number} g_width width of the chart (respected by all elements in the set)
24483  @cfg {Object} title The title of the chart
24484     
24485  -{Array}  values
24486  -opts (object) options for the chart 
24487      o {
24488      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24489      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24490      o vgutter (number)
24491      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.
24492      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24493      o to
24494      o stretch (boolean)
24495      o }
24496  -opts (object) options for the pie
24497      o{
24498      o cut
24499      o startAngle (number)
24500      o endAngle (number)
24501      } 
24502  *
24503  * @constructor
24504  * Create a new Input
24505  * @param {Object} config The config object
24506  */
24507
24508 Roo.bootstrap.Graph = function(config){
24509     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24510     
24511     this.addEvents({
24512         // img events
24513         /**
24514          * @event click
24515          * The img click event for the img.
24516          * @param {Roo.EventObject} e
24517          */
24518         "click" : true
24519     });
24520 };
24521
24522 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24523     
24524     sm: 4,
24525     md: 5,
24526     graphtype: 'bar',
24527     g_height: 250,
24528     g_width: 400,
24529     g_x: 50,
24530     g_y: 50,
24531     g_r: 30,
24532     opts:{
24533         //g_colors: this.colors,
24534         g_type: 'soft',
24535         g_gutter: '20%'
24536
24537     },
24538     title : false,
24539
24540     getAutoCreate : function(){
24541         
24542         var cfg = {
24543             tag: 'div',
24544             html : null
24545         };
24546         
24547         
24548         return  cfg;
24549     },
24550
24551     onRender : function(ct,position){
24552         
24553         
24554         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24555         
24556         if (typeof(Raphael) == 'undefined') {
24557             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24558             return;
24559         }
24560         
24561         this.raphael = Raphael(this.el.dom);
24562         
24563                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24564                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24565                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24566                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24567                 /*
24568                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24569                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24570                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24571                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24572                 
24573                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24574                 r.barchart(330, 10, 300, 220, data1);
24575                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24576                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24577                 */
24578                 
24579                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24580                 // r.barchart(30, 30, 560, 250,  xdata, {
24581                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24582                 //     axis : "0 0 1 1",
24583                 //     axisxlabels :  xdata
24584                 //     //yvalues : cols,
24585                    
24586                 // });
24587 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24588 //        
24589 //        this.load(null,xdata,{
24590 //                axis : "0 0 1 1",
24591 //                axisxlabels :  xdata
24592 //                });
24593
24594     },
24595
24596     load : function(graphtype,xdata,opts)
24597     {
24598         this.raphael.clear();
24599         if(!graphtype) {
24600             graphtype = this.graphtype;
24601         }
24602         if(!opts){
24603             opts = this.opts;
24604         }
24605         var r = this.raphael,
24606             fin = function () {
24607                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24608             },
24609             fout = function () {
24610                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24611             },
24612             pfin = function() {
24613                 this.sector.stop();
24614                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24615
24616                 if (this.label) {
24617                     this.label[0].stop();
24618                     this.label[0].attr({ r: 7.5 });
24619                     this.label[1].attr({ "font-weight": 800 });
24620                 }
24621             },
24622             pfout = function() {
24623                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24624
24625                 if (this.label) {
24626                     this.label[0].animate({ r: 5 }, 500, "bounce");
24627                     this.label[1].attr({ "font-weight": 400 });
24628                 }
24629             };
24630
24631         switch(graphtype){
24632             case 'bar':
24633                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24634                 break;
24635             case 'hbar':
24636                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24637                 break;
24638             case 'pie':
24639 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24640 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24641 //            
24642                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24643                 
24644                 break;
24645
24646         }
24647         
24648         if(this.title){
24649             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24650         }
24651         
24652     },
24653     
24654     setTitle: function(o)
24655     {
24656         this.title = o;
24657     },
24658     
24659     initEvents: function() {
24660         
24661         if(!this.href){
24662             this.el.on('click', this.onClick, this);
24663         }
24664     },
24665     
24666     onClick : function(e)
24667     {
24668         Roo.log('img onclick');
24669         this.fireEvent('click', this, e);
24670     }
24671    
24672 });
24673
24674  
24675 /*
24676  * - LGPL
24677  *
24678  * numberBox
24679  * 
24680  */
24681 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24682
24683 /**
24684  * @class Roo.bootstrap.dash.NumberBox
24685  * @extends Roo.bootstrap.Component
24686  * Bootstrap NumberBox class
24687  * @cfg {String} headline Box headline
24688  * @cfg {String} content Box content
24689  * @cfg {String} icon Box icon
24690  * @cfg {String} footer Footer text
24691  * @cfg {String} fhref Footer href
24692  * 
24693  * @constructor
24694  * Create a new NumberBox
24695  * @param {Object} config The config object
24696  */
24697
24698
24699 Roo.bootstrap.dash.NumberBox = function(config){
24700     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24701     
24702 };
24703
24704 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24705     
24706     headline : '',
24707     content : '',
24708     icon : '',
24709     footer : '',
24710     fhref : '',
24711     ficon : '',
24712     
24713     getAutoCreate : function(){
24714         
24715         var cfg = {
24716             tag : 'div',
24717             cls : 'small-box ',
24718             cn : [
24719                 {
24720                     tag : 'div',
24721                     cls : 'inner',
24722                     cn :[
24723                         {
24724                             tag : 'h3',
24725                             cls : 'roo-headline',
24726                             html : this.headline
24727                         },
24728                         {
24729                             tag : 'p',
24730                             cls : 'roo-content',
24731                             html : this.content
24732                         }
24733                     ]
24734                 }
24735             ]
24736         };
24737         
24738         if(this.icon){
24739             cfg.cn.push({
24740                 tag : 'div',
24741                 cls : 'icon',
24742                 cn :[
24743                     {
24744                         tag : 'i',
24745                         cls : 'ion ' + this.icon
24746                     }
24747                 ]
24748             });
24749         }
24750         
24751         if(this.footer){
24752             var footer = {
24753                 tag : 'a',
24754                 cls : 'small-box-footer',
24755                 href : this.fhref || '#',
24756                 html : this.footer
24757             };
24758             
24759             cfg.cn.push(footer);
24760             
24761         }
24762         
24763         return  cfg;
24764     },
24765
24766     onRender : function(ct,position){
24767         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24768
24769
24770        
24771                 
24772     },
24773
24774     setHeadline: function (value)
24775     {
24776         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24777     },
24778     
24779     setFooter: function (value, href)
24780     {
24781         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24782         
24783         if(href){
24784             this.el.select('a.small-box-footer',true).first().attr('href', href);
24785         }
24786         
24787     },
24788
24789     setContent: function (value)
24790     {
24791         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24792     },
24793
24794     initEvents: function() 
24795     {   
24796         
24797     }
24798     
24799 });
24800
24801  
24802 /*
24803  * - LGPL
24804  *
24805  * TabBox
24806  * 
24807  */
24808 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24809
24810 /**
24811  * @class Roo.bootstrap.dash.TabBox
24812  * @extends Roo.bootstrap.Component
24813  * Bootstrap TabBox class
24814  * @cfg {String} title Title of the TabBox
24815  * @cfg {String} icon Icon of the TabBox
24816  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24817  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24818  * 
24819  * @constructor
24820  * Create a new TabBox
24821  * @param {Object} config The config object
24822  */
24823
24824
24825 Roo.bootstrap.dash.TabBox = function(config){
24826     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24827     this.addEvents({
24828         // raw events
24829         /**
24830          * @event addpane
24831          * When a pane is added
24832          * @param {Roo.bootstrap.dash.TabPane} pane
24833          */
24834         "addpane" : true,
24835         /**
24836          * @event activatepane
24837          * When a pane is activated
24838          * @param {Roo.bootstrap.dash.TabPane} pane
24839          */
24840         "activatepane" : true
24841         
24842          
24843     });
24844     
24845     this.panes = [];
24846 };
24847
24848 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24849
24850     title : '',
24851     icon : false,
24852     showtabs : true,
24853     tabScrollable : false,
24854     
24855     getChildContainer : function()
24856     {
24857         return this.el.select('.tab-content', true).first();
24858     },
24859     
24860     getAutoCreate : function(){
24861         
24862         var header = {
24863             tag: 'li',
24864             cls: 'pull-left header',
24865             html: this.title,
24866             cn : []
24867         };
24868         
24869         if(this.icon){
24870             header.cn.push({
24871                 tag: 'i',
24872                 cls: 'fa ' + this.icon
24873             });
24874         }
24875         
24876         var h = {
24877             tag: 'ul',
24878             cls: 'nav nav-tabs pull-right',
24879             cn: [
24880                 header
24881             ]
24882         };
24883         
24884         if(this.tabScrollable){
24885             h = {
24886                 tag: 'div',
24887                 cls: 'tab-header',
24888                 cn: [
24889                     {
24890                         tag: 'ul',
24891                         cls: 'nav nav-tabs pull-right',
24892                         cn: [
24893                             header
24894                         ]
24895                     }
24896                 ]
24897             };
24898         }
24899         
24900         var cfg = {
24901             tag: 'div',
24902             cls: 'nav-tabs-custom',
24903             cn: [
24904                 h,
24905                 {
24906                     tag: 'div',
24907                     cls: 'tab-content no-padding',
24908                     cn: []
24909                 }
24910             ]
24911         };
24912
24913         return  cfg;
24914     },
24915     initEvents : function()
24916     {
24917         //Roo.log('add add pane handler');
24918         this.on('addpane', this.onAddPane, this);
24919     },
24920      /**
24921      * Updates the box title
24922      * @param {String} html to set the title to.
24923      */
24924     setTitle : function(value)
24925     {
24926         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24927     },
24928     onAddPane : function(pane)
24929     {
24930         this.panes.push(pane);
24931         //Roo.log('addpane');
24932         //Roo.log(pane);
24933         // tabs are rendere left to right..
24934         if(!this.showtabs){
24935             return;
24936         }
24937         
24938         var ctr = this.el.select('.nav-tabs', true).first();
24939          
24940          
24941         var existing = ctr.select('.nav-tab',true);
24942         var qty = existing.getCount();;
24943         
24944         
24945         var tab = ctr.createChild({
24946             tag : 'li',
24947             cls : 'nav-tab' + (qty ? '' : ' active'),
24948             cn : [
24949                 {
24950                     tag : 'a',
24951                     href:'#',
24952                     html : pane.title
24953                 }
24954             ]
24955         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24956         pane.tab = tab;
24957         
24958         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24959         if (!qty) {
24960             pane.el.addClass('active');
24961         }
24962         
24963                 
24964     },
24965     onTabClick : function(ev,un,ob,pane)
24966     {
24967         //Roo.log('tab - prev default');
24968         ev.preventDefault();
24969         
24970         
24971         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24972         pane.tab.addClass('active');
24973         //Roo.log(pane.title);
24974         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24975         // technically we should have a deactivate event.. but maybe add later.
24976         // and it should not de-activate the selected tab...
24977         this.fireEvent('activatepane', pane);
24978         pane.el.addClass('active');
24979         pane.fireEvent('activate');
24980         
24981         
24982     },
24983     
24984     getActivePane : function()
24985     {
24986         var r = false;
24987         Roo.each(this.panes, function(p) {
24988             if(p.el.hasClass('active')){
24989                 r = p;
24990                 return false;
24991             }
24992             
24993             return;
24994         });
24995         
24996         return r;
24997     }
24998     
24999     
25000 });
25001
25002  
25003 /*
25004  * - LGPL
25005  *
25006  * Tab pane
25007  * 
25008  */
25009 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25010 /**
25011  * @class Roo.bootstrap.TabPane
25012  * @extends Roo.bootstrap.Component
25013  * Bootstrap TabPane class
25014  * @cfg {Boolean} active (false | true) Default false
25015  * @cfg {String} title title of panel
25016
25017  * 
25018  * @constructor
25019  * Create a new TabPane
25020  * @param {Object} config The config object
25021  */
25022
25023 Roo.bootstrap.dash.TabPane = function(config){
25024     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25025     
25026     this.addEvents({
25027         // raw events
25028         /**
25029          * @event activate
25030          * When a pane is activated
25031          * @param {Roo.bootstrap.dash.TabPane} pane
25032          */
25033         "activate" : true
25034          
25035     });
25036 };
25037
25038 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25039     
25040     active : false,
25041     title : '',
25042     
25043     // the tabBox that this is attached to.
25044     tab : false,
25045      
25046     getAutoCreate : function() 
25047     {
25048         var cfg = {
25049             tag: 'div',
25050             cls: 'tab-pane'
25051         };
25052         
25053         if(this.active){
25054             cfg.cls += ' active';
25055         }
25056         
25057         return cfg;
25058     },
25059     initEvents  : function()
25060     {
25061         //Roo.log('trigger add pane handler');
25062         this.parent().fireEvent('addpane', this)
25063     },
25064     
25065      /**
25066      * Updates the tab title 
25067      * @param {String} html to set the title to.
25068      */
25069     setTitle: function(str)
25070     {
25071         if (!this.tab) {
25072             return;
25073         }
25074         this.title = str;
25075         this.tab.select('a', true).first().dom.innerHTML = str;
25076         
25077     }
25078     
25079     
25080     
25081 });
25082
25083  
25084
25085
25086  /*
25087  * - LGPL
25088  *
25089  * menu
25090  * 
25091  */
25092 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25093
25094 /**
25095  * @class Roo.bootstrap.menu.Menu
25096  * @extends Roo.bootstrap.Component
25097  * Bootstrap Menu class - container for Menu
25098  * @cfg {String} html Text of the menu
25099  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25100  * @cfg {String} icon Font awesome icon
25101  * @cfg {String} pos Menu align to (top | bottom) default bottom
25102  * 
25103  * 
25104  * @constructor
25105  * Create a new Menu
25106  * @param {Object} config The config object
25107  */
25108
25109
25110 Roo.bootstrap.menu.Menu = function(config){
25111     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25112     
25113     this.addEvents({
25114         /**
25115          * @event beforeshow
25116          * Fires before this menu is displayed
25117          * @param {Roo.bootstrap.menu.Menu} this
25118          */
25119         beforeshow : true,
25120         /**
25121          * @event beforehide
25122          * Fires before this menu is hidden
25123          * @param {Roo.bootstrap.menu.Menu} this
25124          */
25125         beforehide : true,
25126         /**
25127          * @event show
25128          * Fires after this menu is displayed
25129          * @param {Roo.bootstrap.menu.Menu} this
25130          */
25131         show : true,
25132         /**
25133          * @event hide
25134          * Fires after this menu is hidden
25135          * @param {Roo.bootstrap.menu.Menu} this
25136          */
25137         hide : true,
25138         /**
25139          * @event click
25140          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25141          * @param {Roo.bootstrap.menu.Menu} this
25142          * @param {Roo.EventObject} e
25143          */
25144         click : true
25145     });
25146     
25147 };
25148
25149 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25150     
25151     submenu : false,
25152     html : '',
25153     weight : 'default',
25154     icon : false,
25155     pos : 'bottom',
25156     
25157     
25158     getChildContainer : function() {
25159         if(this.isSubMenu){
25160             return this.el;
25161         }
25162         
25163         return this.el.select('ul.dropdown-menu', true).first();  
25164     },
25165     
25166     getAutoCreate : function()
25167     {
25168         var text = [
25169             {
25170                 tag : 'span',
25171                 cls : 'roo-menu-text',
25172                 html : this.html
25173             }
25174         ];
25175         
25176         if(this.icon){
25177             text.unshift({
25178                 tag : 'i',
25179                 cls : 'fa ' + this.icon
25180             })
25181         }
25182         
25183         
25184         var cfg = {
25185             tag : 'div',
25186             cls : 'btn-group',
25187             cn : [
25188                 {
25189                     tag : 'button',
25190                     cls : 'dropdown-button btn btn-' + this.weight,
25191                     cn : text
25192                 },
25193                 {
25194                     tag : 'button',
25195                     cls : 'dropdown-toggle btn btn-' + this.weight,
25196                     cn : [
25197                         {
25198                             tag : 'span',
25199                             cls : 'caret'
25200                         }
25201                     ]
25202                 },
25203                 {
25204                     tag : 'ul',
25205                     cls : 'dropdown-menu'
25206                 }
25207             ]
25208             
25209         };
25210         
25211         if(this.pos == 'top'){
25212             cfg.cls += ' dropup';
25213         }
25214         
25215         if(this.isSubMenu){
25216             cfg = {
25217                 tag : 'ul',
25218                 cls : 'dropdown-menu'
25219             }
25220         }
25221         
25222         return cfg;
25223     },
25224     
25225     onRender : function(ct, position)
25226     {
25227         this.isSubMenu = ct.hasClass('dropdown-submenu');
25228         
25229         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25230     },
25231     
25232     initEvents : function() 
25233     {
25234         if(this.isSubMenu){
25235             return;
25236         }
25237         
25238         this.hidden = true;
25239         
25240         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25241         this.triggerEl.on('click', this.onTriggerPress, this);
25242         
25243         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25244         this.buttonEl.on('click', this.onClick, this);
25245         
25246     },
25247     
25248     list : function()
25249     {
25250         if(this.isSubMenu){
25251             return this.el;
25252         }
25253         
25254         return this.el.select('ul.dropdown-menu', true).first();
25255     },
25256     
25257     onClick : function(e)
25258     {
25259         this.fireEvent("click", this, e);
25260     },
25261     
25262     onTriggerPress  : function(e)
25263     {   
25264         if (this.isVisible()) {
25265             this.hide();
25266         } else {
25267             this.show();
25268         }
25269     },
25270     
25271     isVisible : function(){
25272         return !this.hidden;
25273     },
25274     
25275     show : function()
25276     {
25277         this.fireEvent("beforeshow", this);
25278         
25279         this.hidden = false;
25280         this.el.addClass('open');
25281         
25282         Roo.get(document).on("mouseup", this.onMouseUp, this);
25283         
25284         this.fireEvent("show", this);
25285         
25286         
25287     },
25288     
25289     hide : function()
25290     {
25291         this.fireEvent("beforehide", this);
25292         
25293         this.hidden = true;
25294         this.el.removeClass('open');
25295         
25296         Roo.get(document).un("mouseup", this.onMouseUp);
25297         
25298         this.fireEvent("hide", this);
25299     },
25300     
25301     onMouseUp : function()
25302     {
25303         this.hide();
25304     }
25305     
25306 });
25307
25308  
25309  /*
25310  * - LGPL
25311  *
25312  * menu item
25313  * 
25314  */
25315 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25316
25317 /**
25318  * @class Roo.bootstrap.menu.Item
25319  * @extends Roo.bootstrap.Component
25320  * Bootstrap MenuItem class
25321  * @cfg {Boolean} submenu (true | false) default false
25322  * @cfg {String} html text of the item
25323  * @cfg {String} href the link
25324  * @cfg {Boolean} disable (true | false) default false
25325  * @cfg {Boolean} preventDefault (true | false) default true
25326  * @cfg {String} icon Font awesome icon
25327  * @cfg {String} pos Submenu align to (left | right) default right 
25328  * 
25329  * 
25330  * @constructor
25331  * Create a new Item
25332  * @param {Object} config The config object
25333  */
25334
25335
25336 Roo.bootstrap.menu.Item = function(config){
25337     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25338     this.addEvents({
25339         /**
25340          * @event mouseover
25341          * Fires when the mouse is hovering over this menu
25342          * @param {Roo.bootstrap.menu.Item} this
25343          * @param {Roo.EventObject} e
25344          */
25345         mouseover : true,
25346         /**
25347          * @event mouseout
25348          * Fires when the mouse exits this menu
25349          * @param {Roo.bootstrap.menu.Item} this
25350          * @param {Roo.EventObject} e
25351          */
25352         mouseout : true,
25353         // raw events
25354         /**
25355          * @event click
25356          * The raw click event for the entire grid.
25357          * @param {Roo.EventObject} e
25358          */
25359         click : true
25360     });
25361 };
25362
25363 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25364     
25365     submenu : false,
25366     href : '',
25367     html : '',
25368     preventDefault: true,
25369     disable : false,
25370     icon : false,
25371     pos : 'right',
25372     
25373     getAutoCreate : function()
25374     {
25375         var text = [
25376             {
25377                 tag : 'span',
25378                 cls : 'roo-menu-item-text',
25379                 html : this.html
25380             }
25381         ];
25382         
25383         if(this.icon){
25384             text.unshift({
25385                 tag : 'i',
25386                 cls : 'fa ' + this.icon
25387             })
25388         }
25389         
25390         var cfg = {
25391             tag : 'li',
25392             cn : [
25393                 {
25394                     tag : 'a',
25395                     href : this.href || '#',
25396                     cn : text
25397                 }
25398             ]
25399         };
25400         
25401         if(this.disable){
25402             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25403         }
25404         
25405         if(this.submenu){
25406             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25407             
25408             if(this.pos == 'left'){
25409                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25410             }
25411         }
25412         
25413         return cfg;
25414     },
25415     
25416     initEvents : function() 
25417     {
25418         this.el.on('mouseover', this.onMouseOver, this);
25419         this.el.on('mouseout', this.onMouseOut, this);
25420         
25421         this.el.select('a', true).first().on('click', this.onClick, this);
25422         
25423     },
25424     
25425     onClick : function(e)
25426     {
25427         if(this.preventDefault){
25428             e.preventDefault();
25429         }
25430         
25431         this.fireEvent("click", this, e);
25432     },
25433     
25434     onMouseOver : function(e)
25435     {
25436         if(this.submenu && this.pos == 'left'){
25437             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25438         }
25439         
25440         this.fireEvent("mouseover", this, e);
25441     },
25442     
25443     onMouseOut : function(e)
25444     {
25445         this.fireEvent("mouseout", this, e);
25446     }
25447 });
25448
25449  
25450
25451  /*
25452  * - LGPL
25453  *
25454  * menu separator
25455  * 
25456  */
25457 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25458
25459 /**
25460  * @class Roo.bootstrap.menu.Separator
25461  * @extends Roo.bootstrap.Component
25462  * Bootstrap Separator class
25463  * 
25464  * @constructor
25465  * Create a new Separator
25466  * @param {Object} config The config object
25467  */
25468
25469
25470 Roo.bootstrap.menu.Separator = function(config){
25471     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25472 };
25473
25474 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25475     
25476     getAutoCreate : function(){
25477         var cfg = {
25478             tag : 'li',
25479             cls: 'divider'
25480         };
25481         
25482         return cfg;
25483     }
25484    
25485 });
25486
25487  
25488
25489  /*
25490  * - LGPL
25491  *
25492  * Tooltip
25493  * 
25494  */
25495
25496 /**
25497  * @class Roo.bootstrap.Tooltip
25498  * Bootstrap Tooltip class
25499  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25500  * to determine which dom element triggers the tooltip.
25501  * 
25502  * It needs to add support for additional attributes like tooltip-position
25503  * 
25504  * @constructor
25505  * Create a new Toolti
25506  * @param {Object} config The config object
25507  */
25508
25509 Roo.bootstrap.Tooltip = function(config){
25510     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25511     
25512     this.alignment = Roo.bootstrap.Tooltip.alignment;
25513     
25514     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25515         this.alignment = config.alignment;
25516     }
25517     
25518 };
25519
25520 Roo.apply(Roo.bootstrap.Tooltip, {
25521     /**
25522      * @function init initialize tooltip monitoring.
25523      * @static
25524      */
25525     currentEl : false,
25526     currentTip : false,
25527     currentRegion : false,
25528     
25529     //  init : delay?
25530     
25531     init : function()
25532     {
25533         Roo.get(document).on('mouseover', this.enter ,this);
25534         Roo.get(document).on('mouseout', this.leave, this);
25535          
25536         
25537         this.currentTip = new Roo.bootstrap.Tooltip();
25538     },
25539     
25540     enter : function(ev)
25541     {
25542         var dom = ev.getTarget();
25543         
25544         //Roo.log(['enter',dom]);
25545         var el = Roo.fly(dom);
25546         if (this.currentEl) {
25547             //Roo.log(dom);
25548             //Roo.log(this.currentEl);
25549             //Roo.log(this.currentEl.contains(dom));
25550             if (this.currentEl == el) {
25551                 return;
25552             }
25553             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25554                 return;
25555             }
25556
25557         }
25558         
25559         if (this.currentTip.el) {
25560             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25561         }    
25562         //Roo.log(ev);
25563         
25564         if(!el || el.dom == document){
25565             return;
25566         }
25567         
25568         var bindEl = el;
25569         
25570         // you can not look for children, as if el is the body.. then everythign is the child..
25571         if (!el.attr('tooltip')) { //
25572             if (!el.select("[tooltip]").elements.length) {
25573                 return;
25574             }
25575             // is the mouse over this child...?
25576             bindEl = el.select("[tooltip]").first();
25577             var xy = ev.getXY();
25578             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25579                 //Roo.log("not in region.");
25580                 return;
25581             }
25582             //Roo.log("child element over..");
25583             
25584         }
25585         this.currentEl = bindEl;
25586         this.currentTip.bind(bindEl);
25587         this.currentRegion = Roo.lib.Region.getRegion(dom);
25588         this.currentTip.enter();
25589         
25590     },
25591     leave : function(ev)
25592     {
25593         var dom = ev.getTarget();
25594         //Roo.log(['leave',dom]);
25595         if (!this.currentEl) {
25596             return;
25597         }
25598         
25599         
25600         if (dom != this.currentEl.dom) {
25601             return;
25602         }
25603         var xy = ev.getXY();
25604         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25605             return;
25606         }
25607         // only activate leave if mouse cursor is outside... bounding box..
25608         
25609         
25610         
25611         
25612         if (this.currentTip) {
25613             this.currentTip.leave();
25614         }
25615         //Roo.log('clear currentEl');
25616         this.currentEl = false;
25617         
25618         
25619     },
25620     alignment : {
25621         'left' : ['r-l', [-2,0], 'right'],
25622         'right' : ['l-r', [2,0], 'left'],
25623         'bottom' : ['t-b', [0,2], 'top'],
25624         'top' : [ 'b-t', [0,-2], 'bottom']
25625     }
25626     
25627 });
25628
25629
25630 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25631     
25632     
25633     bindEl : false,
25634     
25635     delay : null, // can be { show : 300 , hide: 500}
25636     
25637     timeout : null,
25638     
25639     hoverState : null, //???
25640     
25641     placement : 'bottom', 
25642     
25643     alignment : false,
25644     
25645     getAutoCreate : function(){
25646     
25647         var cfg = {
25648            cls : 'tooltip',
25649            role : 'tooltip',
25650            cn : [
25651                 {
25652                     cls : 'tooltip-arrow'
25653                 },
25654                 {
25655                     cls : 'tooltip-inner'
25656                 }
25657            ]
25658         };
25659         
25660         return cfg;
25661     },
25662     bind : function(el)
25663     {
25664         this.bindEl = el;
25665     },
25666       
25667     
25668     enter : function () {
25669        
25670         if (this.timeout != null) {
25671             clearTimeout(this.timeout);
25672         }
25673         
25674         this.hoverState = 'in';
25675          //Roo.log("enter - show");
25676         if (!this.delay || !this.delay.show) {
25677             this.show();
25678             return;
25679         }
25680         var _t = this;
25681         this.timeout = setTimeout(function () {
25682             if (_t.hoverState == 'in') {
25683                 _t.show();
25684             }
25685         }, this.delay.show);
25686     },
25687     leave : function()
25688     {
25689         clearTimeout(this.timeout);
25690     
25691         this.hoverState = 'out';
25692          if (!this.delay || !this.delay.hide) {
25693             this.hide();
25694             return;
25695         }
25696        
25697         var _t = this;
25698         this.timeout = setTimeout(function () {
25699             //Roo.log("leave - timeout");
25700             
25701             if (_t.hoverState == 'out') {
25702                 _t.hide();
25703                 Roo.bootstrap.Tooltip.currentEl = false;
25704             }
25705         }, delay);
25706     },
25707     
25708     show : function (msg)
25709     {
25710         if (!this.el) {
25711             this.render(document.body);
25712         }
25713         // set content.
25714         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25715         
25716         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25717         
25718         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25719         
25720         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25721         
25722         var placement = typeof this.placement == 'function' ?
25723             this.placement.call(this, this.el, on_el) :
25724             this.placement;
25725             
25726         var autoToken = /\s?auto?\s?/i;
25727         var autoPlace = autoToken.test(placement);
25728         if (autoPlace) {
25729             placement = placement.replace(autoToken, '') || 'top';
25730         }
25731         
25732         //this.el.detach()
25733         //this.el.setXY([0,0]);
25734         this.el.show();
25735         //this.el.dom.style.display='block';
25736         
25737         //this.el.appendTo(on_el);
25738         
25739         var p = this.getPosition();
25740         var box = this.el.getBox();
25741         
25742         if (autoPlace) {
25743             // fixme..
25744         }
25745         
25746         var align = this.alignment[placement];
25747         
25748         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25749         
25750         if(placement == 'top' || placement == 'bottom'){
25751             if(xy[0] < 0){
25752                 placement = 'right';
25753             }
25754             
25755             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25756                 placement = 'left';
25757             }
25758             
25759             var scroll = Roo.select('body', true).first().getScroll();
25760             
25761             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25762                 placement = 'top';
25763             }
25764             
25765         }
25766         
25767         this.el.alignTo(this.bindEl, align[0],align[1]);
25768         //var arrow = this.el.select('.arrow',true).first();
25769         //arrow.set(align[2], 
25770         
25771         this.el.addClass(placement);
25772         
25773         this.el.addClass('in fade');
25774         
25775         this.hoverState = null;
25776         
25777         if (this.el.hasClass('fade')) {
25778             // fade it?
25779         }
25780         
25781     },
25782     hide : function()
25783     {
25784          
25785         if (!this.el) {
25786             return;
25787         }
25788         //this.el.setXY([0,0]);
25789         this.el.removeClass('in');
25790         //this.el.hide();
25791         
25792     }
25793     
25794 });
25795  
25796
25797  /*
25798  * - LGPL
25799  *
25800  * Location Picker
25801  * 
25802  */
25803
25804 /**
25805  * @class Roo.bootstrap.LocationPicker
25806  * @extends Roo.bootstrap.Component
25807  * Bootstrap LocationPicker class
25808  * @cfg {Number} latitude Position when init default 0
25809  * @cfg {Number} longitude Position when init default 0
25810  * @cfg {Number} zoom default 15
25811  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25812  * @cfg {Boolean} mapTypeControl default false
25813  * @cfg {Boolean} disableDoubleClickZoom default false
25814  * @cfg {Boolean} scrollwheel default true
25815  * @cfg {Boolean} streetViewControl default false
25816  * @cfg {Number} radius default 0
25817  * @cfg {String} locationName
25818  * @cfg {Boolean} draggable default true
25819  * @cfg {Boolean} enableAutocomplete default false
25820  * @cfg {Boolean} enableReverseGeocode default true
25821  * @cfg {String} markerTitle
25822  * 
25823  * @constructor
25824  * Create a new LocationPicker
25825  * @param {Object} config The config object
25826  */
25827
25828
25829 Roo.bootstrap.LocationPicker = function(config){
25830     
25831     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25832     
25833     this.addEvents({
25834         /**
25835          * @event initial
25836          * Fires when the picker initialized.
25837          * @param {Roo.bootstrap.LocationPicker} this
25838          * @param {Google Location} location
25839          */
25840         initial : true,
25841         /**
25842          * @event positionchanged
25843          * Fires when the picker position changed.
25844          * @param {Roo.bootstrap.LocationPicker} this
25845          * @param {Google Location} location
25846          */
25847         positionchanged : true,
25848         /**
25849          * @event resize
25850          * Fires when the map resize.
25851          * @param {Roo.bootstrap.LocationPicker} this
25852          */
25853         resize : true,
25854         /**
25855          * @event show
25856          * Fires when the map show.
25857          * @param {Roo.bootstrap.LocationPicker} this
25858          */
25859         show : true,
25860         /**
25861          * @event hide
25862          * Fires when the map hide.
25863          * @param {Roo.bootstrap.LocationPicker} this
25864          */
25865         hide : true,
25866         /**
25867          * @event mapClick
25868          * Fires when click the map.
25869          * @param {Roo.bootstrap.LocationPicker} this
25870          * @param {Map event} e
25871          */
25872         mapClick : true,
25873         /**
25874          * @event mapRightClick
25875          * Fires when right click the map.
25876          * @param {Roo.bootstrap.LocationPicker} this
25877          * @param {Map event} e
25878          */
25879         mapRightClick : true,
25880         /**
25881          * @event markerClick
25882          * Fires when click the marker.
25883          * @param {Roo.bootstrap.LocationPicker} this
25884          * @param {Map event} e
25885          */
25886         markerClick : true,
25887         /**
25888          * @event markerRightClick
25889          * Fires when right click the marker.
25890          * @param {Roo.bootstrap.LocationPicker} this
25891          * @param {Map event} e
25892          */
25893         markerRightClick : true,
25894         /**
25895          * @event OverlayViewDraw
25896          * Fires when OverlayView Draw
25897          * @param {Roo.bootstrap.LocationPicker} this
25898          */
25899         OverlayViewDraw : true,
25900         /**
25901          * @event OverlayViewOnAdd
25902          * Fires when OverlayView Draw
25903          * @param {Roo.bootstrap.LocationPicker} this
25904          */
25905         OverlayViewOnAdd : true,
25906         /**
25907          * @event OverlayViewOnRemove
25908          * Fires when OverlayView Draw
25909          * @param {Roo.bootstrap.LocationPicker} this
25910          */
25911         OverlayViewOnRemove : true,
25912         /**
25913          * @event OverlayViewShow
25914          * Fires when OverlayView Draw
25915          * @param {Roo.bootstrap.LocationPicker} this
25916          * @param {Pixel} cpx
25917          */
25918         OverlayViewShow : true,
25919         /**
25920          * @event OverlayViewHide
25921          * Fires when OverlayView Draw
25922          * @param {Roo.bootstrap.LocationPicker} this
25923          */
25924         OverlayViewHide : true,
25925         /**
25926          * @event loadexception
25927          * Fires when load google lib failed.
25928          * @param {Roo.bootstrap.LocationPicker} this
25929          */
25930         loadexception : true
25931     });
25932         
25933 };
25934
25935 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25936     
25937     gMapContext: false,
25938     
25939     latitude: 0,
25940     longitude: 0,
25941     zoom: 15,
25942     mapTypeId: false,
25943     mapTypeControl: false,
25944     disableDoubleClickZoom: false,
25945     scrollwheel: true,
25946     streetViewControl: false,
25947     radius: 0,
25948     locationName: '',
25949     draggable: true,
25950     enableAutocomplete: false,
25951     enableReverseGeocode: true,
25952     markerTitle: '',
25953     
25954     getAutoCreate: function()
25955     {
25956
25957         var cfg = {
25958             tag: 'div',
25959             cls: 'roo-location-picker'
25960         };
25961         
25962         return cfg
25963     },
25964     
25965     initEvents: function(ct, position)
25966     {       
25967         if(!this.el.getWidth() || this.isApplied()){
25968             return;
25969         }
25970         
25971         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25972         
25973         this.initial();
25974     },
25975     
25976     initial: function()
25977     {
25978         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25979             this.fireEvent('loadexception', this);
25980             return;
25981         }
25982         
25983         if(!this.mapTypeId){
25984             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25985         }
25986         
25987         this.gMapContext = this.GMapContext();
25988         
25989         this.initOverlayView();
25990         
25991         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25992         
25993         var _this = this;
25994                 
25995         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25996             _this.setPosition(_this.gMapContext.marker.position);
25997         });
25998         
25999         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26000             _this.fireEvent('mapClick', this, event);
26001             
26002         });
26003
26004         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26005             _this.fireEvent('mapRightClick', this, event);
26006             
26007         });
26008         
26009         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26010             _this.fireEvent('markerClick', this, event);
26011             
26012         });
26013
26014         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26015             _this.fireEvent('markerRightClick', this, event);
26016             
26017         });
26018         
26019         this.setPosition(this.gMapContext.location);
26020         
26021         this.fireEvent('initial', this, this.gMapContext.location);
26022     },
26023     
26024     initOverlayView: function()
26025     {
26026         var _this = this;
26027         
26028         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26029             
26030             draw: function()
26031             {
26032                 _this.fireEvent('OverlayViewDraw', _this);
26033             },
26034             
26035             onAdd: function()
26036             {
26037                 _this.fireEvent('OverlayViewOnAdd', _this);
26038             },
26039             
26040             onRemove: function()
26041             {
26042                 _this.fireEvent('OverlayViewOnRemove', _this);
26043             },
26044             
26045             show: function(cpx)
26046             {
26047                 _this.fireEvent('OverlayViewShow', _this, cpx);
26048             },
26049             
26050             hide: function()
26051             {
26052                 _this.fireEvent('OverlayViewHide', _this);
26053             }
26054             
26055         });
26056     },
26057     
26058     fromLatLngToContainerPixel: function(event)
26059     {
26060         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26061     },
26062     
26063     isApplied: function() 
26064     {
26065         return this.getGmapContext() == false ? false : true;
26066     },
26067     
26068     getGmapContext: function() 
26069     {
26070         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26071     },
26072     
26073     GMapContext: function() 
26074     {
26075         var position = new google.maps.LatLng(this.latitude, this.longitude);
26076         
26077         var _map = new google.maps.Map(this.el.dom, {
26078             center: position,
26079             zoom: this.zoom,
26080             mapTypeId: this.mapTypeId,
26081             mapTypeControl: this.mapTypeControl,
26082             disableDoubleClickZoom: this.disableDoubleClickZoom,
26083             scrollwheel: this.scrollwheel,
26084             streetViewControl: this.streetViewControl,
26085             locationName: this.locationName,
26086             draggable: this.draggable,
26087             enableAutocomplete: this.enableAutocomplete,
26088             enableReverseGeocode: this.enableReverseGeocode
26089         });
26090         
26091         var _marker = new google.maps.Marker({
26092             position: position,
26093             map: _map,
26094             title: this.markerTitle,
26095             draggable: this.draggable
26096         });
26097         
26098         return {
26099             map: _map,
26100             marker: _marker,
26101             circle: null,
26102             location: position,
26103             radius: this.radius,
26104             locationName: this.locationName,
26105             addressComponents: {
26106                 formatted_address: null,
26107                 addressLine1: null,
26108                 addressLine2: null,
26109                 streetName: null,
26110                 streetNumber: null,
26111                 city: null,
26112                 district: null,
26113                 state: null,
26114                 stateOrProvince: null
26115             },
26116             settings: this,
26117             domContainer: this.el.dom,
26118             geodecoder: new google.maps.Geocoder()
26119         };
26120     },
26121     
26122     drawCircle: function(center, radius, options) 
26123     {
26124         if (this.gMapContext.circle != null) {
26125             this.gMapContext.circle.setMap(null);
26126         }
26127         if (radius > 0) {
26128             radius *= 1;
26129             options = Roo.apply({}, options, {
26130                 strokeColor: "#0000FF",
26131                 strokeOpacity: .35,
26132                 strokeWeight: 2,
26133                 fillColor: "#0000FF",
26134                 fillOpacity: .2
26135             });
26136             
26137             options.map = this.gMapContext.map;
26138             options.radius = radius;
26139             options.center = center;
26140             this.gMapContext.circle = new google.maps.Circle(options);
26141             return this.gMapContext.circle;
26142         }
26143         
26144         return null;
26145     },
26146     
26147     setPosition: function(location) 
26148     {
26149         this.gMapContext.location = location;
26150         this.gMapContext.marker.setPosition(location);
26151         this.gMapContext.map.panTo(location);
26152         this.drawCircle(location, this.gMapContext.radius, {});
26153         
26154         var _this = this;
26155         
26156         if (this.gMapContext.settings.enableReverseGeocode) {
26157             this.gMapContext.geodecoder.geocode({
26158                 latLng: this.gMapContext.location
26159             }, function(results, status) {
26160                 
26161                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26162                     _this.gMapContext.locationName = results[0].formatted_address;
26163                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26164                     
26165                     _this.fireEvent('positionchanged', this, location);
26166                 }
26167             });
26168             
26169             return;
26170         }
26171         
26172         this.fireEvent('positionchanged', this, location);
26173     },
26174     
26175     resize: function()
26176     {
26177         google.maps.event.trigger(this.gMapContext.map, "resize");
26178         
26179         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26180         
26181         this.fireEvent('resize', this);
26182     },
26183     
26184     setPositionByLatLng: function(latitude, longitude)
26185     {
26186         this.setPosition(new google.maps.LatLng(latitude, longitude));
26187     },
26188     
26189     getCurrentPosition: function() 
26190     {
26191         return {
26192             latitude: this.gMapContext.location.lat(),
26193             longitude: this.gMapContext.location.lng()
26194         };
26195     },
26196     
26197     getAddressName: function() 
26198     {
26199         return this.gMapContext.locationName;
26200     },
26201     
26202     getAddressComponents: function() 
26203     {
26204         return this.gMapContext.addressComponents;
26205     },
26206     
26207     address_component_from_google_geocode: function(address_components) 
26208     {
26209         var result = {};
26210         
26211         for (var i = 0; i < address_components.length; i++) {
26212             var component = address_components[i];
26213             if (component.types.indexOf("postal_code") >= 0) {
26214                 result.postalCode = component.short_name;
26215             } else if (component.types.indexOf("street_number") >= 0) {
26216                 result.streetNumber = component.short_name;
26217             } else if (component.types.indexOf("route") >= 0) {
26218                 result.streetName = component.short_name;
26219             } else if (component.types.indexOf("neighborhood") >= 0) {
26220                 result.city = component.short_name;
26221             } else if (component.types.indexOf("locality") >= 0) {
26222                 result.city = component.short_name;
26223             } else if (component.types.indexOf("sublocality") >= 0) {
26224                 result.district = component.short_name;
26225             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26226                 result.stateOrProvince = component.short_name;
26227             } else if (component.types.indexOf("country") >= 0) {
26228                 result.country = component.short_name;
26229             }
26230         }
26231         
26232         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26233         result.addressLine2 = "";
26234         return result;
26235     },
26236     
26237     setZoomLevel: function(zoom)
26238     {
26239         this.gMapContext.map.setZoom(zoom);
26240     },
26241     
26242     show: function()
26243     {
26244         if(!this.el){
26245             return;
26246         }
26247         
26248         this.el.show();
26249         
26250         this.resize();
26251         
26252         this.fireEvent('show', this);
26253     },
26254     
26255     hide: function()
26256     {
26257         if(!this.el){
26258             return;
26259         }
26260         
26261         this.el.hide();
26262         
26263         this.fireEvent('hide', this);
26264     }
26265     
26266 });
26267
26268 Roo.apply(Roo.bootstrap.LocationPicker, {
26269     
26270     OverlayView : function(map, options)
26271     {
26272         options = options || {};
26273         
26274         this.setMap(map);
26275     }
26276     
26277     
26278 });/*
26279  * - LGPL
26280  *
26281  * Alert
26282  * 
26283  */
26284
26285 /**
26286  * @class Roo.bootstrap.Alert
26287  * @extends Roo.bootstrap.Component
26288  * Bootstrap Alert class
26289  * @cfg {String} title The title of alert
26290  * @cfg {String} html The content of alert
26291  * @cfg {String} weight (  success | info | warning | danger )
26292  * @cfg {String} faicon font-awesomeicon
26293  * 
26294  * @constructor
26295  * Create a new alert
26296  * @param {Object} config The config object
26297  */
26298
26299
26300 Roo.bootstrap.Alert = function(config){
26301     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26302     
26303 };
26304
26305 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26306     
26307     title: '',
26308     html: '',
26309     weight: false,
26310     faicon: false,
26311     
26312     getAutoCreate : function()
26313     {
26314         
26315         var cfg = {
26316             tag : 'div',
26317             cls : 'alert',
26318             cn : [
26319                 {
26320                     tag : 'i',
26321                     cls : 'roo-alert-icon'
26322                     
26323                 },
26324                 {
26325                     tag : 'b',
26326                     cls : 'roo-alert-title',
26327                     html : this.title
26328                 },
26329                 {
26330                     tag : 'span',
26331                     cls : 'roo-alert-text',
26332                     html : this.html
26333                 }
26334             ]
26335         };
26336         
26337         if(this.faicon){
26338             cfg.cn[0].cls += ' fa ' + this.faicon;
26339         }
26340         
26341         if(this.weight){
26342             cfg.cls += ' alert-' + this.weight;
26343         }
26344         
26345         return cfg;
26346     },
26347     
26348     initEvents: function() 
26349     {
26350         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26351     },
26352     
26353     setTitle : function(str)
26354     {
26355         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26356     },
26357     
26358     setText : function(str)
26359     {
26360         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26361     },
26362     
26363     setWeight : function(weight)
26364     {
26365         if(this.weight){
26366             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26367         }
26368         
26369         this.weight = weight;
26370         
26371         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26372     },
26373     
26374     setIcon : function(icon)
26375     {
26376         if(this.faicon){
26377             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26378         }
26379         
26380         this.faicon = icon;
26381         
26382         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26383     },
26384     
26385     hide: function() 
26386     {
26387         this.el.hide();   
26388     },
26389     
26390     show: function() 
26391     {  
26392         this.el.show();   
26393     }
26394     
26395 });
26396
26397  
26398 /*
26399 * Licence: LGPL
26400 */
26401
26402 /**
26403  * @class Roo.bootstrap.UploadCropbox
26404  * @extends Roo.bootstrap.Component
26405  * Bootstrap UploadCropbox class
26406  * @cfg {String} emptyText show when image has been loaded
26407  * @cfg {String} rotateNotify show when image too small to rotate
26408  * @cfg {Number} errorTimeout default 3000
26409  * @cfg {Number} minWidth default 300
26410  * @cfg {Number} minHeight default 300
26411  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26412  * @cfg {Boolean} isDocument (true|false) default false
26413  * @cfg {String} url action url
26414  * @cfg {String} paramName default 'imageUpload'
26415  * @cfg {String} method default POST
26416  * @cfg {Boolean} loadMask (true|false) default true
26417  * @cfg {Boolean} loadingText default 'Loading...'
26418  * 
26419  * @constructor
26420  * Create a new UploadCropbox
26421  * @param {Object} config The config object
26422  */
26423
26424 Roo.bootstrap.UploadCropbox = function(config){
26425     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26426     
26427     this.addEvents({
26428         /**
26429          * @event beforeselectfile
26430          * Fire before select file
26431          * @param {Roo.bootstrap.UploadCropbox} this
26432          */
26433         "beforeselectfile" : true,
26434         /**
26435          * @event initial
26436          * Fire after initEvent
26437          * @param {Roo.bootstrap.UploadCropbox} this
26438          */
26439         "initial" : true,
26440         /**
26441          * @event crop
26442          * Fire after initEvent
26443          * @param {Roo.bootstrap.UploadCropbox} this
26444          * @param {String} data
26445          */
26446         "crop" : true,
26447         /**
26448          * @event prepare
26449          * Fire when preparing the file data
26450          * @param {Roo.bootstrap.UploadCropbox} this
26451          * @param {Object} file
26452          */
26453         "prepare" : true,
26454         /**
26455          * @event exception
26456          * Fire when get exception
26457          * @param {Roo.bootstrap.UploadCropbox} this
26458          * @param {XMLHttpRequest} xhr
26459          */
26460         "exception" : true,
26461         /**
26462          * @event beforeloadcanvas
26463          * Fire before load the canvas
26464          * @param {Roo.bootstrap.UploadCropbox} this
26465          * @param {String} src
26466          */
26467         "beforeloadcanvas" : true,
26468         /**
26469          * @event trash
26470          * Fire when trash image
26471          * @param {Roo.bootstrap.UploadCropbox} this
26472          */
26473         "trash" : true,
26474         /**
26475          * @event download
26476          * Fire when download the image
26477          * @param {Roo.bootstrap.UploadCropbox} this
26478          */
26479         "download" : true,
26480         /**
26481          * @event footerbuttonclick
26482          * Fire when footerbuttonclick
26483          * @param {Roo.bootstrap.UploadCropbox} this
26484          * @param {String} type
26485          */
26486         "footerbuttonclick" : true,
26487         /**
26488          * @event resize
26489          * Fire when resize
26490          * @param {Roo.bootstrap.UploadCropbox} this
26491          */
26492         "resize" : true,
26493         /**
26494          * @event rotate
26495          * Fire when rotate the image
26496          * @param {Roo.bootstrap.UploadCropbox} this
26497          * @param {String} pos
26498          */
26499         "rotate" : true,
26500         /**
26501          * @event inspect
26502          * Fire when inspect the file
26503          * @param {Roo.bootstrap.UploadCropbox} this
26504          * @param {Object} file
26505          */
26506         "inspect" : true,
26507         /**
26508          * @event upload
26509          * Fire when xhr upload the file
26510          * @param {Roo.bootstrap.UploadCropbox} this
26511          * @param {Object} data
26512          */
26513         "upload" : true,
26514         /**
26515          * @event arrange
26516          * Fire when arrange the file data
26517          * @param {Roo.bootstrap.UploadCropbox} this
26518          * @param {Object} formData
26519          */
26520         "arrange" : true
26521     });
26522     
26523     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26524 };
26525
26526 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26527     
26528     emptyText : 'Click to upload image',
26529     rotateNotify : 'Image is too small to rotate',
26530     errorTimeout : 3000,
26531     scale : 0,
26532     baseScale : 1,
26533     rotate : 0,
26534     dragable : false,
26535     pinching : false,
26536     mouseX : 0,
26537     mouseY : 0,
26538     cropData : false,
26539     minWidth : 300,
26540     minHeight : 300,
26541     file : false,
26542     exif : {},
26543     baseRotate : 1,
26544     cropType : 'image/jpeg',
26545     buttons : false,
26546     canvasLoaded : false,
26547     isDocument : false,
26548     method : 'POST',
26549     paramName : 'imageUpload',
26550     loadMask : true,
26551     loadingText : 'Loading...',
26552     maskEl : false,
26553     
26554     getAutoCreate : function()
26555     {
26556         var cfg = {
26557             tag : 'div',
26558             cls : 'roo-upload-cropbox',
26559             cn : [
26560                 {
26561                     tag : 'input',
26562                     cls : 'roo-upload-cropbox-selector',
26563                     type : 'file'
26564                 },
26565                 {
26566                     tag : 'div',
26567                     cls : 'roo-upload-cropbox-body',
26568                     style : 'cursor:pointer',
26569                     cn : [
26570                         {
26571                             tag : 'div',
26572                             cls : 'roo-upload-cropbox-preview'
26573                         },
26574                         {
26575                             tag : 'div',
26576                             cls : 'roo-upload-cropbox-thumb'
26577                         },
26578                         {
26579                             tag : 'div',
26580                             cls : 'roo-upload-cropbox-empty-notify',
26581                             html : this.emptyText
26582                         },
26583                         {
26584                             tag : 'div',
26585                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26586                             html : this.rotateNotify
26587                         }
26588                     ]
26589                 },
26590                 {
26591                     tag : 'div',
26592                     cls : 'roo-upload-cropbox-footer',
26593                     cn : {
26594                         tag : 'div',
26595                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26596                         cn : []
26597                     }
26598                 }
26599             ]
26600         };
26601         
26602         return cfg;
26603     },
26604     
26605     onRender : function(ct, position)
26606     {
26607         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26608         
26609         if (this.buttons.length) {
26610             
26611             Roo.each(this.buttons, function(bb) {
26612                 
26613                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26614                 
26615                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26616                 
26617             }, this);
26618         }
26619         
26620         if(this.loadMask){
26621             this.maskEl = this.el;
26622         }
26623     },
26624     
26625     initEvents : function()
26626     {
26627         this.urlAPI = (window.createObjectURL && window) || 
26628                                 (window.URL && URL.revokeObjectURL && URL) || 
26629                                 (window.webkitURL && webkitURL);
26630                         
26631         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26632         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26633         
26634         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26635         this.selectorEl.hide();
26636         
26637         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26638         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26639         
26640         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26641         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26642         this.thumbEl.hide();
26643         
26644         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26645         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26646         
26647         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26648         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26649         this.errorEl.hide();
26650         
26651         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26652         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26653         this.footerEl.hide();
26654         
26655         this.setThumbBoxSize();
26656         
26657         this.bind();
26658         
26659         this.resize();
26660         
26661         this.fireEvent('initial', this);
26662     },
26663
26664     bind : function()
26665     {
26666         var _this = this;
26667         
26668         window.addEventListener("resize", function() { _this.resize(); } );
26669         
26670         this.bodyEl.on('click', this.beforeSelectFile, this);
26671         
26672         if(Roo.isTouch){
26673             this.bodyEl.on('touchstart', this.onTouchStart, this);
26674             this.bodyEl.on('touchmove', this.onTouchMove, this);
26675             this.bodyEl.on('touchend', this.onTouchEnd, this);
26676         }
26677         
26678         if(!Roo.isTouch){
26679             this.bodyEl.on('mousedown', this.onMouseDown, this);
26680             this.bodyEl.on('mousemove', this.onMouseMove, this);
26681             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26682             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26683             Roo.get(document).on('mouseup', this.onMouseUp, this);
26684         }
26685         
26686         this.selectorEl.on('change', this.onFileSelected, this);
26687     },
26688     
26689     reset : function()
26690     {    
26691         this.scale = 0;
26692         this.baseScale = 1;
26693         this.rotate = 0;
26694         this.baseRotate = 1;
26695         this.dragable = false;
26696         this.pinching = false;
26697         this.mouseX = 0;
26698         this.mouseY = 0;
26699         this.cropData = false;
26700         this.notifyEl.dom.innerHTML = this.emptyText;
26701         
26702         this.selectorEl.dom.value = '';
26703         
26704     },
26705     
26706     resize : function()
26707     {
26708         if(this.fireEvent('resize', this) != false){
26709             this.setThumbBoxPosition();
26710             this.setCanvasPosition();
26711         }
26712     },
26713     
26714     onFooterButtonClick : function(e, el, o, type)
26715     {
26716         switch (type) {
26717             case 'rotate-left' :
26718                 this.onRotateLeft(e);
26719                 break;
26720             case 'rotate-right' :
26721                 this.onRotateRight(e);
26722                 break;
26723             case 'picture' :
26724                 this.beforeSelectFile(e);
26725                 break;
26726             case 'trash' :
26727                 this.trash(e);
26728                 break;
26729             case 'crop' :
26730                 this.crop(e);
26731                 break;
26732             case 'download' :
26733                 this.download(e);
26734                 break;
26735             default :
26736                 break;
26737         }
26738         
26739         this.fireEvent('footerbuttonclick', this, type);
26740     },
26741     
26742     beforeSelectFile : function(e)
26743     {
26744         e.preventDefault();
26745         
26746         if(this.fireEvent('beforeselectfile', this) != false){
26747             this.selectorEl.dom.click();
26748         }
26749     },
26750     
26751     onFileSelected : function(e)
26752     {
26753         e.preventDefault();
26754         
26755         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26756             return;
26757         }
26758         
26759         var file = this.selectorEl.dom.files[0];
26760         
26761         if(this.fireEvent('inspect', this, file) != false){
26762             this.prepare(file);
26763         }
26764         
26765     },
26766     
26767     trash : function(e)
26768     {
26769         this.fireEvent('trash', this);
26770     },
26771     
26772     download : function(e)
26773     {
26774         this.fireEvent('download', this);
26775     },
26776     
26777     loadCanvas : function(src)
26778     {   
26779         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26780             
26781             this.reset();
26782             
26783             this.imageEl = document.createElement('img');
26784             
26785             var _this = this;
26786             
26787             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26788             
26789             this.imageEl.src = src;
26790         }
26791     },
26792     
26793     onLoadCanvas : function()
26794     {   
26795         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26796         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26797         
26798         this.bodyEl.un('click', this.beforeSelectFile, this);
26799         
26800         this.notifyEl.hide();
26801         this.thumbEl.show();
26802         this.footerEl.show();
26803         
26804         this.baseRotateLevel();
26805         
26806         if(this.isDocument){
26807             this.setThumbBoxSize();
26808         }
26809         
26810         this.setThumbBoxPosition();
26811         
26812         this.baseScaleLevel();
26813         
26814         this.draw();
26815         
26816         this.resize();
26817         
26818         this.canvasLoaded = true;
26819         
26820         if(this.loadMask){
26821             this.maskEl.unmask();
26822         }
26823         
26824     },
26825     
26826     setCanvasPosition : function()
26827     {   
26828         if(!this.canvasEl){
26829             return;
26830         }
26831         
26832         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26833         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26834         
26835         this.previewEl.setLeft(pw);
26836         this.previewEl.setTop(ph);
26837         
26838     },
26839     
26840     onMouseDown : function(e)
26841     {   
26842         e.stopEvent();
26843         
26844         this.dragable = true;
26845         this.pinching = false;
26846         
26847         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26848             this.dragable = false;
26849             return;
26850         }
26851         
26852         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26853         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26854         
26855     },
26856     
26857     onMouseMove : function(e)
26858     {   
26859         e.stopEvent();
26860         
26861         if(!this.canvasLoaded){
26862             return;
26863         }
26864         
26865         if (!this.dragable){
26866             return;
26867         }
26868         
26869         var minX = Math.ceil(this.thumbEl.getLeft(true));
26870         var minY = Math.ceil(this.thumbEl.getTop(true));
26871         
26872         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26873         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26874         
26875         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26876         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26877         
26878         x = x - this.mouseX;
26879         y = y - this.mouseY;
26880         
26881         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26882         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26883         
26884         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26885         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26886         
26887         this.previewEl.setLeft(bgX);
26888         this.previewEl.setTop(bgY);
26889         
26890         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26891         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26892     },
26893     
26894     onMouseUp : function(e)
26895     {   
26896         e.stopEvent();
26897         
26898         this.dragable = false;
26899     },
26900     
26901     onMouseWheel : function(e)
26902     {   
26903         e.stopEvent();
26904         
26905         this.startScale = this.scale;
26906         
26907         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26908         
26909         if(!this.zoomable()){
26910             this.scale = this.startScale;
26911             return;
26912         }
26913         
26914         this.draw();
26915         
26916         return;
26917     },
26918     
26919     zoomable : function()
26920     {
26921         var minScale = this.thumbEl.getWidth() / this.minWidth;
26922         
26923         if(this.minWidth < this.minHeight){
26924             minScale = this.thumbEl.getHeight() / this.minHeight;
26925         }
26926         
26927         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26928         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26929         
26930         if(
26931                 this.isDocument &&
26932                 (this.rotate == 0 || this.rotate == 180) && 
26933                 (
26934                     width > this.imageEl.OriginWidth || 
26935                     height > this.imageEl.OriginHeight ||
26936                     (width < this.minWidth && height < this.minHeight)
26937                 )
26938         ){
26939             return false;
26940         }
26941         
26942         if(
26943                 this.isDocument &&
26944                 (this.rotate == 90 || this.rotate == 270) && 
26945                 (
26946                     width > this.imageEl.OriginWidth || 
26947                     height > this.imageEl.OriginHeight ||
26948                     (width < this.minHeight && height < this.minWidth)
26949                 )
26950         ){
26951             return false;
26952         }
26953         
26954         if(
26955                 !this.isDocument &&
26956                 (this.rotate == 0 || this.rotate == 180) && 
26957                 (
26958                     width < this.minWidth || 
26959                     width > this.imageEl.OriginWidth || 
26960                     height < this.minHeight || 
26961                     height > this.imageEl.OriginHeight
26962                 )
26963         ){
26964             return false;
26965         }
26966         
26967         if(
26968                 !this.isDocument &&
26969                 (this.rotate == 90 || this.rotate == 270) && 
26970                 (
26971                     width < this.minHeight || 
26972                     width > this.imageEl.OriginWidth || 
26973                     height < this.minWidth || 
26974                     height > this.imageEl.OriginHeight
26975                 )
26976         ){
26977             return false;
26978         }
26979         
26980         return true;
26981         
26982     },
26983     
26984     onRotateLeft : function(e)
26985     {   
26986         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26987             
26988             var minScale = this.thumbEl.getWidth() / this.minWidth;
26989             
26990             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26991             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26992             
26993             this.startScale = this.scale;
26994             
26995             while (this.getScaleLevel() < minScale){
26996             
26997                 this.scale = this.scale + 1;
26998                 
26999                 if(!this.zoomable()){
27000                     break;
27001                 }
27002                 
27003                 if(
27004                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27005                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27006                 ){
27007                     continue;
27008                 }
27009                 
27010                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27011
27012                 this.draw();
27013                 
27014                 return;
27015             }
27016             
27017             this.scale = this.startScale;
27018             
27019             this.onRotateFail();
27020             
27021             return false;
27022         }
27023         
27024         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27025
27026         if(this.isDocument){
27027             this.setThumbBoxSize();
27028             this.setThumbBoxPosition();
27029             this.setCanvasPosition();
27030         }
27031         
27032         this.draw();
27033         
27034         this.fireEvent('rotate', this, 'left');
27035         
27036     },
27037     
27038     onRotateRight : function(e)
27039     {
27040         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27041             
27042             var minScale = this.thumbEl.getWidth() / this.minWidth;
27043         
27044             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27045             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27046             
27047             this.startScale = this.scale;
27048             
27049             while (this.getScaleLevel() < minScale){
27050             
27051                 this.scale = this.scale + 1;
27052                 
27053                 if(!this.zoomable()){
27054                     break;
27055                 }
27056                 
27057                 if(
27058                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27059                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27060                 ){
27061                     continue;
27062                 }
27063                 
27064                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27065
27066                 this.draw();
27067                 
27068                 return;
27069             }
27070             
27071             this.scale = this.startScale;
27072             
27073             this.onRotateFail();
27074             
27075             return false;
27076         }
27077         
27078         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27079
27080         if(this.isDocument){
27081             this.setThumbBoxSize();
27082             this.setThumbBoxPosition();
27083             this.setCanvasPosition();
27084         }
27085         
27086         this.draw();
27087         
27088         this.fireEvent('rotate', this, 'right');
27089     },
27090     
27091     onRotateFail : function()
27092     {
27093         this.errorEl.show(true);
27094         
27095         var _this = this;
27096         
27097         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27098     },
27099     
27100     draw : function()
27101     {
27102         this.previewEl.dom.innerHTML = '';
27103         
27104         var canvasEl = document.createElement("canvas");
27105         
27106         var contextEl = canvasEl.getContext("2d");
27107         
27108         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27109         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27110         var center = this.imageEl.OriginWidth / 2;
27111         
27112         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27113             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27114             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27115             center = this.imageEl.OriginHeight / 2;
27116         }
27117         
27118         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27119         
27120         contextEl.translate(center, center);
27121         contextEl.rotate(this.rotate * Math.PI / 180);
27122
27123         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27124         
27125         this.canvasEl = document.createElement("canvas");
27126         
27127         this.contextEl = this.canvasEl.getContext("2d");
27128         
27129         switch (this.rotate) {
27130             case 0 :
27131                 
27132                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27133                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27134                 
27135                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27136                 
27137                 break;
27138             case 90 : 
27139                 
27140                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27141                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27142                 
27143                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27144                     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);
27145                     break;
27146                 }
27147                 
27148                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27149                 
27150                 break;
27151             case 180 :
27152                 
27153                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27154                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27155                 
27156                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27157                     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);
27158                     break;
27159                 }
27160                 
27161                 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);
27162                 
27163                 break;
27164             case 270 :
27165                 
27166                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27167                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27168         
27169                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27170                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27171                     break;
27172                 }
27173                 
27174                 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);
27175                 
27176                 break;
27177             default : 
27178                 break;
27179         }
27180         
27181         this.previewEl.appendChild(this.canvasEl);
27182         
27183         this.setCanvasPosition();
27184     },
27185     
27186     crop : function()
27187     {
27188         if(!this.canvasLoaded){
27189             return;
27190         }
27191         
27192         var imageCanvas = document.createElement("canvas");
27193         
27194         var imageContext = imageCanvas.getContext("2d");
27195         
27196         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27197         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27198         
27199         var center = imageCanvas.width / 2;
27200         
27201         imageContext.translate(center, center);
27202         
27203         imageContext.rotate(this.rotate * Math.PI / 180);
27204         
27205         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27206         
27207         var canvas = document.createElement("canvas");
27208         
27209         var context = canvas.getContext("2d");
27210                 
27211         canvas.width = this.minWidth;
27212         canvas.height = this.minHeight;
27213
27214         switch (this.rotate) {
27215             case 0 :
27216                 
27217                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27218                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27219                 
27220                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27221                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27222                 
27223                 var targetWidth = this.minWidth - 2 * x;
27224                 var targetHeight = this.minHeight - 2 * y;
27225                 
27226                 var scale = 1;
27227                 
27228                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27229                     scale = targetWidth / width;
27230                 }
27231                 
27232                 if(x > 0 && y == 0){
27233                     scale = targetHeight / height;
27234                 }
27235                 
27236                 if(x > 0 && y > 0){
27237                     scale = targetWidth / width;
27238                     
27239                     if(width < height){
27240                         scale = targetHeight / height;
27241                     }
27242                 }
27243                 
27244                 context.scale(scale, scale);
27245                 
27246                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27247                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27248
27249                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27250                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27251
27252                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27253                 
27254                 break;
27255             case 90 : 
27256                 
27257                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27258                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27259                 
27260                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27261                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27262                 
27263                 var targetWidth = this.minWidth - 2 * x;
27264                 var targetHeight = this.minHeight - 2 * y;
27265                 
27266                 var scale = 1;
27267                 
27268                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27269                     scale = targetWidth / width;
27270                 }
27271                 
27272                 if(x > 0 && y == 0){
27273                     scale = targetHeight / height;
27274                 }
27275                 
27276                 if(x > 0 && y > 0){
27277                     scale = targetWidth / width;
27278                     
27279                     if(width < height){
27280                         scale = targetHeight / height;
27281                     }
27282                 }
27283                 
27284                 context.scale(scale, scale);
27285                 
27286                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27287                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27288
27289                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27290                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27291                 
27292                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27293                 
27294                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27295                 
27296                 break;
27297             case 180 :
27298                 
27299                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27300                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27301                 
27302                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27303                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27304                 
27305                 var targetWidth = this.minWidth - 2 * x;
27306                 var targetHeight = this.minHeight - 2 * y;
27307                 
27308                 var scale = 1;
27309                 
27310                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27311                     scale = targetWidth / width;
27312                 }
27313                 
27314                 if(x > 0 && y == 0){
27315                     scale = targetHeight / height;
27316                 }
27317                 
27318                 if(x > 0 && y > 0){
27319                     scale = targetWidth / width;
27320                     
27321                     if(width < height){
27322                         scale = targetHeight / height;
27323                     }
27324                 }
27325                 
27326                 context.scale(scale, scale);
27327                 
27328                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27329                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27330
27331                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27332                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27333
27334                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27335                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27336                 
27337                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27338                 
27339                 break;
27340             case 270 :
27341                 
27342                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27343                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27344                 
27345                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27346                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27347                 
27348                 var targetWidth = this.minWidth - 2 * x;
27349                 var targetHeight = this.minHeight - 2 * y;
27350                 
27351                 var scale = 1;
27352                 
27353                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27354                     scale = targetWidth / width;
27355                 }
27356                 
27357                 if(x > 0 && y == 0){
27358                     scale = targetHeight / height;
27359                 }
27360                 
27361                 if(x > 0 && y > 0){
27362                     scale = targetWidth / width;
27363                     
27364                     if(width < height){
27365                         scale = targetHeight / height;
27366                     }
27367                 }
27368                 
27369                 context.scale(scale, scale);
27370                 
27371                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27372                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27373
27374                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27375                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27376                 
27377                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27378                 
27379                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27380                 
27381                 break;
27382             default : 
27383                 break;
27384         }
27385         
27386         this.cropData = canvas.toDataURL(this.cropType);
27387         
27388         if(this.fireEvent('crop', this, this.cropData) !== false){
27389             this.process(this.file, this.cropData);
27390         }
27391         
27392         return;
27393         
27394     },
27395     
27396     setThumbBoxSize : function()
27397     {
27398         var width, height;
27399         
27400         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27401             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27402             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27403             
27404             this.minWidth = width;
27405             this.minHeight = height;
27406             
27407             if(this.rotate == 90 || this.rotate == 270){
27408                 this.minWidth = height;
27409                 this.minHeight = width;
27410             }
27411         }
27412         
27413         height = 300;
27414         width = Math.ceil(this.minWidth * height / this.minHeight);
27415         
27416         if(this.minWidth > this.minHeight){
27417             width = 300;
27418             height = Math.ceil(this.minHeight * width / this.minWidth);
27419         }
27420         
27421         this.thumbEl.setStyle({
27422             width : width + 'px',
27423             height : height + 'px'
27424         });
27425
27426         return;
27427             
27428     },
27429     
27430     setThumbBoxPosition : function()
27431     {
27432         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27433         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27434         
27435         this.thumbEl.setLeft(x);
27436         this.thumbEl.setTop(y);
27437         
27438     },
27439     
27440     baseRotateLevel : function()
27441     {
27442         this.baseRotate = 1;
27443         
27444         if(
27445                 typeof(this.exif) != 'undefined' &&
27446                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27447                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27448         ){
27449             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27450         }
27451         
27452         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27453         
27454     },
27455     
27456     baseScaleLevel : function()
27457     {
27458         var width, height;
27459         
27460         if(this.isDocument){
27461             
27462             if(this.baseRotate == 6 || this.baseRotate == 8){
27463             
27464                 height = this.thumbEl.getHeight();
27465                 this.baseScale = height / this.imageEl.OriginWidth;
27466
27467                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27468                     width = this.thumbEl.getWidth();
27469                     this.baseScale = width / this.imageEl.OriginHeight;
27470                 }
27471
27472                 return;
27473             }
27474
27475             height = this.thumbEl.getHeight();
27476             this.baseScale = height / this.imageEl.OriginHeight;
27477
27478             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27479                 width = this.thumbEl.getWidth();
27480                 this.baseScale = width / this.imageEl.OriginWidth;
27481             }
27482
27483             return;
27484         }
27485         
27486         if(this.baseRotate == 6 || this.baseRotate == 8){
27487             
27488             width = this.thumbEl.getHeight();
27489             this.baseScale = width / this.imageEl.OriginHeight;
27490             
27491             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27492                 height = this.thumbEl.getWidth();
27493                 this.baseScale = height / this.imageEl.OriginHeight;
27494             }
27495             
27496             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27497                 height = this.thumbEl.getWidth();
27498                 this.baseScale = height / this.imageEl.OriginHeight;
27499                 
27500                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27501                     width = this.thumbEl.getHeight();
27502                     this.baseScale = width / this.imageEl.OriginWidth;
27503                 }
27504             }
27505             
27506             return;
27507         }
27508         
27509         width = this.thumbEl.getWidth();
27510         this.baseScale = width / this.imageEl.OriginWidth;
27511         
27512         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27513             height = this.thumbEl.getHeight();
27514             this.baseScale = height / this.imageEl.OriginHeight;
27515         }
27516         
27517         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27518             
27519             height = this.thumbEl.getHeight();
27520             this.baseScale = height / this.imageEl.OriginHeight;
27521             
27522             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27523                 width = this.thumbEl.getWidth();
27524                 this.baseScale = width / this.imageEl.OriginWidth;
27525             }
27526             
27527         }
27528         
27529         return;
27530     },
27531     
27532     getScaleLevel : function()
27533     {
27534         return this.baseScale * Math.pow(1.1, this.scale);
27535     },
27536     
27537     onTouchStart : function(e)
27538     {
27539         if(!this.canvasLoaded){
27540             this.beforeSelectFile(e);
27541             return;
27542         }
27543         
27544         var touches = e.browserEvent.touches;
27545         
27546         if(!touches){
27547             return;
27548         }
27549         
27550         if(touches.length == 1){
27551             this.onMouseDown(e);
27552             return;
27553         }
27554         
27555         if(touches.length != 2){
27556             return;
27557         }
27558         
27559         var coords = [];
27560         
27561         for(var i = 0, finger; finger = touches[i]; i++){
27562             coords.push(finger.pageX, finger.pageY);
27563         }
27564         
27565         var x = Math.pow(coords[0] - coords[2], 2);
27566         var y = Math.pow(coords[1] - coords[3], 2);
27567         
27568         this.startDistance = Math.sqrt(x + y);
27569         
27570         this.startScale = this.scale;
27571         
27572         this.pinching = true;
27573         this.dragable = false;
27574         
27575     },
27576     
27577     onTouchMove : function(e)
27578     {
27579         if(!this.pinching && !this.dragable){
27580             return;
27581         }
27582         
27583         var touches = e.browserEvent.touches;
27584         
27585         if(!touches){
27586             return;
27587         }
27588         
27589         if(this.dragable){
27590             this.onMouseMove(e);
27591             return;
27592         }
27593         
27594         var coords = [];
27595         
27596         for(var i = 0, finger; finger = touches[i]; i++){
27597             coords.push(finger.pageX, finger.pageY);
27598         }
27599         
27600         var x = Math.pow(coords[0] - coords[2], 2);
27601         var y = Math.pow(coords[1] - coords[3], 2);
27602         
27603         this.endDistance = Math.sqrt(x + y);
27604         
27605         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27606         
27607         if(!this.zoomable()){
27608             this.scale = this.startScale;
27609             return;
27610         }
27611         
27612         this.draw();
27613         
27614     },
27615     
27616     onTouchEnd : function(e)
27617     {
27618         this.pinching = false;
27619         this.dragable = false;
27620         
27621     },
27622     
27623     process : function(file, crop)
27624     {
27625         if(this.loadMask){
27626             this.maskEl.mask(this.loadingText);
27627         }
27628         
27629         this.xhr = new XMLHttpRequest();
27630         
27631         file.xhr = this.xhr;
27632
27633         this.xhr.open(this.method, this.url, true);
27634         
27635         var headers = {
27636             "Accept": "application/json",
27637             "Cache-Control": "no-cache",
27638             "X-Requested-With": "XMLHttpRequest"
27639         };
27640         
27641         for (var headerName in headers) {
27642             var headerValue = headers[headerName];
27643             if (headerValue) {
27644                 this.xhr.setRequestHeader(headerName, headerValue);
27645             }
27646         }
27647         
27648         var _this = this;
27649         
27650         this.xhr.onload = function()
27651         {
27652             _this.xhrOnLoad(_this.xhr);
27653         }
27654         
27655         this.xhr.onerror = function()
27656         {
27657             _this.xhrOnError(_this.xhr);
27658         }
27659         
27660         var formData = new FormData();
27661
27662         formData.append('returnHTML', 'NO');
27663         
27664         if(crop){
27665             formData.append('crop', crop);
27666         }
27667         
27668         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27669             formData.append(this.paramName, file, file.name);
27670         }
27671         
27672         if(typeof(file.filename) != 'undefined'){
27673             formData.append('filename', file.filename);
27674         }
27675         
27676         if(typeof(file.mimetype) != 'undefined'){
27677             formData.append('mimetype', file.mimetype);
27678         }
27679         
27680         if(this.fireEvent('arrange', this, formData) != false){
27681             this.xhr.send(formData);
27682         };
27683     },
27684     
27685     xhrOnLoad : function(xhr)
27686     {
27687         if(this.loadMask){
27688             this.maskEl.unmask();
27689         }
27690         
27691         if (xhr.readyState !== 4) {
27692             this.fireEvent('exception', this, xhr);
27693             return;
27694         }
27695
27696         var response = Roo.decode(xhr.responseText);
27697         
27698         if(!response.success){
27699             this.fireEvent('exception', this, xhr);
27700             return;
27701         }
27702         
27703         var response = Roo.decode(xhr.responseText);
27704         
27705         this.fireEvent('upload', this, response);
27706         
27707     },
27708     
27709     xhrOnError : function()
27710     {
27711         if(this.loadMask){
27712             this.maskEl.unmask();
27713         }
27714         
27715         Roo.log('xhr on error');
27716         
27717         var response = Roo.decode(xhr.responseText);
27718           
27719         Roo.log(response);
27720         
27721     },
27722     
27723     prepare : function(file)
27724     {   
27725         if(this.loadMask){
27726             this.maskEl.mask(this.loadingText);
27727         }
27728         
27729         this.file = false;
27730         this.exif = {};
27731         
27732         if(typeof(file) === 'string'){
27733             this.loadCanvas(file);
27734             return;
27735         }
27736         
27737         if(!file || !this.urlAPI){
27738             return;
27739         }
27740         
27741         this.file = file;
27742         this.cropType = file.type;
27743         
27744         var _this = this;
27745         
27746         if(this.fireEvent('prepare', this, this.file) != false){
27747             
27748             var reader = new FileReader();
27749             
27750             reader.onload = function (e) {
27751                 if (e.target.error) {
27752                     Roo.log(e.target.error);
27753                     return;
27754                 }
27755                 
27756                 var buffer = e.target.result,
27757                     dataView = new DataView(buffer),
27758                     offset = 2,
27759                     maxOffset = dataView.byteLength - 4,
27760                     markerBytes,
27761                     markerLength;
27762                 
27763                 if (dataView.getUint16(0) === 0xffd8) {
27764                     while (offset < maxOffset) {
27765                         markerBytes = dataView.getUint16(offset);
27766                         
27767                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27768                             markerLength = dataView.getUint16(offset + 2) + 2;
27769                             if (offset + markerLength > dataView.byteLength) {
27770                                 Roo.log('Invalid meta data: Invalid segment size.');
27771                                 break;
27772                             }
27773                             
27774                             if(markerBytes == 0xffe1){
27775                                 _this.parseExifData(
27776                                     dataView,
27777                                     offset,
27778                                     markerLength
27779                                 );
27780                             }
27781                             
27782                             offset += markerLength;
27783                             
27784                             continue;
27785                         }
27786                         
27787                         break;
27788                     }
27789                     
27790                 }
27791                 
27792                 var url = _this.urlAPI.createObjectURL(_this.file);
27793                 
27794                 _this.loadCanvas(url);
27795                 
27796                 return;
27797             }
27798             
27799             reader.readAsArrayBuffer(this.file);
27800             
27801         }
27802         
27803     },
27804     
27805     parseExifData : function(dataView, offset, length)
27806     {
27807         var tiffOffset = offset + 10,
27808             littleEndian,
27809             dirOffset;
27810     
27811         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27812             // No Exif data, might be XMP data instead
27813             return;
27814         }
27815         
27816         // Check for the ASCII code for "Exif" (0x45786966):
27817         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27818             // No Exif data, might be XMP data instead
27819             return;
27820         }
27821         if (tiffOffset + 8 > dataView.byteLength) {
27822             Roo.log('Invalid Exif data: Invalid segment size.');
27823             return;
27824         }
27825         // Check for the two null bytes:
27826         if (dataView.getUint16(offset + 8) !== 0x0000) {
27827             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27828             return;
27829         }
27830         // Check the byte alignment:
27831         switch (dataView.getUint16(tiffOffset)) {
27832         case 0x4949:
27833             littleEndian = true;
27834             break;
27835         case 0x4D4D:
27836             littleEndian = false;
27837             break;
27838         default:
27839             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27840             return;
27841         }
27842         // Check for the TIFF tag marker (0x002A):
27843         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27844             Roo.log('Invalid Exif data: Missing TIFF marker.');
27845             return;
27846         }
27847         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27848         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27849         
27850         this.parseExifTags(
27851             dataView,
27852             tiffOffset,
27853             tiffOffset + dirOffset,
27854             littleEndian
27855         );
27856     },
27857     
27858     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27859     {
27860         var tagsNumber,
27861             dirEndOffset,
27862             i;
27863         if (dirOffset + 6 > dataView.byteLength) {
27864             Roo.log('Invalid Exif data: Invalid directory offset.');
27865             return;
27866         }
27867         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27868         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27869         if (dirEndOffset + 4 > dataView.byteLength) {
27870             Roo.log('Invalid Exif data: Invalid directory size.');
27871             return;
27872         }
27873         for (i = 0; i < tagsNumber; i += 1) {
27874             this.parseExifTag(
27875                 dataView,
27876                 tiffOffset,
27877                 dirOffset + 2 + 12 * i, // tag offset
27878                 littleEndian
27879             );
27880         }
27881         // Return the offset to the next directory:
27882         return dataView.getUint32(dirEndOffset, littleEndian);
27883     },
27884     
27885     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27886     {
27887         var tag = dataView.getUint16(offset, littleEndian);
27888         
27889         this.exif[tag] = this.getExifValue(
27890             dataView,
27891             tiffOffset,
27892             offset,
27893             dataView.getUint16(offset + 2, littleEndian), // tag type
27894             dataView.getUint32(offset + 4, littleEndian), // tag length
27895             littleEndian
27896         );
27897     },
27898     
27899     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27900     {
27901         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27902             tagSize,
27903             dataOffset,
27904             values,
27905             i,
27906             str,
27907             c;
27908     
27909         if (!tagType) {
27910             Roo.log('Invalid Exif data: Invalid tag type.');
27911             return;
27912         }
27913         
27914         tagSize = tagType.size * length;
27915         // Determine if the value is contained in the dataOffset bytes,
27916         // or if the value at the dataOffset is a pointer to the actual data:
27917         dataOffset = tagSize > 4 ?
27918                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27919         if (dataOffset + tagSize > dataView.byteLength) {
27920             Roo.log('Invalid Exif data: Invalid data offset.');
27921             return;
27922         }
27923         if (length === 1) {
27924             return tagType.getValue(dataView, dataOffset, littleEndian);
27925         }
27926         values = [];
27927         for (i = 0; i < length; i += 1) {
27928             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27929         }
27930         
27931         if (tagType.ascii) {
27932             str = '';
27933             // Concatenate the chars:
27934             for (i = 0; i < values.length; i += 1) {
27935                 c = values[i];
27936                 // Ignore the terminating NULL byte(s):
27937                 if (c === '\u0000') {
27938                     break;
27939                 }
27940                 str += c;
27941             }
27942             return str;
27943         }
27944         return values;
27945     }
27946     
27947 });
27948
27949 Roo.apply(Roo.bootstrap.UploadCropbox, {
27950     tags : {
27951         'Orientation': 0x0112
27952     },
27953     
27954     Orientation: {
27955             1: 0, //'top-left',
27956 //            2: 'top-right',
27957             3: 180, //'bottom-right',
27958 //            4: 'bottom-left',
27959 //            5: 'left-top',
27960             6: 90, //'right-top',
27961 //            7: 'right-bottom',
27962             8: 270 //'left-bottom'
27963     },
27964     
27965     exifTagTypes : {
27966         // byte, 8-bit unsigned int:
27967         1: {
27968             getValue: function (dataView, dataOffset) {
27969                 return dataView.getUint8(dataOffset);
27970             },
27971             size: 1
27972         },
27973         // ascii, 8-bit byte:
27974         2: {
27975             getValue: function (dataView, dataOffset) {
27976                 return String.fromCharCode(dataView.getUint8(dataOffset));
27977             },
27978             size: 1,
27979             ascii: true
27980         },
27981         // short, 16 bit int:
27982         3: {
27983             getValue: function (dataView, dataOffset, littleEndian) {
27984                 return dataView.getUint16(dataOffset, littleEndian);
27985             },
27986             size: 2
27987         },
27988         // long, 32 bit int:
27989         4: {
27990             getValue: function (dataView, dataOffset, littleEndian) {
27991                 return dataView.getUint32(dataOffset, littleEndian);
27992             },
27993             size: 4
27994         },
27995         // rational = two long values, first is numerator, second is denominator:
27996         5: {
27997             getValue: function (dataView, dataOffset, littleEndian) {
27998                 return dataView.getUint32(dataOffset, littleEndian) /
27999                     dataView.getUint32(dataOffset + 4, littleEndian);
28000             },
28001             size: 8
28002         },
28003         // slong, 32 bit signed int:
28004         9: {
28005             getValue: function (dataView, dataOffset, littleEndian) {
28006                 return dataView.getInt32(dataOffset, littleEndian);
28007             },
28008             size: 4
28009         },
28010         // srational, two slongs, first is numerator, second is denominator:
28011         10: {
28012             getValue: function (dataView, dataOffset, littleEndian) {
28013                 return dataView.getInt32(dataOffset, littleEndian) /
28014                     dataView.getInt32(dataOffset + 4, littleEndian);
28015             },
28016             size: 8
28017         }
28018     },
28019     
28020     footer : {
28021         STANDARD : [
28022             {
28023                 tag : 'div',
28024                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28025                 action : 'rotate-left',
28026                 cn : [
28027                     {
28028                         tag : 'button',
28029                         cls : 'btn btn-default',
28030                         html : '<i class="fa fa-undo"></i>'
28031                     }
28032                 ]
28033             },
28034             {
28035                 tag : 'div',
28036                 cls : 'btn-group roo-upload-cropbox-picture',
28037                 action : 'picture',
28038                 cn : [
28039                     {
28040                         tag : 'button',
28041                         cls : 'btn btn-default',
28042                         html : '<i class="fa fa-picture-o"></i>'
28043                     }
28044                 ]
28045             },
28046             {
28047                 tag : 'div',
28048                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28049                 action : 'rotate-right',
28050                 cn : [
28051                     {
28052                         tag : 'button',
28053                         cls : 'btn btn-default',
28054                         html : '<i class="fa fa-repeat"></i>'
28055                     }
28056                 ]
28057             }
28058         ],
28059         DOCUMENT : [
28060             {
28061                 tag : 'div',
28062                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28063                 action : 'rotate-left',
28064                 cn : [
28065                     {
28066                         tag : 'button',
28067                         cls : 'btn btn-default',
28068                         html : '<i class="fa fa-undo"></i>'
28069                     }
28070                 ]
28071             },
28072             {
28073                 tag : 'div',
28074                 cls : 'btn-group roo-upload-cropbox-download',
28075                 action : 'download',
28076                 cn : [
28077                     {
28078                         tag : 'button',
28079                         cls : 'btn btn-default',
28080                         html : '<i class="fa fa-download"></i>'
28081                     }
28082                 ]
28083             },
28084             {
28085                 tag : 'div',
28086                 cls : 'btn-group roo-upload-cropbox-crop',
28087                 action : 'crop',
28088                 cn : [
28089                     {
28090                         tag : 'button',
28091                         cls : 'btn btn-default',
28092                         html : '<i class="fa fa-crop"></i>'
28093                     }
28094                 ]
28095             },
28096             {
28097                 tag : 'div',
28098                 cls : 'btn-group roo-upload-cropbox-trash',
28099                 action : 'trash',
28100                 cn : [
28101                     {
28102                         tag : 'button',
28103                         cls : 'btn btn-default',
28104                         html : '<i class="fa fa-trash"></i>'
28105                     }
28106                 ]
28107             },
28108             {
28109                 tag : 'div',
28110                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28111                 action : 'rotate-right',
28112                 cn : [
28113                     {
28114                         tag : 'button',
28115                         cls : 'btn btn-default',
28116                         html : '<i class="fa fa-repeat"></i>'
28117                     }
28118                 ]
28119             }
28120         ],
28121         ROTATOR : [
28122             {
28123                 tag : 'div',
28124                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28125                 action : 'rotate-left',
28126                 cn : [
28127                     {
28128                         tag : 'button',
28129                         cls : 'btn btn-default',
28130                         html : '<i class="fa fa-undo"></i>'
28131                     }
28132                 ]
28133             },
28134             {
28135                 tag : 'div',
28136                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28137                 action : 'rotate-right',
28138                 cn : [
28139                     {
28140                         tag : 'button',
28141                         cls : 'btn btn-default',
28142                         html : '<i class="fa fa-repeat"></i>'
28143                     }
28144                 ]
28145             }
28146         ]
28147     }
28148 });
28149
28150 /*
28151 * Licence: LGPL
28152 */
28153
28154 /**
28155  * @class Roo.bootstrap.DocumentManager
28156  * @extends Roo.bootstrap.Component
28157  * Bootstrap DocumentManager class
28158  * @cfg {String} paramName default 'imageUpload'
28159  * @cfg {String} toolTipName default 'filename'
28160  * @cfg {String} method default POST
28161  * @cfg {String} url action url
28162  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28163  * @cfg {Boolean} multiple multiple upload default true
28164  * @cfg {Number} thumbSize default 300
28165  * @cfg {String} fieldLabel
28166  * @cfg {Number} labelWidth default 4
28167  * @cfg {String} labelAlign (left|top) default left
28168  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28169 * @cfg {Number} labellg set the width of label (1-12)
28170  * @cfg {Number} labelmd set the width of label (1-12)
28171  * @cfg {Number} labelsm set the width of label (1-12)
28172  * @cfg {Number} labelxs set the width of label (1-12)
28173  * 
28174  * @constructor
28175  * Create a new DocumentManager
28176  * @param {Object} config The config object
28177  */
28178
28179 Roo.bootstrap.DocumentManager = function(config){
28180     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28181     
28182     this.files = [];
28183     this.delegates = [];
28184     
28185     this.addEvents({
28186         /**
28187          * @event initial
28188          * Fire when initial the DocumentManager
28189          * @param {Roo.bootstrap.DocumentManager} this
28190          */
28191         "initial" : true,
28192         /**
28193          * @event inspect
28194          * inspect selected file
28195          * @param {Roo.bootstrap.DocumentManager} this
28196          * @param {File} file
28197          */
28198         "inspect" : true,
28199         /**
28200          * @event exception
28201          * Fire when xhr load exception
28202          * @param {Roo.bootstrap.DocumentManager} this
28203          * @param {XMLHttpRequest} xhr
28204          */
28205         "exception" : true,
28206         /**
28207          * @event afterupload
28208          * Fire when xhr load exception
28209          * @param {Roo.bootstrap.DocumentManager} this
28210          * @param {XMLHttpRequest} xhr
28211          */
28212         "afterupload" : true,
28213         /**
28214          * @event prepare
28215          * prepare the form data
28216          * @param {Roo.bootstrap.DocumentManager} this
28217          * @param {Object} formData
28218          */
28219         "prepare" : true,
28220         /**
28221          * @event remove
28222          * Fire when remove the file
28223          * @param {Roo.bootstrap.DocumentManager} this
28224          * @param {Object} file
28225          */
28226         "remove" : true,
28227         /**
28228          * @event refresh
28229          * Fire after refresh the file
28230          * @param {Roo.bootstrap.DocumentManager} this
28231          */
28232         "refresh" : true,
28233         /**
28234          * @event click
28235          * Fire after click the image
28236          * @param {Roo.bootstrap.DocumentManager} this
28237          * @param {Object} file
28238          */
28239         "click" : true,
28240         /**
28241          * @event edit
28242          * Fire when upload a image and editable set to true
28243          * @param {Roo.bootstrap.DocumentManager} this
28244          * @param {Object} file
28245          */
28246         "edit" : true,
28247         /**
28248          * @event beforeselectfile
28249          * Fire before select file
28250          * @param {Roo.bootstrap.DocumentManager} this
28251          */
28252         "beforeselectfile" : true,
28253         /**
28254          * @event process
28255          * Fire before process file
28256          * @param {Roo.bootstrap.DocumentManager} this
28257          * @param {Object} file
28258          */
28259         "process" : true
28260         
28261     });
28262 };
28263
28264 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28265     
28266     boxes : 0,
28267     inputName : '',
28268     thumbSize : 300,
28269     multiple : true,
28270     files : false,
28271     method : 'POST',
28272     url : '',
28273     paramName : 'imageUpload',
28274     toolTipName : 'filename',
28275     fieldLabel : '',
28276     labelWidth : 4,
28277     labelAlign : 'left',
28278     editable : true,
28279     delegates : false,
28280     xhr : false, 
28281     
28282     labellg : 0,
28283     labelmd : 0,
28284     labelsm : 0,
28285     labelxs : 0,
28286     
28287     getAutoCreate : function()
28288     {   
28289         var managerWidget = {
28290             tag : 'div',
28291             cls : 'roo-document-manager',
28292             cn : [
28293                 {
28294                     tag : 'input',
28295                     cls : 'roo-document-manager-selector',
28296                     type : 'file'
28297                 },
28298                 {
28299                     tag : 'div',
28300                     cls : 'roo-document-manager-uploader',
28301                     cn : [
28302                         {
28303                             tag : 'div',
28304                             cls : 'roo-document-manager-upload-btn',
28305                             html : '<i class="fa fa-plus"></i>'
28306                         }
28307                     ]
28308                     
28309                 }
28310             ]
28311         };
28312         
28313         var content = [
28314             {
28315                 tag : 'div',
28316                 cls : 'column col-md-12',
28317                 cn : managerWidget
28318             }
28319         ];
28320         
28321         if(this.fieldLabel.length){
28322             
28323             content = [
28324                 {
28325                     tag : 'div',
28326                     cls : 'column col-md-12',
28327                     html : this.fieldLabel
28328                 },
28329                 {
28330                     tag : 'div',
28331                     cls : 'column col-md-12',
28332                     cn : managerWidget
28333                 }
28334             ];
28335
28336             if(this.labelAlign == 'left'){
28337                 content = [
28338                     {
28339                         tag : 'div',
28340                         cls : 'column',
28341                         html : this.fieldLabel
28342                     },
28343                     {
28344                         tag : 'div',
28345                         cls : 'column',
28346                         cn : managerWidget
28347                     }
28348                 ];
28349                 
28350                 if(this.labelWidth > 12){
28351                     content[0].style = "width: " + this.labelWidth + 'px';
28352                 }
28353
28354                 if(this.labelWidth < 13 && this.labelmd == 0){
28355                     this.labelmd = this.labelWidth;
28356                 }
28357
28358                 if(this.labellg > 0){
28359                     content[0].cls += ' col-lg-' + this.labellg;
28360                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28361                 }
28362
28363                 if(this.labelmd > 0){
28364                     content[0].cls += ' col-md-' + this.labelmd;
28365                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28366                 }
28367
28368                 if(this.labelsm > 0){
28369                     content[0].cls += ' col-sm-' + this.labelsm;
28370                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28371                 }
28372
28373                 if(this.labelxs > 0){
28374                     content[0].cls += ' col-xs-' + this.labelxs;
28375                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28376                 }
28377                 
28378             }
28379         }
28380         
28381         var cfg = {
28382             tag : 'div',
28383             cls : 'row clearfix',
28384             cn : content
28385         };
28386         
28387         return cfg;
28388         
28389     },
28390     
28391     initEvents : function()
28392     {
28393         this.managerEl = this.el.select('.roo-document-manager', true).first();
28394         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28395         
28396         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28397         this.selectorEl.hide();
28398         
28399         if(this.multiple){
28400             this.selectorEl.attr('multiple', 'multiple');
28401         }
28402         
28403         this.selectorEl.on('change', this.onFileSelected, this);
28404         
28405         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28406         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28407         
28408         this.uploader.on('click', this.onUploaderClick, this);
28409         
28410         this.renderProgressDialog();
28411         
28412         var _this = this;
28413         
28414         window.addEventListener("resize", function() { _this.refresh(); } );
28415         
28416         this.fireEvent('initial', this);
28417     },
28418     
28419     renderProgressDialog : function()
28420     {
28421         var _this = this;
28422         
28423         this.progressDialog = new Roo.bootstrap.Modal({
28424             cls : 'roo-document-manager-progress-dialog',
28425             allow_close : false,
28426             title : '',
28427             buttons : [
28428                 {
28429                     name  :'cancel',
28430                     weight : 'danger',
28431                     html : 'Cancel'
28432                 }
28433             ], 
28434             listeners : { 
28435                 btnclick : function() {
28436                     _this.uploadCancel();
28437                     this.hide();
28438                 }
28439             }
28440         });
28441          
28442         this.progressDialog.render(Roo.get(document.body));
28443          
28444         this.progress = new Roo.bootstrap.Progress({
28445             cls : 'roo-document-manager-progress',
28446             active : true,
28447             striped : true
28448         });
28449         
28450         this.progress.render(this.progressDialog.getChildContainer());
28451         
28452         this.progressBar = new Roo.bootstrap.ProgressBar({
28453             cls : 'roo-document-manager-progress-bar',
28454             aria_valuenow : 0,
28455             aria_valuemin : 0,
28456             aria_valuemax : 12,
28457             panel : 'success'
28458         });
28459         
28460         this.progressBar.render(this.progress.getChildContainer());
28461     },
28462     
28463     onUploaderClick : function(e)
28464     {
28465         e.preventDefault();
28466      
28467         if(this.fireEvent('beforeselectfile', this) != false){
28468             this.selectorEl.dom.click();
28469         }
28470         
28471     },
28472     
28473     onFileSelected : function(e)
28474     {
28475         e.preventDefault();
28476         
28477         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28478             return;
28479         }
28480         
28481         Roo.each(this.selectorEl.dom.files, function(file){
28482             if(this.fireEvent('inspect', this, file) != false){
28483                 this.files.push(file);
28484             }
28485         }, this);
28486         
28487         this.queue();
28488         
28489     },
28490     
28491     queue : function()
28492     {
28493         this.selectorEl.dom.value = '';
28494         
28495         if(!this.files.length){
28496             return;
28497         }
28498         
28499         if(this.boxes > 0 && this.files.length > this.boxes){
28500             this.files = this.files.slice(0, this.boxes);
28501         }
28502         
28503         this.uploader.show();
28504         
28505         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28506             this.uploader.hide();
28507         }
28508         
28509         var _this = this;
28510         
28511         var files = [];
28512         
28513         var docs = [];
28514         
28515         Roo.each(this.files, function(file){
28516             
28517             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28518                 var f = this.renderPreview(file);
28519                 files.push(f);
28520                 return;
28521             }
28522             
28523             if(file.type.indexOf('image') != -1){
28524                 this.delegates.push(
28525                     (function(){
28526                         _this.process(file);
28527                     }).createDelegate(this)
28528                 );
28529         
28530                 return;
28531             }
28532             
28533             docs.push(
28534                 (function(){
28535                     _this.process(file);
28536                 }).createDelegate(this)
28537             );
28538             
28539         }, this);
28540         
28541         this.files = files;
28542         
28543         this.delegates = this.delegates.concat(docs);
28544         
28545         if(!this.delegates.length){
28546             this.refresh();
28547             return;
28548         }
28549         
28550         this.progressBar.aria_valuemax = this.delegates.length;
28551         
28552         this.arrange();
28553         
28554         return;
28555     },
28556     
28557     arrange : function()
28558     {
28559         if(!this.delegates.length){
28560             this.progressDialog.hide();
28561             this.refresh();
28562             return;
28563         }
28564         
28565         var delegate = this.delegates.shift();
28566         
28567         this.progressDialog.show();
28568         
28569         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28570         
28571         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28572         
28573         delegate();
28574     },
28575     
28576     refresh : function()
28577     {
28578         this.uploader.show();
28579         
28580         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28581             this.uploader.hide();
28582         }
28583         
28584         Roo.isTouch ? this.closable(false) : this.closable(true);
28585         
28586         this.fireEvent('refresh', this);
28587     },
28588     
28589     onRemove : function(e, el, o)
28590     {
28591         e.preventDefault();
28592         
28593         this.fireEvent('remove', this, o);
28594         
28595     },
28596     
28597     remove : function(o)
28598     {
28599         var files = [];
28600         
28601         Roo.each(this.files, function(file){
28602             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28603                 files.push(file);
28604                 return;
28605             }
28606
28607             o.target.remove();
28608
28609         }, this);
28610         
28611         this.files = files;
28612         
28613         this.refresh();
28614     },
28615     
28616     clear : function()
28617     {
28618         Roo.each(this.files, function(file){
28619             if(!file.target){
28620                 return;
28621             }
28622             
28623             file.target.remove();
28624
28625         }, this);
28626         
28627         this.files = [];
28628         
28629         this.refresh();
28630     },
28631     
28632     onClick : function(e, el, o)
28633     {
28634         e.preventDefault();
28635         
28636         this.fireEvent('click', this, o);
28637         
28638     },
28639     
28640     closable : function(closable)
28641     {
28642         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28643             
28644             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28645             
28646             if(closable){
28647                 el.show();
28648                 return;
28649             }
28650             
28651             el.hide();
28652             
28653         }, this);
28654     },
28655     
28656     xhrOnLoad : function(xhr)
28657     {
28658         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28659             el.remove();
28660         }, this);
28661         
28662         if (xhr.readyState !== 4) {
28663             this.arrange();
28664             this.fireEvent('exception', this, xhr);
28665             return;
28666         }
28667
28668         var response = Roo.decode(xhr.responseText);
28669         
28670         if(!response.success){
28671             this.arrange();
28672             this.fireEvent('exception', this, xhr);
28673             return;
28674         }
28675         
28676         var file = this.renderPreview(response.data);
28677         
28678         this.files.push(file);
28679         
28680         this.arrange();
28681         
28682         this.fireEvent('afterupload', this, xhr);
28683         
28684     },
28685     
28686     xhrOnError : function(xhr)
28687     {
28688         Roo.log('xhr on error');
28689         
28690         var response = Roo.decode(xhr.responseText);
28691           
28692         Roo.log(response);
28693         
28694         this.arrange();
28695     },
28696     
28697     process : function(file)
28698     {
28699         if(this.fireEvent('process', this, file) !== false){
28700             if(this.editable && file.type.indexOf('image') != -1){
28701                 this.fireEvent('edit', this, file);
28702                 return;
28703             }
28704
28705             this.uploadStart(file, false);
28706
28707             return;
28708         }
28709         
28710     },
28711     
28712     uploadStart : function(file, crop)
28713     {
28714         this.xhr = new XMLHttpRequest();
28715         
28716         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28717             this.arrange();
28718             return;
28719         }
28720         
28721         file.xhr = this.xhr;
28722             
28723         this.managerEl.createChild({
28724             tag : 'div',
28725             cls : 'roo-document-manager-loading',
28726             cn : [
28727                 {
28728                     tag : 'div',
28729                     tooltip : file.name,
28730                     cls : 'roo-document-manager-thumb',
28731                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28732                 }
28733             ]
28734
28735         });
28736
28737         this.xhr.open(this.method, this.url, true);
28738         
28739         var headers = {
28740             "Accept": "application/json",
28741             "Cache-Control": "no-cache",
28742             "X-Requested-With": "XMLHttpRequest"
28743         };
28744         
28745         for (var headerName in headers) {
28746             var headerValue = headers[headerName];
28747             if (headerValue) {
28748                 this.xhr.setRequestHeader(headerName, headerValue);
28749             }
28750         }
28751         
28752         var _this = this;
28753         
28754         this.xhr.onload = function()
28755         {
28756             _this.xhrOnLoad(_this.xhr);
28757         }
28758         
28759         this.xhr.onerror = function()
28760         {
28761             _this.xhrOnError(_this.xhr);
28762         }
28763         
28764         var formData = new FormData();
28765
28766         formData.append('returnHTML', 'NO');
28767         
28768         if(crop){
28769             formData.append('crop', crop);
28770         }
28771         
28772         formData.append(this.paramName, file, file.name);
28773         
28774         var options = {
28775             file : file, 
28776             manually : false
28777         };
28778         
28779         if(this.fireEvent('prepare', this, formData, options) != false){
28780             
28781             if(options.manually){
28782                 return;
28783             }
28784             
28785             this.xhr.send(formData);
28786             return;
28787         };
28788         
28789         this.uploadCancel();
28790     },
28791     
28792     uploadCancel : function()
28793     {
28794         if (this.xhr) {
28795             this.xhr.abort();
28796         }
28797         
28798         this.delegates = [];
28799         
28800         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28801             el.remove();
28802         }, this);
28803         
28804         this.arrange();
28805     },
28806     
28807     renderPreview : function(file)
28808     {
28809         if(typeof(file.target) != 'undefined' && file.target){
28810             return file;
28811         }
28812         
28813         var previewEl = this.managerEl.createChild({
28814             tag : 'div',
28815             cls : 'roo-document-manager-preview',
28816             cn : [
28817                 {
28818                     tag : 'div',
28819                     tooltip : file[this.toolTipName],
28820                     cls : 'roo-document-manager-thumb',
28821                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28822                 },
28823                 {
28824                     tag : 'button',
28825                     cls : 'close',
28826                     html : '<i class="fa fa-times-circle"></i>'
28827                 }
28828             ]
28829         });
28830
28831         var close = previewEl.select('button.close', true).first();
28832
28833         close.on('click', this.onRemove, this, file);
28834
28835         file.target = previewEl;
28836
28837         var image = previewEl.select('img', true).first();
28838         
28839         var _this = this;
28840         
28841         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28842         
28843         image.on('click', this.onClick, this, file);
28844         
28845         return file;
28846         
28847     },
28848     
28849     onPreviewLoad : function(file, image)
28850     {
28851         if(typeof(file.target) == 'undefined' || !file.target){
28852             return;
28853         }
28854         
28855         var width = image.dom.naturalWidth || image.dom.width;
28856         var height = image.dom.naturalHeight || image.dom.height;
28857         
28858         if(width > height){
28859             file.target.addClass('wide');
28860             return;
28861         }
28862         
28863         file.target.addClass('tall');
28864         return;
28865         
28866     },
28867     
28868     uploadFromSource : function(file, crop)
28869     {
28870         this.xhr = new XMLHttpRequest();
28871         
28872         this.managerEl.createChild({
28873             tag : 'div',
28874             cls : 'roo-document-manager-loading',
28875             cn : [
28876                 {
28877                     tag : 'div',
28878                     tooltip : file.name,
28879                     cls : 'roo-document-manager-thumb',
28880                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28881                 }
28882             ]
28883
28884         });
28885
28886         this.xhr.open(this.method, this.url, true);
28887         
28888         var headers = {
28889             "Accept": "application/json",
28890             "Cache-Control": "no-cache",
28891             "X-Requested-With": "XMLHttpRequest"
28892         };
28893         
28894         for (var headerName in headers) {
28895             var headerValue = headers[headerName];
28896             if (headerValue) {
28897                 this.xhr.setRequestHeader(headerName, headerValue);
28898             }
28899         }
28900         
28901         var _this = this;
28902         
28903         this.xhr.onload = function()
28904         {
28905             _this.xhrOnLoad(_this.xhr);
28906         }
28907         
28908         this.xhr.onerror = function()
28909         {
28910             _this.xhrOnError(_this.xhr);
28911         }
28912         
28913         var formData = new FormData();
28914
28915         formData.append('returnHTML', 'NO');
28916         
28917         formData.append('crop', crop);
28918         
28919         if(typeof(file.filename) != 'undefined'){
28920             formData.append('filename', file.filename);
28921         }
28922         
28923         if(typeof(file.mimetype) != 'undefined'){
28924             formData.append('mimetype', file.mimetype);
28925         }
28926         
28927         Roo.log(formData);
28928         
28929         if(this.fireEvent('prepare', this, formData) != false){
28930             this.xhr.send(formData);
28931         };
28932     }
28933 });
28934
28935 /*
28936 * Licence: LGPL
28937 */
28938
28939 /**
28940  * @class Roo.bootstrap.DocumentViewer
28941  * @extends Roo.bootstrap.Component
28942  * Bootstrap DocumentViewer class
28943  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28944  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28945  * 
28946  * @constructor
28947  * Create a new DocumentViewer
28948  * @param {Object} config The config object
28949  */
28950
28951 Roo.bootstrap.DocumentViewer = function(config){
28952     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28953     
28954     this.addEvents({
28955         /**
28956          * @event initial
28957          * Fire after initEvent
28958          * @param {Roo.bootstrap.DocumentViewer} this
28959          */
28960         "initial" : true,
28961         /**
28962          * @event click
28963          * Fire after click
28964          * @param {Roo.bootstrap.DocumentViewer} this
28965          */
28966         "click" : true,
28967         /**
28968          * @event download
28969          * Fire after download button
28970          * @param {Roo.bootstrap.DocumentViewer} this
28971          */
28972         "download" : true,
28973         /**
28974          * @event trash
28975          * Fire after trash button
28976          * @param {Roo.bootstrap.DocumentViewer} this
28977          */
28978         "trash" : true
28979         
28980     });
28981 };
28982
28983 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28984     
28985     showDownload : true,
28986     
28987     showTrash : true,
28988     
28989     getAutoCreate : function()
28990     {
28991         var cfg = {
28992             tag : 'div',
28993             cls : 'roo-document-viewer',
28994             cn : [
28995                 {
28996                     tag : 'div',
28997                     cls : 'roo-document-viewer-body',
28998                     cn : [
28999                         {
29000                             tag : 'div',
29001                             cls : 'roo-document-viewer-thumb',
29002                             cn : [
29003                                 {
29004                                     tag : 'img',
29005                                     cls : 'roo-document-viewer-image'
29006                                 }
29007                             ]
29008                         }
29009                     ]
29010                 },
29011                 {
29012                     tag : 'div',
29013                     cls : 'roo-document-viewer-footer',
29014                     cn : {
29015                         tag : 'div',
29016                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29017                         cn : [
29018                             {
29019                                 tag : 'div',
29020                                 cls : 'btn-group roo-document-viewer-download',
29021                                 cn : [
29022                                     {
29023                                         tag : 'button',
29024                                         cls : 'btn btn-default',
29025                                         html : '<i class="fa fa-download"></i>'
29026                                     }
29027                                 ]
29028                             },
29029                             {
29030                                 tag : 'div',
29031                                 cls : 'btn-group roo-document-viewer-trash',
29032                                 cn : [
29033                                     {
29034                                         tag : 'button',
29035                                         cls : 'btn btn-default',
29036                                         html : '<i class="fa fa-trash"></i>'
29037                                     }
29038                                 ]
29039                             }
29040                         ]
29041                     }
29042                 }
29043             ]
29044         };
29045         
29046         return cfg;
29047     },
29048     
29049     initEvents : function()
29050     {
29051         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29052         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29053         
29054         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29055         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29056         
29057         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29058         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29059         
29060         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29061         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29062         
29063         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29064         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29065         
29066         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29067         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29068         
29069         this.bodyEl.on('click', this.onClick, this);
29070         this.downloadBtn.on('click', this.onDownload, this);
29071         this.trashBtn.on('click', this.onTrash, this);
29072         
29073         this.downloadBtn.hide();
29074         this.trashBtn.hide();
29075         
29076         if(this.showDownload){
29077             this.downloadBtn.show();
29078         }
29079         
29080         if(this.showTrash){
29081             this.trashBtn.show();
29082         }
29083         
29084         if(!this.showDownload && !this.showTrash) {
29085             this.footerEl.hide();
29086         }
29087         
29088     },
29089     
29090     initial : function()
29091     {
29092         this.fireEvent('initial', this);
29093         
29094     },
29095     
29096     onClick : function(e)
29097     {
29098         e.preventDefault();
29099         
29100         this.fireEvent('click', this);
29101     },
29102     
29103     onDownload : function(e)
29104     {
29105         e.preventDefault();
29106         
29107         this.fireEvent('download', this);
29108     },
29109     
29110     onTrash : function(e)
29111     {
29112         e.preventDefault();
29113         
29114         this.fireEvent('trash', this);
29115     }
29116     
29117 });
29118 /*
29119  * - LGPL
29120  *
29121  * nav progress bar
29122  * 
29123  */
29124
29125 /**
29126  * @class Roo.bootstrap.NavProgressBar
29127  * @extends Roo.bootstrap.Component
29128  * Bootstrap NavProgressBar class
29129  * 
29130  * @constructor
29131  * Create a new nav progress bar
29132  * @param {Object} config The config object
29133  */
29134
29135 Roo.bootstrap.NavProgressBar = function(config){
29136     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29137
29138     this.bullets = this.bullets || [];
29139    
29140 //    Roo.bootstrap.NavProgressBar.register(this);
29141      this.addEvents({
29142         /**
29143              * @event changed
29144              * Fires when the active item changes
29145              * @param {Roo.bootstrap.NavProgressBar} this
29146              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29147              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29148          */
29149         'changed': true
29150      });
29151     
29152 };
29153
29154 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29155     
29156     bullets : [],
29157     barItems : [],
29158     
29159     getAutoCreate : function()
29160     {
29161         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29162         
29163         cfg = {
29164             tag : 'div',
29165             cls : 'roo-navigation-bar-group',
29166             cn : [
29167                 {
29168                     tag : 'div',
29169                     cls : 'roo-navigation-top-bar'
29170                 },
29171                 {
29172                     tag : 'div',
29173                     cls : 'roo-navigation-bullets-bar',
29174                     cn : [
29175                         {
29176                             tag : 'ul',
29177                             cls : 'roo-navigation-bar'
29178                         }
29179                     ]
29180                 },
29181                 
29182                 {
29183                     tag : 'div',
29184                     cls : 'roo-navigation-bottom-bar'
29185                 }
29186             ]
29187             
29188         };
29189         
29190         return cfg;
29191         
29192     },
29193     
29194     initEvents: function() 
29195     {
29196         
29197     },
29198     
29199     onRender : function(ct, position) 
29200     {
29201         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29202         
29203         if(this.bullets.length){
29204             Roo.each(this.bullets, function(b){
29205                this.addItem(b);
29206             }, this);
29207         }
29208         
29209         this.format();
29210         
29211     },
29212     
29213     addItem : function(cfg)
29214     {
29215         var item = new Roo.bootstrap.NavProgressItem(cfg);
29216         
29217         item.parentId = this.id;
29218         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29219         
29220         if(cfg.html){
29221             var top = new Roo.bootstrap.Element({
29222                 tag : 'div',
29223                 cls : 'roo-navigation-bar-text'
29224             });
29225             
29226             var bottom = new Roo.bootstrap.Element({
29227                 tag : 'div',
29228                 cls : 'roo-navigation-bar-text'
29229             });
29230             
29231             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29232             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29233             
29234             var topText = new Roo.bootstrap.Element({
29235                 tag : 'span',
29236                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29237             });
29238             
29239             var bottomText = new Roo.bootstrap.Element({
29240                 tag : 'span',
29241                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29242             });
29243             
29244             topText.onRender(top.el, null);
29245             bottomText.onRender(bottom.el, null);
29246             
29247             item.topEl = top;
29248             item.bottomEl = bottom;
29249         }
29250         
29251         this.barItems.push(item);
29252         
29253         return item;
29254     },
29255     
29256     getActive : function()
29257     {
29258         var active = false;
29259         
29260         Roo.each(this.barItems, function(v){
29261             
29262             if (!v.isActive()) {
29263                 return;
29264             }
29265             
29266             active = v;
29267             return false;
29268             
29269         });
29270         
29271         return active;
29272     },
29273     
29274     setActiveItem : function(item)
29275     {
29276         var prev = false;
29277         
29278         Roo.each(this.barItems, function(v){
29279             if (v.rid == item.rid) {
29280                 return ;
29281             }
29282             
29283             if (v.isActive()) {
29284                 v.setActive(false);
29285                 prev = v;
29286             }
29287         });
29288
29289         item.setActive(true);
29290         
29291         this.fireEvent('changed', this, item, prev);
29292     },
29293     
29294     getBarItem: function(rid)
29295     {
29296         var ret = false;
29297         
29298         Roo.each(this.barItems, function(e) {
29299             if (e.rid != rid) {
29300                 return;
29301             }
29302             
29303             ret =  e;
29304             return false;
29305         });
29306         
29307         return ret;
29308     },
29309     
29310     indexOfItem : function(item)
29311     {
29312         var index = false;
29313         
29314         Roo.each(this.barItems, function(v, i){
29315             
29316             if (v.rid != item.rid) {
29317                 return;
29318             }
29319             
29320             index = i;
29321             return false
29322         });
29323         
29324         return index;
29325     },
29326     
29327     setActiveNext : function()
29328     {
29329         var i = this.indexOfItem(this.getActive());
29330         
29331         if (i > this.barItems.length) {
29332             return;
29333         }
29334         
29335         this.setActiveItem(this.barItems[i+1]);
29336     },
29337     
29338     setActivePrev : function()
29339     {
29340         var i = this.indexOfItem(this.getActive());
29341         
29342         if (i  < 1) {
29343             return;
29344         }
29345         
29346         this.setActiveItem(this.barItems[i-1]);
29347     },
29348     
29349     format : function()
29350     {
29351         if(!this.barItems.length){
29352             return;
29353         }
29354      
29355         var width = 100 / this.barItems.length;
29356         
29357         Roo.each(this.barItems, function(i){
29358             i.el.setStyle('width', width + '%');
29359             i.topEl.el.setStyle('width', width + '%');
29360             i.bottomEl.el.setStyle('width', width + '%');
29361         }, this);
29362         
29363     }
29364     
29365 });
29366 /*
29367  * - LGPL
29368  *
29369  * Nav Progress Item
29370  * 
29371  */
29372
29373 /**
29374  * @class Roo.bootstrap.NavProgressItem
29375  * @extends Roo.bootstrap.Component
29376  * Bootstrap NavProgressItem class
29377  * @cfg {String} rid the reference id
29378  * @cfg {Boolean} active (true|false) Is item active default false
29379  * @cfg {Boolean} disabled (true|false) Is item active default false
29380  * @cfg {String} html
29381  * @cfg {String} position (top|bottom) text position default bottom
29382  * @cfg {String} icon show icon instead of number
29383  * 
29384  * @constructor
29385  * Create a new NavProgressItem
29386  * @param {Object} config The config object
29387  */
29388 Roo.bootstrap.NavProgressItem = function(config){
29389     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29390     this.addEvents({
29391         // raw events
29392         /**
29393          * @event click
29394          * The raw click event for the entire grid.
29395          * @param {Roo.bootstrap.NavProgressItem} this
29396          * @param {Roo.EventObject} e
29397          */
29398         "click" : true
29399     });
29400    
29401 };
29402
29403 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29404     
29405     rid : '',
29406     active : false,
29407     disabled : false,
29408     html : '',
29409     position : 'bottom',
29410     icon : false,
29411     
29412     getAutoCreate : function()
29413     {
29414         var iconCls = 'roo-navigation-bar-item-icon';
29415         
29416         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29417         
29418         var cfg = {
29419             tag: 'li',
29420             cls: 'roo-navigation-bar-item',
29421             cn : [
29422                 {
29423                     tag : 'i',
29424                     cls : iconCls
29425                 }
29426             ]
29427         };
29428         
29429         if(this.active){
29430             cfg.cls += ' active';
29431         }
29432         if(this.disabled){
29433             cfg.cls += ' disabled';
29434         }
29435         
29436         return cfg;
29437     },
29438     
29439     disable : function()
29440     {
29441         this.setDisabled(true);
29442     },
29443     
29444     enable : function()
29445     {
29446         this.setDisabled(false);
29447     },
29448     
29449     initEvents: function() 
29450     {
29451         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29452         
29453         this.iconEl.on('click', this.onClick, this);
29454     },
29455     
29456     onClick : function(e)
29457     {
29458         e.preventDefault();
29459         
29460         if(this.disabled){
29461             return;
29462         }
29463         
29464         if(this.fireEvent('click', this, e) === false){
29465             return;
29466         };
29467         
29468         this.parent().setActiveItem(this);
29469     },
29470     
29471     isActive: function () 
29472     {
29473         return this.active;
29474     },
29475     
29476     setActive : function(state)
29477     {
29478         if(this.active == state){
29479             return;
29480         }
29481         
29482         this.active = state;
29483         
29484         if (state) {
29485             this.el.addClass('active');
29486             return;
29487         }
29488         
29489         this.el.removeClass('active');
29490         
29491         return;
29492     },
29493     
29494     setDisabled : function(state)
29495     {
29496         if(this.disabled == state){
29497             return;
29498         }
29499         
29500         this.disabled = state;
29501         
29502         if (state) {
29503             this.el.addClass('disabled');
29504             return;
29505         }
29506         
29507         this.el.removeClass('disabled');
29508     },
29509     
29510     tooltipEl : function()
29511     {
29512         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29513     }
29514 });
29515  
29516
29517  /*
29518  * - LGPL
29519  *
29520  * FieldLabel
29521  * 
29522  */
29523
29524 /**
29525  * @class Roo.bootstrap.FieldLabel
29526  * @extends Roo.bootstrap.Component
29527  * Bootstrap FieldLabel class
29528  * @cfg {String} html contents of the element
29529  * @cfg {String} tag tag of the element default label
29530  * @cfg {String} cls class of the element
29531  * @cfg {String} target label target 
29532  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29533  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29534  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29535  * @cfg {String} iconTooltip default "This field is required"
29536  * 
29537  * @constructor
29538  * Create a new FieldLabel
29539  * @param {Object} config The config object
29540  */
29541
29542 Roo.bootstrap.FieldLabel = function(config){
29543     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29544     
29545     this.addEvents({
29546             /**
29547              * @event invalid
29548              * Fires after the field has been marked as invalid.
29549              * @param {Roo.form.FieldLabel} this
29550              * @param {String} msg The validation message
29551              */
29552             invalid : true,
29553             /**
29554              * @event valid
29555              * Fires after the field has been validated with no errors.
29556              * @param {Roo.form.FieldLabel} this
29557              */
29558             valid : true
29559         });
29560 };
29561
29562 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29563     
29564     tag: 'label',
29565     cls: '',
29566     html: '',
29567     target: '',
29568     allowBlank : true,
29569     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29570     validClass : 'text-success fa fa-lg fa-check',
29571     iconTooltip : 'This field is required',
29572     
29573     getAutoCreate : function(){
29574         
29575         var cfg = {
29576             tag : this.tag,
29577             cls : 'roo-bootstrap-field-label ' + this.cls,
29578             for : this.target,
29579             cn : [
29580                 {
29581                     tag : 'i',
29582                     cls : '',
29583                     tooltip : this.iconTooltip
29584                 },
29585                 {
29586                     tag : 'span',
29587                     html : this.html
29588                 }
29589             ] 
29590         };
29591         
29592         return cfg;
29593     },
29594     
29595     initEvents: function() 
29596     {
29597         Roo.bootstrap.Element.superclass.initEvents.call(this);
29598         
29599         this.iconEl = this.el.select('i', true).first();
29600         
29601         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29602         
29603         Roo.bootstrap.FieldLabel.register(this);
29604     },
29605     
29606     /**
29607      * Mark this field as valid
29608      */
29609     markValid : function()
29610     {
29611         this.iconEl.show();
29612         
29613         this.iconEl.removeClass(this.invalidClass);
29614         
29615         this.iconEl.addClass(this.validClass);
29616         
29617         this.fireEvent('valid', this);
29618     },
29619     
29620     /**
29621      * Mark this field as invalid
29622      * @param {String} msg The validation message
29623      */
29624     markInvalid : function(msg)
29625     {
29626         this.iconEl.show();
29627         
29628         this.iconEl.removeClass(this.validClass);
29629         
29630         this.iconEl.addClass(this.invalidClass);
29631         
29632         this.fireEvent('invalid', this, msg);
29633     }
29634     
29635    
29636 });
29637
29638 Roo.apply(Roo.bootstrap.FieldLabel, {
29639     
29640     groups: {},
29641     
29642      /**
29643     * register a FieldLabel Group
29644     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29645     */
29646     register : function(label)
29647     {
29648         if(this.groups.hasOwnProperty(label.target)){
29649             return;
29650         }
29651      
29652         this.groups[label.target] = label;
29653         
29654     },
29655     /**
29656     * fetch a FieldLabel Group based on the target
29657     * @param {string} target
29658     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29659     */
29660     get: function(target) {
29661         if (typeof(this.groups[target]) == 'undefined') {
29662             return false;
29663         }
29664         
29665         return this.groups[target] ;
29666     }
29667 });
29668
29669  
29670
29671  /*
29672  * - LGPL
29673  *
29674  * page DateSplitField.
29675  * 
29676  */
29677
29678
29679 /**
29680  * @class Roo.bootstrap.DateSplitField
29681  * @extends Roo.bootstrap.Component
29682  * Bootstrap DateSplitField class
29683  * @cfg {string} fieldLabel - the label associated
29684  * @cfg {Number} labelWidth set the width of label (0-12)
29685  * @cfg {String} labelAlign (top|left)
29686  * @cfg {Boolean} dayAllowBlank (true|false) default false
29687  * @cfg {Boolean} monthAllowBlank (true|false) default false
29688  * @cfg {Boolean} yearAllowBlank (true|false) default false
29689  * @cfg {string} dayPlaceholder 
29690  * @cfg {string} monthPlaceholder
29691  * @cfg {string} yearPlaceholder
29692  * @cfg {string} dayFormat default 'd'
29693  * @cfg {string} monthFormat default 'm'
29694  * @cfg {string} yearFormat default 'Y'
29695  * @cfg {Number} labellg set the width of label (1-12)
29696  * @cfg {Number} labelmd set the width of label (1-12)
29697  * @cfg {Number} labelsm set the width of label (1-12)
29698  * @cfg {Number} labelxs set the width of label (1-12)
29699
29700  *     
29701  * @constructor
29702  * Create a new DateSplitField
29703  * @param {Object} config The config object
29704  */
29705
29706 Roo.bootstrap.DateSplitField = function(config){
29707     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29708     
29709     this.addEvents({
29710         // raw events
29711          /**
29712          * @event years
29713          * getting the data of years
29714          * @param {Roo.bootstrap.DateSplitField} this
29715          * @param {Object} years
29716          */
29717         "years" : true,
29718         /**
29719          * @event days
29720          * getting the data of days
29721          * @param {Roo.bootstrap.DateSplitField} this
29722          * @param {Object} days
29723          */
29724         "days" : true,
29725         /**
29726          * @event invalid
29727          * Fires after the field has been marked as invalid.
29728          * @param {Roo.form.Field} this
29729          * @param {String} msg The validation message
29730          */
29731         invalid : true,
29732        /**
29733          * @event valid
29734          * Fires after the field has been validated with no errors.
29735          * @param {Roo.form.Field} this
29736          */
29737         valid : true
29738     });
29739 };
29740
29741 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29742     
29743     fieldLabel : '',
29744     labelAlign : 'top',
29745     labelWidth : 3,
29746     dayAllowBlank : false,
29747     monthAllowBlank : false,
29748     yearAllowBlank : false,
29749     dayPlaceholder : '',
29750     monthPlaceholder : '',
29751     yearPlaceholder : '',
29752     dayFormat : 'd',
29753     monthFormat : 'm',
29754     yearFormat : 'Y',
29755     isFormField : true,
29756     labellg : 0,
29757     labelmd : 0,
29758     labelsm : 0,
29759     labelxs : 0,
29760     
29761     getAutoCreate : function()
29762     {
29763         var cfg = {
29764             tag : 'div',
29765             cls : 'row roo-date-split-field-group',
29766             cn : [
29767                 {
29768                     tag : 'input',
29769                     type : 'hidden',
29770                     cls : 'form-hidden-field roo-date-split-field-group-value',
29771                     name : this.name
29772                 }
29773             ]
29774         };
29775         
29776         var labelCls = 'col-md-12';
29777         var contentCls = 'col-md-4';
29778         
29779         if(this.fieldLabel){
29780             
29781             var label = {
29782                 tag : 'div',
29783                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29784                 cn : [
29785                     {
29786                         tag : 'label',
29787                         html : this.fieldLabel
29788                     }
29789                 ]
29790             };
29791             
29792             if(this.labelAlign == 'left'){
29793             
29794                 if(this.labelWidth > 12){
29795                     label.style = "width: " + this.labelWidth + 'px';
29796                 }
29797
29798                 if(this.labelWidth < 13 && this.labelmd == 0){
29799                     this.labelmd = this.labelWidth;
29800                 }
29801
29802                 if(this.labellg > 0){
29803                     labelCls = ' col-lg-' + this.labellg;
29804                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29805                 }
29806
29807                 if(this.labelmd > 0){
29808                     labelCls = ' col-md-' + this.labelmd;
29809                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29810                 }
29811
29812                 if(this.labelsm > 0){
29813                     labelCls = ' col-sm-' + this.labelsm;
29814                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29815                 }
29816
29817                 if(this.labelxs > 0){
29818                     labelCls = ' col-xs-' + this.labelxs;
29819                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29820                 }
29821             }
29822             
29823             label.cls += ' ' + labelCls;
29824             
29825             cfg.cn.push(label);
29826         }
29827         
29828         Roo.each(['day', 'month', 'year'], function(t){
29829             cfg.cn.push({
29830                 tag : 'div',
29831                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29832             });
29833         }, this);
29834         
29835         return cfg;
29836     },
29837     
29838     inputEl: function ()
29839     {
29840         return this.el.select('.roo-date-split-field-group-value', true).first();
29841     },
29842     
29843     onRender : function(ct, position) 
29844     {
29845         var _this = this;
29846         
29847         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29848         
29849         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29850         
29851         this.dayField = new Roo.bootstrap.ComboBox({
29852             allowBlank : this.dayAllowBlank,
29853             alwaysQuery : true,
29854             displayField : 'value',
29855             editable : false,
29856             fieldLabel : '',
29857             forceSelection : true,
29858             mode : 'local',
29859             placeholder : this.dayPlaceholder,
29860             selectOnFocus : true,
29861             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29862             triggerAction : 'all',
29863             typeAhead : true,
29864             valueField : 'value',
29865             store : new Roo.data.SimpleStore({
29866                 data : (function() {    
29867                     var days = [];
29868                     _this.fireEvent('days', _this, days);
29869                     return days;
29870                 })(),
29871                 fields : [ 'value' ]
29872             }),
29873             listeners : {
29874                 select : function (_self, record, index)
29875                 {
29876                     _this.setValue(_this.getValue());
29877                 }
29878             }
29879         });
29880
29881         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29882         
29883         this.monthField = new Roo.bootstrap.MonthField({
29884             after : '<i class=\"fa fa-calendar\"></i>',
29885             allowBlank : this.monthAllowBlank,
29886             placeholder : this.monthPlaceholder,
29887             readOnly : true,
29888             listeners : {
29889                 render : function (_self)
29890                 {
29891                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29892                         e.preventDefault();
29893                         _self.focus();
29894                     });
29895                 },
29896                 select : function (_self, oldvalue, newvalue)
29897                 {
29898                     _this.setValue(_this.getValue());
29899                 }
29900             }
29901         });
29902         
29903         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29904         
29905         this.yearField = new Roo.bootstrap.ComboBox({
29906             allowBlank : this.yearAllowBlank,
29907             alwaysQuery : true,
29908             displayField : 'value',
29909             editable : false,
29910             fieldLabel : '',
29911             forceSelection : true,
29912             mode : 'local',
29913             placeholder : this.yearPlaceholder,
29914             selectOnFocus : true,
29915             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29916             triggerAction : 'all',
29917             typeAhead : true,
29918             valueField : 'value',
29919             store : new Roo.data.SimpleStore({
29920                 data : (function() {
29921                     var years = [];
29922                     _this.fireEvent('years', _this, years);
29923                     return years;
29924                 })(),
29925                 fields : [ 'value' ]
29926             }),
29927             listeners : {
29928                 select : function (_self, record, index)
29929                 {
29930                     _this.setValue(_this.getValue());
29931                 }
29932             }
29933         });
29934
29935         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29936     },
29937     
29938     setValue : function(v, format)
29939     {
29940         this.inputEl.dom.value = v;
29941         
29942         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29943         
29944         var d = Date.parseDate(v, f);
29945         
29946         if(!d){
29947             this.validate();
29948             return;
29949         }
29950         
29951         this.setDay(d.format(this.dayFormat));
29952         this.setMonth(d.format(this.monthFormat));
29953         this.setYear(d.format(this.yearFormat));
29954         
29955         this.validate();
29956         
29957         return;
29958     },
29959     
29960     setDay : function(v)
29961     {
29962         this.dayField.setValue(v);
29963         this.inputEl.dom.value = this.getValue();
29964         this.validate();
29965         return;
29966     },
29967     
29968     setMonth : function(v)
29969     {
29970         this.monthField.setValue(v, true);
29971         this.inputEl.dom.value = this.getValue();
29972         this.validate();
29973         return;
29974     },
29975     
29976     setYear : function(v)
29977     {
29978         this.yearField.setValue(v);
29979         this.inputEl.dom.value = this.getValue();
29980         this.validate();
29981         return;
29982     },
29983     
29984     getDay : function()
29985     {
29986         return this.dayField.getValue();
29987     },
29988     
29989     getMonth : function()
29990     {
29991         return this.monthField.getValue();
29992     },
29993     
29994     getYear : function()
29995     {
29996         return this.yearField.getValue();
29997     },
29998     
29999     getValue : function()
30000     {
30001         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30002         
30003         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30004         
30005         return date;
30006     },
30007     
30008     reset : function()
30009     {
30010         this.setDay('');
30011         this.setMonth('');
30012         this.setYear('');
30013         this.inputEl.dom.value = '';
30014         this.validate();
30015         return;
30016     },
30017     
30018     validate : function()
30019     {
30020         var d = this.dayField.validate();
30021         var m = this.monthField.validate();
30022         var y = this.yearField.validate();
30023         
30024         var valid = true;
30025         
30026         if(
30027                 (!this.dayAllowBlank && !d) ||
30028                 (!this.monthAllowBlank && !m) ||
30029                 (!this.yearAllowBlank && !y)
30030         ){
30031             valid = false;
30032         }
30033         
30034         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30035             return valid;
30036         }
30037         
30038         if(valid){
30039             this.markValid();
30040             return valid;
30041         }
30042         
30043         this.markInvalid();
30044         
30045         return valid;
30046     },
30047     
30048     markValid : function()
30049     {
30050         
30051         var label = this.el.select('label', true).first();
30052         var icon = this.el.select('i.fa-star', true).first();
30053
30054         if(label && icon){
30055             icon.remove();
30056         }
30057         
30058         this.fireEvent('valid', this);
30059     },
30060     
30061      /**
30062      * Mark this field as invalid
30063      * @param {String} msg The validation message
30064      */
30065     markInvalid : function(msg)
30066     {
30067         
30068         var label = this.el.select('label', true).first();
30069         var icon = this.el.select('i.fa-star', true).first();
30070
30071         if(label && !icon){
30072             this.el.select('.roo-date-split-field-label', true).createChild({
30073                 tag : 'i',
30074                 cls : 'text-danger fa fa-lg fa-star',
30075                 tooltip : 'This field is required',
30076                 style : 'margin-right:5px;'
30077             }, label, true);
30078         }
30079         
30080         this.fireEvent('invalid', this, msg);
30081     },
30082     
30083     clearInvalid : function()
30084     {
30085         var label = this.el.select('label', true).first();
30086         var icon = this.el.select('i.fa-star', true).first();
30087
30088         if(label && icon){
30089             icon.remove();
30090         }
30091         
30092         this.fireEvent('valid', this);
30093     },
30094     
30095     getName: function()
30096     {
30097         return this.name;
30098     }
30099     
30100 });
30101
30102  /**
30103  *
30104  * This is based on 
30105  * http://masonry.desandro.com
30106  *
30107  * The idea is to render all the bricks based on vertical width...
30108  *
30109  * The original code extends 'outlayer' - we might need to use that....
30110  * 
30111  */
30112
30113
30114 /**
30115  * @class Roo.bootstrap.LayoutMasonry
30116  * @extends Roo.bootstrap.Component
30117  * Bootstrap Layout Masonry class
30118  * 
30119  * @constructor
30120  * Create a new Element
30121  * @param {Object} config The config object
30122  */
30123
30124 Roo.bootstrap.LayoutMasonry = function(config){
30125     
30126     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30127     
30128     this.bricks = [];
30129     
30130     Roo.bootstrap.LayoutMasonry.register(this);
30131     
30132     this.addEvents({
30133         // raw events
30134         /**
30135          * @event layout
30136          * Fire after layout the items
30137          * @param {Roo.bootstrap.LayoutMasonry} this
30138          * @param {Roo.EventObject} e
30139          */
30140         "layout" : true
30141     });
30142     
30143 };
30144
30145 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30146     
30147     /**
30148      * @cfg {Boolean} isLayoutInstant = no animation?
30149      */   
30150     isLayoutInstant : false, // needed?
30151    
30152     /**
30153      * @cfg {Number} boxWidth  width of the columns
30154      */   
30155     boxWidth : 450,
30156     
30157       /**
30158      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30159      */   
30160     boxHeight : 0,
30161     
30162     /**
30163      * @cfg {Number} padWidth padding below box..
30164      */   
30165     padWidth : 10, 
30166     
30167     /**
30168      * @cfg {Number} gutter gutter width..
30169      */   
30170     gutter : 10,
30171     
30172      /**
30173      * @cfg {Number} maxCols maximum number of columns
30174      */   
30175     
30176     maxCols: 0,
30177     
30178     /**
30179      * @cfg {Boolean} isAutoInitial defalut true
30180      */   
30181     isAutoInitial : true, 
30182     
30183     containerWidth: 0,
30184     
30185     /**
30186      * @cfg {Boolean} isHorizontal defalut false
30187      */   
30188     isHorizontal : false, 
30189
30190     currentSize : null,
30191     
30192     tag: 'div',
30193     
30194     cls: '',
30195     
30196     bricks: null, //CompositeElement
30197     
30198     cols : 1,
30199     
30200     _isLayoutInited : false,
30201     
30202 //    isAlternative : false, // only use for vertical layout...
30203     
30204     /**
30205      * @cfg {Number} alternativePadWidth padding below box..
30206      */   
30207     alternativePadWidth : 50,
30208     
30209     selectedBrick : [],
30210     
30211     getAutoCreate : function(){
30212         
30213         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30214         
30215         var cfg = {
30216             tag: this.tag,
30217             cls: 'blog-masonary-wrapper ' + this.cls,
30218             cn : {
30219                 cls : 'mas-boxes masonary'
30220             }
30221         };
30222         
30223         return cfg;
30224     },
30225     
30226     getChildContainer: function( )
30227     {
30228         if (this.boxesEl) {
30229             return this.boxesEl;
30230         }
30231         
30232         this.boxesEl = this.el.select('.mas-boxes').first();
30233         
30234         return this.boxesEl;
30235     },
30236     
30237     
30238     initEvents : function()
30239     {
30240         var _this = this;
30241         
30242         if(this.isAutoInitial){
30243             Roo.log('hook children rendered');
30244             this.on('childrenrendered', function() {
30245                 Roo.log('children rendered');
30246                 _this.initial();
30247             } ,this);
30248         }
30249     },
30250     
30251     initial : function()
30252     {
30253         this.selectedBrick = [];
30254         
30255         this.currentSize = this.el.getBox(true);
30256         
30257         Roo.EventManager.onWindowResize(this.resize, this); 
30258
30259         if(!this.isAutoInitial){
30260             this.layout();
30261             return;
30262         }
30263         
30264         this.layout();
30265         
30266         return;
30267         //this.layout.defer(500,this);
30268         
30269     },
30270     
30271     resize : function()
30272     {
30273         var cs = this.el.getBox(true);
30274         
30275         if (
30276                 this.currentSize.width == cs.width && 
30277                 this.currentSize.x == cs.x && 
30278                 this.currentSize.height == cs.height && 
30279                 this.currentSize.y == cs.y 
30280         ) {
30281             Roo.log("no change in with or X or Y");
30282             return;
30283         }
30284         
30285         this.currentSize = cs;
30286         
30287         this.layout();
30288         
30289     },
30290     
30291     layout : function()
30292     {   
30293         this._resetLayout();
30294         
30295         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30296         
30297         this.layoutItems( isInstant );
30298       
30299         this._isLayoutInited = true;
30300         
30301         this.fireEvent('layout', this);
30302         
30303     },
30304     
30305     _resetLayout : function()
30306     {
30307         if(this.isHorizontal){
30308             this.horizontalMeasureColumns();
30309             return;
30310         }
30311         
30312         this.verticalMeasureColumns();
30313         
30314     },
30315     
30316     verticalMeasureColumns : function()
30317     {
30318         this.getContainerWidth();
30319         
30320 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30321 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30322 //            return;
30323 //        }
30324         
30325         var boxWidth = this.boxWidth + this.padWidth;
30326         
30327         if(this.containerWidth < this.boxWidth){
30328             boxWidth = this.containerWidth
30329         }
30330         
30331         var containerWidth = this.containerWidth;
30332         
30333         var cols = Math.floor(containerWidth / boxWidth);
30334         
30335         this.cols = Math.max( cols, 1 );
30336         
30337         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30338         
30339         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30340         
30341         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30342         
30343         this.colWidth = boxWidth + avail - this.padWidth;
30344         
30345         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30346         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30347     },
30348     
30349     horizontalMeasureColumns : function()
30350     {
30351         this.getContainerWidth();
30352         
30353         var boxWidth = this.boxWidth;
30354         
30355         if(this.containerWidth < boxWidth){
30356             boxWidth = this.containerWidth;
30357         }
30358         
30359         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30360         
30361         this.el.setHeight(boxWidth);
30362         
30363     },
30364     
30365     getContainerWidth : function()
30366     {
30367         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30368     },
30369     
30370     layoutItems : function( isInstant )
30371     {
30372         Roo.log(this.bricks);
30373         
30374         var items = Roo.apply([], this.bricks);
30375         
30376         if(this.isHorizontal){
30377             this._horizontalLayoutItems( items , isInstant );
30378             return;
30379         }
30380         
30381 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30382 //            this._verticalAlternativeLayoutItems( items , isInstant );
30383 //            return;
30384 //        }
30385         
30386         this._verticalLayoutItems( items , isInstant );
30387         
30388     },
30389     
30390     _verticalLayoutItems : function ( items , isInstant)
30391     {
30392         if ( !items || !items.length ) {
30393             return;
30394         }
30395         
30396         var standard = [
30397             ['xs', 'xs', 'xs', 'tall'],
30398             ['xs', 'xs', 'tall'],
30399             ['xs', 'xs', 'sm'],
30400             ['xs', 'xs', 'xs'],
30401             ['xs', 'tall'],
30402             ['xs', 'sm'],
30403             ['xs', 'xs'],
30404             ['xs'],
30405             
30406             ['sm', 'xs', 'xs'],
30407             ['sm', 'xs'],
30408             ['sm'],
30409             
30410             ['tall', 'xs', 'xs', 'xs'],
30411             ['tall', 'xs', 'xs'],
30412             ['tall', 'xs'],
30413             ['tall']
30414             
30415         ];
30416         
30417         var queue = [];
30418         
30419         var boxes = [];
30420         
30421         var box = [];
30422         
30423         Roo.each(items, function(item, k){
30424             
30425             switch (item.size) {
30426                 // these layouts take up a full box,
30427                 case 'md' :
30428                 case 'md-left' :
30429                 case 'md-right' :
30430                 case 'wide' :
30431                     
30432                     if(box.length){
30433                         boxes.push(box);
30434                         box = [];
30435                     }
30436                     
30437                     boxes.push([item]);
30438                     
30439                     break;
30440                     
30441                 case 'xs' :
30442                 case 'sm' :
30443                 case 'tall' :
30444                     
30445                     box.push(item);
30446                     
30447                     break;
30448                 default :
30449                     break;
30450                     
30451             }
30452             
30453         }, this);
30454         
30455         if(box.length){
30456             boxes.push(box);
30457             box = [];
30458         }
30459         
30460         var filterPattern = function(box, length)
30461         {
30462             if(!box.length){
30463                 return;
30464             }
30465             
30466             var match = false;
30467             
30468             var pattern = box.slice(0, length);
30469             
30470             var format = [];
30471             
30472             Roo.each(pattern, function(i){
30473                 format.push(i.size);
30474             }, this);
30475             
30476             Roo.each(standard, function(s){
30477                 
30478                 if(String(s) != String(format)){
30479                     return;
30480                 }
30481                 
30482                 match = true;
30483                 return false;
30484                 
30485             }, this);
30486             
30487             if(!match && length == 1){
30488                 return;
30489             }
30490             
30491             if(!match){
30492                 filterPattern(box, length - 1);
30493                 return;
30494             }
30495                 
30496             queue.push(pattern);
30497
30498             box = box.slice(length, box.length);
30499
30500             filterPattern(box, 4);
30501
30502             return;
30503             
30504         }
30505         
30506         Roo.each(boxes, function(box, k){
30507             
30508             if(!box.length){
30509                 return;
30510             }
30511             
30512             if(box.length == 1){
30513                 queue.push(box);
30514                 return;
30515             }
30516             
30517             filterPattern(box, 4);
30518             
30519         }, this);
30520         
30521         this._processVerticalLayoutQueue( queue, isInstant );
30522         
30523     },
30524     
30525 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30526 //    {
30527 //        if ( !items || !items.length ) {
30528 //            return;
30529 //        }
30530 //
30531 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30532 //        
30533 //    },
30534     
30535     _horizontalLayoutItems : function ( items , isInstant)
30536     {
30537         if ( !items || !items.length || items.length < 3) {
30538             return;
30539         }
30540         
30541         items.reverse();
30542         
30543         var eItems = items.slice(0, 3);
30544         
30545         items = items.slice(3, items.length);
30546         
30547         var standard = [
30548             ['xs', 'xs', 'xs', 'wide'],
30549             ['xs', 'xs', 'wide'],
30550             ['xs', 'xs', 'sm'],
30551             ['xs', 'xs', 'xs'],
30552             ['xs', 'wide'],
30553             ['xs', 'sm'],
30554             ['xs', 'xs'],
30555             ['xs'],
30556             
30557             ['sm', 'xs', 'xs'],
30558             ['sm', 'xs'],
30559             ['sm'],
30560             
30561             ['wide', 'xs', 'xs', 'xs'],
30562             ['wide', 'xs', 'xs'],
30563             ['wide', 'xs'],
30564             ['wide'],
30565             
30566             ['wide-thin']
30567         ];
30568         
30569         var queue = [];
30570         
30571         var boxes = [];
30572         
30573         var box = [];
30574         
30575         Roo.each(items, function(item, k){
30576             
30577             switch (item.size) {
30578                 case 'md' :
30579                 case 'md-left' :
30580                 case 'md-right' :
30581                 case 'tall' :
30582                     
30583                     if(box.length){
30584                         boxes.push(box);
30585                         box = [];
30586                     }
30587                     
30588                     boxes.push([item]);
30589                     
30590                     break;
30591                     
30592                 case 'xs' :
30593                 case 'sm' :
30594                 case 'wide' :
30595                 case 'wide-thin' :
30596                     
30597                     box.push(item);
30598                     
30599                     break;
30600                 default :
30601                     break;
30602                     
30603             }
30604             
30605         }, this);
30606         
30607         if(box.length){
30608             boxes.push(box);
30609             box = [];
30610         }
30611         
30612         var filterPattern = function(box, length)
30613         {
30614             if(!box.length){
30615                 return;
30616             }
30617             
30618             var match = false;
30619             
30620             var pattern = box.slice(0, length);
30621             
30622             var format = [];
30623             
30624             Roo.each(pattern, function(i){
30625                 format.push(i.size);
30626             }, this);
30627             
30628             Roo.each(standard, function(s){
30629                 
30630                 if(String(s) != String(format)){
30631                     return;
30632                 }
30633                 
30634                 match = true;
30635                 return false;
30636                 
30637             }, this);
30638             
30639             if(!match && length == 1){
30640                 return;
30641             }
30642             
30643             if(!match){
30644                 filterPattern(box, length - 1);
30645                 return;
30646             }
30647                 
30648             queue.push(pattern);
30649
30650             box = box.slice(length, box.length);
30651
30652             filterPattern(box, 4);
30653
30654             return;
30655             
30656         }
30657         
30658         Roo.each(boxes, function(box, k){
30659             
30660             if(!box.length){
30661                 return;
30662             }
30663             
30664             if(box.length == 1){
30665                 queue.push(box);
30666                 return;
30667             }
30668             
30669             filterPattern(box, 4);
30670             
30671         }, this);
30672         
30673         
30674         var prune = [];
30675         
30676         var pos = this.el.getBox(true);
30677         
30678         var minX = pos.x;
30679         
30680         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30681         
30682         var hit_end = false;
30683         
30684         Roo.each(queue, function(box){
30685             
30686             if(hit_end){
30687                 
30688                 Roo.each(box, function(b){
30689                 
30690                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30691                     b.el.hide();
30692
30693                 }, this);
30694
30695                 return;
30696             }
30697             
30698             var mx = 0;
30699             
30700             Roo.each(box, function(b){
30701                 
30702                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30703                 b.el.show();
30704
30705                 mx = Math.max(mx, b.x);
30706                 
30707             }, this);
30708             
30709             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30710             
30711             if(maxX < minX){
30712                 
30713                 Roo.each(box, function(b){
30714                 
30715                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30716                     b.el.hide();
30717                     
30718                 }, this);
30719                 
30720                 hit_end = true;
30721                 
30722                 return;
30723             }
30724             
30725             prune.push(box);
30726             
30727         }, this);
30728         
30729         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30730     },
30731     
30732     /** Sets position of item in DOM
30733     * @param {Element} item
30734     * @param {Number} x - horizontal position
30735     * @param {Number} y - vertical position
30736     * @param {Boolean} isInstant - disables transitions
30737     */
30738     _processVerticalLayoutQueue : function( queue, isInstant )
30739     {
30740         var pos = this.el.getBox(true);
30741         var x = pos.x;
30742         var y = pos.y;
30743         var maxY = [];
30744         
30745         for (var i = 0; i < this.cols; i++){
30746             maxY[i] = pos.y;
30747         }
30748         
30749         Roo.each(queue, function(box, k){
30750             
30751             var col = k % this.cols;
30752             
30753             Roo.each(box, function(b,kk){
30754                 
30755                 b.el.position('absolute');
30756                 
30757                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30758                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30759                 
30760                 if(b.size == 'md-left' || b.size == 'md-right'){
30761                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30762                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30763                 }
30764                 
30765                 b.el.setWidth(width);
30766                 b.el.setHeight(height);
30767                 // iframe?
30768                 b.el.select('iframe',true).setSize(width,height);
30769                 
30770             }, this);
30771             
30772             for (var i = 0; i < this.cols; i++){
30773                 
30774                 if(maxY[i] < maxY[col]){
30775                     col = i;
30776                     continue;
30777                 }
30778                 
30779                 col = Math.min(col, i);
30780                 
30781             }
30782             
30783             x = pos.x + col * (this.colWidth + this.padWidth);
30784             
30785             y = maxY[col];
30786             
30787             var positions = [];
30788             
30789             switch (box.length){
30790                 case 1 :
30791                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30792                     break;
30793                 case 2 :
30794                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30795                     break;
30796                 case 3 :
30797                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30798                     break;
30799                 case 4 :
30800                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30801                     break;
30802                 default :
30803                     break;
30804             }
30805             
30806             Roo.each(box, function(b,kk){
30807                 
30808                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30809                 
30810                 var sz = b.el.getSize();
30811                 
30812                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30813                 
30814             }, this);
30815             
30816         }, this);
30817         
30818         var mY = 0;
30819         
30820         for (var i = 0; i < this.cols; i++){
30821             mY = Math.max(mY, maxY[i]);
30822         }
30823         
30824         this.el.setHeight(mY - pos.y);
30825         
30826     },
30827     
30828 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30829 //    {
30830 //        var pos = this.el.getBox(true);
30831 //        var x = pos.x;
30832 //        var y = pos.y;
30833 //        var maxX = pos.right;
30834 //        
30835 //        var maxHeight = 0;
30836 //        
30837 //        Roo.each(items, function(item, k){
30838 //            
30839 //            var c = k % 2;
30840 //            
30841 //            item.el.position('absolute');
30842 //                
30843 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30844 //
30845 //            item.el.setWidth(width);
30846 //
30847 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30848 //
30849 //            item.el.setHeight(height);
30850 //            
30851 //            if(c == 0){
30852 //                item.el.setXY([x, y], isInstant ? false : true);
30853 //            } else {
30854 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30855 //            }
30856 //            
30857 //            y = y + height + this.alternativePadWidth;
30858 //            
30859 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30860 //            
30861 //        }, this);
30862 //        
30863 //        this.el.setHeight(maxHeight);
30864 //        
30865 //    },
30866     
30867     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30868     {
30869         var pos = this.el.getBox(true);
30870         
30871         var minX = pos.x;
30872         var minY = pos.y;
30873         
30874         var maxX = pos.right;
30875         
30876         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30877         
30878         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30879         
30880         Roo.each(queue, function(box, k){
30881             
30882             Roo.each(box, function(b, kk){
30883                 
30884                 b.el.position('absolute');
30885                 
30886                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30887                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30888                 
30889                 if(b.size == 'md-left' || b.size == 'md-right'){
30890                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30891                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30892                 }
30893                 
30894                 b.el.setWidth(width);
30895                 b.el.setHeight(height);
30896                 
30897             }, this);
30898             
30899             if(!box.length){
30900                 return;
30901             }
30902             
30903             var positions = [];
30904             
30905             switch (box.length){
30906                 case 1 :
30907                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30908                     break;
30909                 case 2 :
30910                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30911                     break;
30912                 case 3 :
30913                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30914                     break;
30915                 case 4 :
30916                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30917                     break;
30918                 default :
30919                     break;
30920             }
30921             
30922             Roo.each(box, function(b,kk){
30923                 
30924                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30925                 
30926                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30927                 
30928             }, this);
30929             
30930         }, this);
30931         
30932     },
30933     
30934     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30935     {
30936         Roo.each(eItems, function(b,k){
30937             
30938             b.size = (k == 0) ? 'sm' : 'xs';
30939             b.x = (k == 0) ? 2 : 1;
30940             b.y = (k == 0) ? 2 : 1;
30941             
30942             b.el.position('absolute');
30943             
30944             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30945                 
30946             b.el.setWidth(width);
30947             
30948             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30949             
30950             b.el.setHeight(height);
30951             
30952         }, this);
30953
30954         var positions = [];
30955         
30956         positions.push({
30957             x : maxX - this.unitWidth * 2 - this.gutter,
30958             y : minY
30959         });
30960         
30961         positions.push({
30962             x : maxX - this.unitWidth,
30963             y : minY + (this.unitWidth + this.gutter) * 2
30964         });
30965         
30966         positions.push({
30967             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30968             y : minY
30969         });
30970         
30971         Roo.each(eItems, function(b,k){
30972             
30973             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30974
30975         }, this);
30976         
30977     },
30978     
30979     getVerticalOneBoxColPositions : function(x, y, box)
30980     {
30981         var pos = [];
30982         
30983         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30984         
30985         if(box[0].size == 'md-left'){
30986             rand = 0;
30987         }
30988         
30989         if(box[0].size == 'md-right'){
30990             rand = 1;
30991         }
30992         
30993         pos.push({
30994             x : x + (this.unitWidth + this.gutter) * rand,
30995             y : y
30996         });
30997         
30998         return pos;
30999     },
31000     
31001     getVerticalTwoBoxColPositions : function(x, y, box)
31002     {
31003         var pos = [];
31004         
31005         if(box[0].size == 'xs'){
31006             
31007             pos.push({
31008                 x : x,
31009                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31010             });
31011
31012             pos.push({
31013                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31014                 y : y
31015             });
31016             
31017             return pos;
31018             
31019         }
31020         
31021         pos.push({
31022             x : x,
31023             y : y
31024         });
31025
31026         pos.push({
31027             x : x + (this.unitWidth + this.gutter) * 2,
31028             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31029         });
31030         
31031         return pos;
31032         
31033     },
31034     
31035     getVerticalThreeBoxColPositions : function(x, y, box)
31036     {
31037         var pos = [];
31038         
31039         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31040             
31041             pos.push({
31042                 x : x,
31043                 y : y
31044             });
31045
31046             pos.push({
31047                 x : x + (this.unitWidth + this.gutter) * 1,
31048                 y : y
31049             });
31050             
31051             pos.push({
31052                 x : x + (this.unitWidth + this.gutter) * 2,
31053                 y : y
31054             });
31055             
31056             return pos;
31057             
31058         }
31059         
31060         if(box[0].size == 'xs' && box[1].size == 'xs'){
31061             
31062             pos.push({
31063                 x : x,
31064                 y : y
31065             });
31066
31067             pos.push({
31068                 x : x,
31069                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31070             });
31071             
31072             pos.push({
31073                 x : x + (this.unitWidth + this.gutter) * 1,
31074                 y : y
31075             });
31076             
31077             return pos;
31078             
31079         }
31080         
31081         pos.push({
31082             x : x,
31083             y : y
31084         });
31085
31086         pos.push({
31087             x : x + (this.unitWidth + this.gutter) * 2,
31088             y : y
31089         });
31090
31091         pos.push({
31092             x : x + (this.unitWidth + this.gutter) * 2,
31093             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31094         });
31095             
31096         return pos;
31097         
31098     },
31099     
31100     getVerticalFourBoxColPositions : function(x, y, box)
31101     {
31102         var pos = [];
31103         
31104         if(box[0].size == 'xs'){
31105             
31106             pos.push({
31107                 x : x,
31108                 y : y
31109             });
31110
31111             pos.push({
31112                 x : x,
31113                 y : y + (this.unitHeight + this.gutter) * 1
31114             });
31115             
31116             pos.push({
31117                 x : x,
31118                 y : y + (this.unitHeight + this.gutter) * 2
31119             });
31120             
31121             pos.push({
31122                 x : x + (this.unitWidth + this.gutter) * 1,
31123                 y : y
31124             });
31125             
31126             return pos;
31127             
31128         }
31129         
31130         pos.push({
31131             x : x,
31132             y : y
31133         });
31134
31135         pos.push({
31136             x : x + (this.unitWidth + this.gutter) * 2,
31137             y : y
31138         });
31139
31140         pos.push({
31141             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31142             y : y + (this.unitHeight + this.gutter) * 1
31143         });
31144
31145         pos.push({
31146             x : x + (this.unitWidth + this.gutter) * 2,
31147             y : y + (this.unitWidth + this.gutter) * 2
31148         });
31149
31150         return pos;
31151         
31152     },
31153     
31154     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31155     {
31156         var pos = [];
31157         
31158         if(box[0].size == 'md-left'){
31159             pos.push({
31160                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31161                 y : minY
31162             });
31163             
31164             return pos;
31165         }
31166         
31167         if(box[0].size == 'md-right'){
31168             pos.push({
31169                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31170                 y : minY + (this.unitWidth + this.gutter) * 1
31171             });
31172             
31173             return pos;
31174         }
31175         
31176         var rand = Math.floor(Math.random() * (4 - box[0].y));
31177         
31178         pos.push({
31179             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31180             y : minY + (this.unitWidth + this.gutter) * rand
31181         });
31182         
31183         return pos;
31184         
31185     },
31186     
31187     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31188     {
31189         var pos = [];
31190         
31191         if(box[0].size == 'xs'){
31192             
31193             pos.push({
31194                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31195                 y : minY
31196             });
31197
31198             pos.push({
31199                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31200                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31201             });
31202             
31203             return pos;
31204             
31205         }
31206         
31207         pos.push({
31208             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31209             y : minY
31210         });
31211
31212         pos.push({
31213             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31214             y : minY + (this.unitWidth + this.gutter) * 2
31215         });
31216         
31217         return pos;
31218         
31219     },
31220     
31221     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31222     {
31223         var pos = [];
31224         
31225         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31226             
31227             pos.push({
31228                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31229                 y : minY
31230             });
31231
31232             pos.push({
31233                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31234                 y : minY + (this.unitWidth + this.gutter) * 1
31235             });
31236             
31237             pos.push({
31238                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31239                 y : minY + (this.unitWidth + this.gutter) * 2
31240             });
31241             
31242             return pos;
31243             
31244         }
31245         
31246         if(box[0].size == 'xs' && box[1].size == 'xs'){
31247             
31248             pos.push({
31249                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31250                 y : minY
31251             });
31252
31253             pos.push({
31254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31255                 y : minY
31256             });
31257             
31258             pos.push({
31259                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31260                 y : minY + (this.unitWidth + this.gutter) * 1
31261             });
31262             
31263             return pos;
31264             
31265         }
31266         
31267         pos.push({
31268             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31269             y : minY
31270         });
31271
31272         pos.push({
31273             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31274             y : minY + (this.unitWidth + this.gutter) * 2
31275         });
31276
31277         pos.push({
31278             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31279             y : minY + (this.unitWidth + this.gutter) * 2
31280         });
31281             
31282         return pos;
31283         
31284     },
31285     
31286     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31287     {
31288         var pos = [];
31289         
31290         if(box[0].size == 'xs'){
31291             
31292             pos.push({
31293                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31294                 y : minY
31295             });
31296
31297             pos.push({
31298                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31299                 y : minY
31300             });
31301             
31302             pos.push({
31303                 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),
31304                 y : minY
31305             });
31306             
31307             pos.push({
31308                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31309                 y : minY + (this.unitWidth + this.gutter) * 1
31310             });
31311             
31312             return pos;
31313             
31314         }
31315         
31316         pos.push({
31317             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31318             y : minY
31319         });
31320         
31321         pos.push({
31322             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31323             y : minY + (this.unitWidth + this.gutter) * 2
31324         });
31325         
31326         pos.push({
31327             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31328             y : minY + (this.unitWidth + this.gutter) * 2
31329         });
31330         
31331         pos.push({
31332             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),
31333             y : minY + (this.unitWidth + this.gutter) * 2
31334         });
31335
31336         return pos;
31337         
31338     },
31339     
31340     /**
31341     * remove a Masonry Brick
31342     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31343     */
31344     removeBrick : function(brick_id)
31345     {
31346         if (!brick_id) {
31347             return;
31348         }
31349         
31350         for (var i = 0; i<this.bricks.length; i++) {
31351             if (this.bricks[i].id == brick_id) {
31352                 this.bricks.splice(i,1);
31353                 this.selectedBrick = [];
31354                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31355                 this.initial();
31356             }
31357         }
31358     },
31359     
31360     /**
31361     * adds a Masonry Brick
31362     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31363     */
31364     addBrick : function(cfg)
31365     {
31366         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31367         //this.register(cn);
31368         cn.parentId = this.id;
31369         cn.onRender(this.el, null);
31370         return cn;
31371     },
31372     
31373     /**
31374     * register a Masonry Brick
31375     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31376     */
31377     
31378     register : function(brick)
31379     {
31380         this.bricks.push(brick);
31381         brick.masonryId = this.id;
31382     },
31383     
31384     /**
31385     * clear all the Masonry Brick
31386     */
31387     clearAll : function()
31388     {
31389         this.bricks = [];
31390         //this.getChildContainer().dom.innerHTML = "";
31391         this.el.dom.innerHTML = '';
31392     },
31393     
31394     getSelected : function()
31395     {
31396         if (!this.selectedBrick) {
31397             return false;
31398         }
31399         
31400         return this.selectedBrick;
31401     }
31402 });
31403
31404 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31405     
31406     groups: {},
31407      /**
31408     * register a Masonry Layout
31409     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31410     */
31411     
31412     register : function(layout)
31413     {
31414         this.groups[layout.id] = layout;
31415     },
31416     /**
31417     * fetch a  Masonry Layout based on the masonry layout ID
31418     * @param {string} the masonry layout to add
31419     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31420     */
31421     
31422     get: function(layout_id) {
31423         if (typeof(this.groups[layout_id]) == 'undefined') {
31424             return false;
31425         }
31426         return this.groups[layout_id] ;
31427     }
31428     
31429     
31430     
31431 });
31432
31433  
31434
31435  /**
31436  *
31437  * This is based on 
31438  * http://masonry.desandro.com
31439  *
31440  * The idea is to render all the bricks based on vertical width...
31441  *
31442  * The original code extends 'outlayer' - we might need to use that....
31443  * 
31444  */
31445
31446
31447 /**
31448  * @class Roo.bootstrap.LayoutMasonryAuto
31449  * @extends Roo.bootstrap.Component
31450  * Bootstrap Layout Masonry class
31451  * 
31452  * @constructor
31453  * Create a new Element
31454  * @param {Object} config The config object
31455  */
31456
31457 Roo.bootstrap.LayoutMasonryAuto = function(config){
31458     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31459 };
31460
31461 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31462     
31463       /**
31464      * @cfg {Boolean} isFitWidth  - resize the width..
31465      */   
31466     isFitWidth : false,  // options..
31467     /**
31468      * @cfg {Boolean} isOriginLeft = left align?
31469      */   
31470     isOriginLeft : true,
31471     /**
31472      * @cfg {Boolean} isOriginTop = top align?
31473      */   
31474     isOriginTop : false,
31475     /**
31476      * @cfg {Boolean} isLayoutInstant = no animation?
31477      */   
31478     isLayoutInstant : false, // needed?
31479     /**
31480      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31481      */   
31482     isResizingContainer : true,
31483     /**
31484      * @cfg {Number} columnWidth  width of the columns 
31485      */   
31486     
31487     columnWidth : 0,
31488     
31489     /**
31490      * @cfg {Number} maxCols maximum number of columns
31491      */   
31492     
31493     maxCols: 0,
31494     /**
31495      * @cfg {Number} padHeight padding below box..
31496      */   
31497     
31498     padHeight : 10, 
31499     
31500     /**
31501      * @cfg {Boolean} isAutoInitial defalut true
31502      */   
31503     
31504     isAutoInitial : true, 
31505     
31506     // private?
31507     gutter : 0,
31508     
31509     containerWidth: 0,
31510     initialColumnWidth : 0,
31511     currentSize : null,
31512     
31513     colYs : null, // array.
31514     maxY : 0,
31515     padWidth: 10,
31516     
31517     
31518     tag: 'div',
31519     cls: '',
31520     bricks: null, //CompositeElement
31521     cols : 0, // array?
31522     // element : null, // wrapped now this.el
31523     _isLayoutInited : null, 
31524     
31525     
31526     getAutoCreate : function(){
31527         
31528         var cfg = {
31529             tag: this.tag,
31530             cls: 'blog-masonary-wrapper ' + this.cls,
31531             cn : {
31532                 cls : 'mas-boxes masonary'
31533             }
31534         };
31535         
31536         return cfg;
31537     },
31538     
31539     getChildContainer: function( )
31540     {
31541         if (this.boxesEl) {
31542             return this.boxesEl;
31543         }
31544         
31545         this.boxesEl = this.el.select('.mas-boxes').first();
31546         
31547         return this.boxesEl;
31548     },
31549     
31550     
31551     initEvents : function()
31552     {
31553         var _this = this;
31554         
31555         if(this.isAutoInitial){
31556             Roo.log('hook children rendered');
31557             this.on('childrenrendered', function() {
31558                 Roo.log('children rendered');
31559                 _this.initial();
31560             } ,this);
31561         }
31562         
31563     },
31564     
31565     initial : function()
31566     {
31567         this.reloadItems();
31568
31569         this.currentSize = this.el.getBox(true);
31570
31571         /// was window resize... - let's see if this works..
31572         Roo.EventManager.onWindowResize(this.resize, this); 
31573
31574         if(!this.isAutoInitial){
31575             this.layout();
31576             return;
31577         }
31578         
31579         this.layout.defer(500,this);
31580     },
31581     
31582     reloadItems: function()
31583     {
31584         this.bricks = this.el.select('.masonry-brick', true);
31585         
31586         this.bricks.each(function(b) {
31587             //Roo.log(b.getSize());
31588             if (!b.attr('originalwidth')) {
31589                 b.attr('originalwidth',  b.getSize().width);
31590             }
31591             
31592         });
31593         
31594         Roo.log(this.bricks.elements.length);
31595     },
31596     
31597     resize : function()
31598     {
31599         Roo.log('resize');
31600         var cs = this.el.getBox(true);
31601         
31602         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31603             Roo.log("no change in with or X");
31604             return;
31605         }
31606         this.currentSize = cs;
31607         this.layout();
31608     },
31609     
31610     layout : function()
31611     {
31612          Roo.log('layout');
31613         this._resetLayout();
31614         //this._manageStamps();
31615       
31616         // don't animate first layout
31617         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31618         this.layoutItems( isInstant );
31619       
31620         // flag for initalized
31621         this._isLayoutInited = true;
31622     },
31623     
31624     layoutItems : function( isInstant )
31625     {
31626         //var items = this._getItemsForLayout( this.items );
31627         // original code supports filtering layout items.. we just ignore it..
31628         
31629         this._layoutItems( this.bricks , isInstant );
31630       
31631         this._postLayout();
31632     },
31633     _layoutItems : function ( items , isInstant)
31634     {
31635        //this.fireEvent( 'layout', this, items );
31636     
31637
31638         if ( !items || !items.elements.length ) {
31639           // no items, emit event with empty array
31640             return;
31641         }
31642
31643         var queue = [];
31644         items.each(function(item) {
31645             Roo.log("layout item");
31646             Roo.log(item);
31647             // get x/y object from method
31648             var position = this._getItemLayoutPosition( item );
31649             // enqueue
31650             position.item = item;
31651             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31652             queue.push( position );
31653         }, this);
31654       
31655         this._processLayoutQueue( queue );
31656     },
31657     /** Sets position of item in DOM
31658     * @param {Element} item
31659     * @param {Number} x - horizontal position
31660     * @param {Number} y - vertical position
31661     * @param {Boolean} isInstant - disables transitions
31662     */
31663     _processLayoutQueue : function( queue )
31664     {
31665         for ( var i=0, len = queue.length; i < len; i++ ) {
31666             var obj = queue[i];
31667             obj.item.position('absolute');
31668             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31669         }
31670     },
31671       
31672     
31673     /**
31674     * Any logic you want to do after each layout,
31675     * i.e. size the container
31676     */
31677     _postLayout : function()
31678     {
31679         this.resizeContainer();
31680     },
31681     
31682     resizeContainer : function()
31683     {
31684         if ( !this.isResizingContainer ) {
31685             return;
31686         }
31687         var size = this._getContainerSize();
31688         if ( size ) {
31689             this.el.setSize(size.width,size.height);
31690             this.boxesEl.setSize(size.width,size.height);
31691         }
31692     },
31693     
31694     
31695     
31696     _resetLayout : function()
31697     {
31698         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31699         this.colWidth = this.el.getWidth();
31700         //this.gutter = this.el.getWidth(); 
31701         
31702         this.measureColumns();
31703
31704         // reset column Y
31705         var i = this.cols;
31706         this.colYs = [];
31707         while (i--) {
31708             this.colYs.push( 0 );
31709         }
31710     
31711         this.maxY = 0;
31712     },
31713
31714     measureColumns : function()
31715     {
31716         this.getContainerWidth();
31717       // if columnWidth is 0, default to outerWidth of first item
31718         if ( !this.columnWidth ) {
31719             var firstItem = this.bricks.first();
31720             Roo.log(firstItem);
31721             this.columnWidth  = this.containerWidth;
31722             if (firstItem && firstItem.attr('originalwidth') ) {
31723                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31724             }
31725             // columnWidth fall back to item of first element
31726             Roo.log("set column width?");
31727                         this.initialColumnWidth = this.columnWidth  ;
31728
31729             // if first elem has no width, default to size of container
31730             
31731         }
31732         
31733         
31734         if (this.initialColumnWidth) {
31735             this.columnWidth = this.initialColumnWidth;
31736         }
31737         
31738         
31739             
31740         // column width is fixed at the top - however if container width get's smaller we should
31741         // reduce it...
31742         
31743         // this bit calcs how man columns..
31744             
31745         var columnWidth = this.columnWidth += this.gutter;
31746       
31747         // calculate columns
31748         var containerWidth = this.containerWidth + this.gutter;
31749         
31750         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31751         // fix rounding errors, typically with gutters
31752         var excess = columnWidth - containerWidth % columnWidth;
31753         
31754         
31755         // if overshoot is less than a pixel, round up, otherwise floor it
31756         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31757         cols = Math[ mathMethod ]( cols );
31758         this.cols = Math.max( cols, 1 );
31759         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31760         
31761          // padding positioning..
31762         var totalColWidth = this.cols * this.columnWidth;
31763         var padavail = this.containerWidth - totalColWidth;
31764         // so for 2 columns - we need 3 'pads'
31765         
31766         var padNeeded = (1+this.cols) * this.padWidth;
31767         
31768         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31769         
31770         this.columnWidth += padExtra
31771         //this.padWidth = Math.floor(padavail /  ( this.cols));
31772         
31773         // adjust colum width so that padding is fixed??
31774         
31775         // we have 3 columns ... total = width * 3
31776         // we have X left over... that should be used by 
31777         
31778         //if (this.expandC) {
31779             
31780         //}
31781         
31782         
31783         
31784     },
31785     
31786     getContainerWidth : function()
31787     {
31788        /* // container is parent if fit width
31789         var container = this.isFitWidth ? this.element.parentNode : this.element;
31790         // check that this.size and size are there
31791         // IE8 triggers resize on body size change, so they might not be
31792         
31793         var size = getSize( container );  //FIXME
31794         this.containerWidth = size && size.innerWidth; //FIXME
31795         */
31796          
31797         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31798         
31799     },
31800     
31801     _getItemLayoutPosition : function( item )  // what is item?
31802     {
31803         // we resize the item to our columnWidth..
31804       
31805         item.setWidth(this.columnWidth);
31806         item.autoBoxAdjust  = false;
31807         
31808         var sz = item.getSize();
31809  
31810         // how many columns does this brick span
31811         var remainder = this.containerWidth % this.columnWidth;
31812         
31813         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31814         // round if off by 1 pixel, otherwise use ceil
31815         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31816         colSpan = Math.min( colSpan, this.cols );
31817         
31818         // normally this should be '1' as we dont' currently allow multi width columns..
31819         
31820         var colGroup = this._getColGroup( colSpan );
31821         // get the minimum Y value from the columns
31822         var minimumY = Math.min.apply( Math, colGroup );
31823         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31824         
31825         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31826          
31827         // position the brick
31828         var position = {
31829             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31830             y: this.currentSize.y + minimumY + this.padHeight
31831         };
31832         
31833         Roo.log(position);
31834         // apply setHeight to necessary columns
31835         var setHeight = minimumY + sz.height + this.padHeight;
31836         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31837         
31838         var setSpan = this.cols + 1 - colGroup.length;
31839         for ( var i = 0; i < setSpan; i++ ) {
31840           this.colYs[ shortColIndex + i ] = setHeight ;
31841         }
31842       
31843         return position;
31844     },
31845     
31846     /**
31847      * @param {Number} colSpan - number of columns the element spans
31848      * @returns {Array} colGroup
31849      */
31850     _getColGroup : function( colSpan )
31851     {
31852         if ( colSpan < 2 ) {
31853           // if brick spans only one column, use all the column Ys
31854           return this.colYs;
31855         }
31856       
31857         var colGroup = [];
31858         // how many different places could this brick fit horizontally
31859         var groupCount = this.cols + 1 - colSpan;
31860         // for each group potential horizontal position
31861         for ( var i = 0; i < groupCount; i++ ) {
31862           // make an array of colY values for that one group
31863           var groupColYs = this.colYs.slice( i, i + colSpan );
31864           // and get the max value of the array
31865           colGroup[i] = Math.max.apply( Math, groupColYs );
31866         }
31867         return colGroup;
31868     },
31869     /*
31870     _manageStamp : function( stamp )
31871     {
31872         var stampSize =  stamp.getSize();
31873         var offset = stamp.getBox();
31874         // get the columns that this stamp affects
31875         var firstX = this.isOriginLeft ? offset.x : offset.right;
31876         var lastX = firstX + stampSize.width;
31877         var firstCol = Math.floor( firstX / this.columnWidth );
31878         firstCol = Math.max( 0, firstCol );
31879         
31880         var lastCol = Math.floor( lastX / this.columnWidth );
31881         // lastCol should not go over if multiple of columnWidth #425
31882         lastCol -= lastX % this.columnWidth ? 0 : 1;
31883         lastCol = Math.min( this.cols - 1, lastCol );
31884         
31885         // set colYs to bottom of the stamp
31886         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31887             stampSize.height;
31888             
31889         for ( var i = firstCol; i <= lastCol; i++ ) {
31890           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31891         }
31892     },
31893     */
31894     
31895     _getContainerSize : function()
31896     {
31897         this.maxY = Math.max.apply( Math, this.colYs );
31898         var size = {
31899             height: this.maxY
31900         };
31901       
31902         if ( this.isFitWidth ) {
31903             size.width = this._getContainerFitWidth();
31904         }
31905       
31906         return size;
31907     },
31908     
31909     _getContainerFitWidth : function()
31910     {
31911         var unusedCols = 0;
31912         // count unused columns
31913         var i = this.cols;
31914         while ( --i ) {
31915           if ( this.colYs[i] !== 0 ) {
31916             break;
31917           }
31918           unusedCols++;
31919         }
31920         // fit container to columns that have been used
31921         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31922     },
31923     
31924     needsResizeLayout : function()
31925     {
31926         var previousWidth = this.containerWidth;
31927         this.getContainerWidth();
31928         return previousWidth !== this.containerWidth;
31929     }
31930  
31931 });
31932
31933  
31934
31935  /*
31936  * - LGPL
31937  *
31938  * element
31939  * 
31940  */
31941
31942 /**
31943  * @class Roo.bootstrap.MasonryBrick
31944  * @extends Roo.bootstrap.Component
31945  * Bootstrap MasonryBrick class
31946  * 
31947  * @constructor
31948  * Create a new MasonryBrick
31949  * @param {Object} config The config object
31950  */
31951
31952 Roo.bootstrap.MasonryBrick = function(config){
31953     
31954     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31955     
31956     Roo.bootstrap.MasonryBrick.register(this);
31957     
31958     this.addEvents({
31959         // raw events
31960         /**
31961          * @event click
31962          * When a MasonryBrick is clcik
31963          * @param {Roo.bootstrap.MasonryBrick} this
31964          * @param {Roo.EventObject} e
31965          */
31966         "click" : true
31967     });
31968 };
31969
31970 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31971     
31972     /**
31973      * @cfg {String} title
31974      */   
31975     title : '',
31976     /**
31977      * @cfg {String} html
31978      */   
31979     html : '',
31980     /**
31981      * @cfg {String} bgimage
31982      */   
31983     bgimage : '',
31984     /**
31985      * @cfg {String} videourl
31986      */   
31987     videourl : '',
31988     /**
31989      * @cfg {String} cls
31990      */   
31991     cls : '',
31992     /**
31993      * @cfg {String} href
31994      */   
31995     href : '',
31996     /**
31997      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31998      */   
31999     size : 'xs',
32000     
32001     /**
32002      * @cfg {String} placetitle (center|bottom)
32003      */   
32004     placetitle : '',
32005     
32006     /**
32007      * @cfg {Boolean} isFitContainer defalut true
32008      */   
32009     isFitContainer : true, 
32010     
32011     /**
32012      * @cfg {Boolean} preventDefault defalut false
32013      */   
32014     preventDefault : false, 
32015     
32016     /**
32017      * @cfg {Boolean} inverse defalut false
32018      */   
32019     maskInverse : false, 
32020     
32021     getAutoCreate : function()
32022     {
32023         if(!this.isFitContainer){
32024             return this.getSplitAutoCreate();
32025         }
32026         
32027         var cls = 'masonry-brick masonry-brick-full';
32028         
32029         if(this.href.length){
32030             cls += ' masonry-brick-link';
32031         }
32032         
32033         if(this.bgimage.length){
32034             cls += ' masonry-brick-image';
32035         }
32036         
32037         if(this.maskInverse){
32038             cls += ' mask-inverse';
32039         }
32040         
32041         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32042             cls += ' enable-mask';
32043         }
32044         
32045         if(this.size){
32046             cls += ' masonry-' + this.size + '-brick';
32047         }
32048         
32049         if(this.placetitle.length){
32050             
32051             switch (this.placetitle) {
32052                 case 'center' :
32053                     cls += ' masonry-center-title';
32054                     break;
32055                 case 'bottom' :
32056                     cls += ' masonry-bottom-title';
32057                     break;
32058                 default:
32059                     break;
32060             }
32061             
32062         } else {
32063             if(!this.html.length && !this.bgimage.length){
32064                 cls += ' masonry-center-title';
32065             }
32066
32067             if(!this.html.length && this.bgimage.length){
32068                 cls += ' masonry-bottom-title';
32069             }
32070         }
32071         
32072         if(this.cls){
32073             cls += ' ' + this.cls;
32074         }
32075         
32076         var cfg = {
32077             tag: (this.href.length) ? 'a' : 'div',
32078             cls: cls,
32079             cn: [
32080                 {
32081                     tag: 'div',
32082                     cls: 'masonry-brick-mask'
32083                 },
32084                 {
32085                     tag: 'div',
32086                     cls: 'masonry-brick-paragraph',
32087                     cn: []
32088                 }
32089             ]
32090         };
32091         
32092         if(this.href.length){
32093             cfg.href = this.href;
32094         }
32095         
32096         var cn = cfg.cn[1].cn;
32097         
32098         if(this.title.length){
32099             cn.push({
32100                 tag: 'h4',
32101                 cls: 'masonry-brick-title',
32102                 html: this.title
32103             });
32104         }
32105         
32106         if(this.html.length){
32107             cn.push({
32108                 tag: 'p',
32109                 cls: 'masonry-brick-text',
32110                 html: this.html
32111             });
32112         }
32113         
32114         if (!this.title.length && !this.html.length) {
32115             cfg.cn[1].cls += ' hide';
32116         }
32117         
32118         if(this.bgimage.length){
32119             cfg.cn.push({
32120                 tag: 'img',
32121                 cls: 'masonry-brick-image-view',
32122                 src: this.bgimage
32123             });
32124         }
32125         
32126         if(this.videourl.length){
32127             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32128             // youtube support only?
32129             cfg.cn.push({
32130                 tag: 'iframe',
32131                 cls: 'masonry-brick-image-view',
32132                 src: vurl,
32133                 frameborder : 0,
32134                 allowfullscreen : true
32135             });
32136         }
32137         
32138         return cfg;
32139         
32140     },
32141     
32142     getSplitAutoCreate : function()
32143     {
32144         var cls = 'masonry-brick masonry-brick-split';
32145         
32146         if(this.href.length){
32147             cls += ' masonry-brick-link';
32148         }
32149         
32150         if(this.bgimage.length){
32151             cls += ' masonry-brick-image';
32152         }
32153         
32154         if(this.size){
32155             cls += ' masonry-' + this.size + '-brick';
32156         }
32157         
32158         switch (this.placetitle) {
32159             case 'center' :
32160                 cls += ' masonry-center-title';
32161                 break;
32162             case 'bottom' :
32163                 cls += ' masonry-bottom-title';
32164                 break;
32165             default:
32166                 if(!this.bgimage.length){
32167                     cls += ' masonry-center-title';
32168                 }
32169
32170                 if(this.bgimage.length){
32171                     cls += ' masonry-bottom-title';
32172                 }
32173                 break;
32174         }
32175         
32176         if(this.cls){
32177             cls += ' ' + this.cls;
32178         }
32179         
32180         var cfg = {
32181             tag: (this.href.length) ? 'a' : 'div',
32182             cls: cls,
32183             cn: [
32184                 {
32185                     tag: 'div',
32186                     cls: 'masonry-brick-split-head',
32187                     cn: [
32188                         {
32189                             tag: 'div',
32190                             cls: 'masonry-brick-paragraph',
32191                             cn: []
32192                         }
32193                     ]
32194                 },
32195                 {
32196                     tag: 'div',
32197                     cls: 'masonry-brick-split-body',
32198                     cn: []
32199                 }
32200             ]
32201         };
32202         
32203         if(this.href.length){
32204             cfg.href = this.href;
32205         }
32206         
32207         if(this.title.length){
32208             cfg.cn[0].cn[0].cn.push({
32209                 tag: 'h4',
32210                 cls: 'masonry-brick-title',
32211                 html: this.title
32212             });
32213         }
32214         
32215         if(this.html.length){
32216             cfg.cn[1].cn.push({
32217                 tag: 'p',
32218                 cls: 'masonry-brick-text',
32219                 html: this.html
32220             });
32221         }
32222
32223         if(this.bgimage.length){
32224             cfg.cn[0].cn.push({
32225                 tag: 'img',
32226                 cls: 'masonry-brick-image-view',
32227                 src: this.bgimage
32228             });
32229         }
32230         
32231         if(this.videourl.length){
32232             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32233             // youtube support only?
32234             cfg.cn[0].cn.cn.push({
32235                 tag: 'iframe',
32236                 cls: 'masonry-brick-image-view',
32237                 src: vurl,
32238                 frameborder : 0,
32239                 allowfullscreen : true
32240             });
32241         }
32242         
32243         return cfg;
32244     },
32245     
32246     initEvents: function() 
32247     {
32248         switch (this.size) {
32249             case 'xs' :
32250                 this.x = 1;
32251                 this.y = 1;
32252                 break;
32253             case 'sm' :
32254                 this.x = 2;
32255                 this.y = 2;
32256                 break;
32257             case 'md' :
32258             case 'md-left' :
32259             case 'md-right' :
32260                 this.x = 3;
32261                 this.y = 3;
32262                 break;
32263             case 'tall' :
32264                 this.x = 2;
32265                 this.y = 3;
32266                 break;
32267             case 'wide' :
32268                 this.x = 3;
32269                 this.y = 2;
32270                 break;
32271             case 'wide-thin' :
32272                 this.x = 3;
32273                 this.y = 1;
32274                 break;
32275                         
32276             default :
32277                 break;
32278         }
32279         
32280         if(Roo.isTouch){
32281             this.el.on('touchstart', this.onTouchStart, this);
32282             this.el.on('touchmove', this.onTouchMove, this);
32283             this.el.on('touchend', this.onTouchEnd, this);
32284             this.el.on('contextmenu', this.onContextMenu, this);
32285         } else {
32286             this.el.on('mouseenter'  ,this.enter, this);
32287             this.el.on('mouseleave', this.leave, this);
32288             this.el.on('click', this.onClick, this);
32289         }
32290         
32291         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32292             this.parent().bricks.push(this);   
32293         }
32294         
32295     },
32296     
32297     onClick: function(e, el)
32298     {
32299         var time = this.endTimer - this.startTimer;
32300         // Roo.log(e.preventDefault());
32301         if(Roo.isTouch){
32302             if(time > 1000){
32303                 e.preventDefault();
32304                 return;
32305             }
32306         }
32307         
32308         if(!this.preventDefault){
32309             return;
32310         }
32311         
32312         e.preventDefault();
32313         
32314         if (this.activcClass != '') {
32315             this.selectBrick();
32316         }
32317         
32318         this.fireEvent('click', this);
32319     },
32320     
32321     enter: function(e, el)
32322     {
32323         e.preventDefault();
32324         
32325         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32326             return;
32327         }
32328         
32329         if(this.bgimage.length && this.html.length){
32330             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32331         }
32332     },
32333     
32334     leave: function(e, el)
32335     {
32336         e.preventDefault();
32337         
32338         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32339             return;
32340         }
32341         
32342         if(this.bgimage.length && this.html.length){
32343             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32344         }
32345     },
32346     
32347     onTouchStart: function(e, el)
32348     {
32349 //        e.preventDefault();
32350         
32351         this.touchmoved = false;
32352         
32353         if(!this.isFitContainer){
32354             return;
32355         }
32356         
32357         if(!this.bgimage.length || !this.html.length){
32358             return;
32359         }
32360         
32361         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32362         
32363         this.timer = new Date().getTime();
32364         
32365     },
32366     
32367     onTouchMove: function(e, el)
32368     {
32369         this.touchmoved = true;
32370     },
32371     
32372     onContextMenu : function(e,el)
32373     {
32374         e.preventDefault();
32375         e.stopPropagation();
32376         return false;
32377     },
32378     
32379     onTouchEnd: function(e, el)
32380     {
32381 //        e.preventDefault();
32382         
32383         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32384         
32385             this.leave(e,el);
32386             
32387             return;
32388         }
32389         
32390         if(!this.bgimage.length || !this.html.length){
32391             
32392             if(this.href.length){
32393                 window.location.href = this.href;
32394             }
32395             
32396             return;
32397         }
32398         
32399         if(!this.isFitContainer){
32400             return;
32401         }
32402         
32403         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32404         
32405         window.location.href = this.href;
32406     },
32407     
32408     //selection on single brick only
32409     selectBrick : function() {
32410         
32411         if (!this.parentId) {
32412             return;
32413         }
32414         
32415         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32416         var index = m.selectedBrick.indexOf(this.id);
32417         
32418         if ( index > -1) {
32419             m.selectedBrick.splice(index,1);
32420             this.el.removeClass(this.activeClass);
32421             return;
32422         }
32423         
32424         for(var i = 0; i < m.selectedBrick.length; i++) {
32425             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32426             b.el.removeClass(b.activeClass);
32427         }
32428         
32429         m.selectedBrick = [];
32430         
32431         m.selectedBrick.push(this.id);
32432         this.el.addClass(this.activeClass);
32433         return;
32434     }
32435     
32436 });
32437
32438 Roo.apply(Roo.bootstrap.MasonryBrick, {
32439     
32440     //groups: {},
32441     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32442      /**
32443     * register a Masonry Brick
32444     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32445     */
32446     
32447     register : function(brick)
32448     {
32449         //this.groups[brick.id] = brick;
32450         this.groups.add(brick.id, brick);
32451     },
32452     /**
32453     * fetch a  masonry brick based on the masonry brick ID
32454     * @param {string} the masonry brick to add
32455     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32456     */
32457     
32458     get: function(brick_id) 
32459     {
32460         // if (typeof(this.groups[brick_id]) == 'undefined') {
32461         //     return false;
32462         // }
32463         // return this.groups[brick_id] ;
32464         
32465         if(this.groups.key(brick_id)) {
32466             return this.groups.key(brick_id);
32467         }
32468         
32469         return false;
32470     }
32471     
32472     
32473     
32474 });
32475
32476  /*
32477  * - LGPL
32478  *
32479  * element
32480  * 
32481  */
32482
32483 /**
32484  * @class Roo.bootstrap.Brick
32485  * @extends Roo.bootstrap.Component
32486  * Bootstrap Brick class
32487  * 
32488  * @constructor
32489  * Create a new Brick
32490  * @param {Object} config The config object
32491  */
32492
32493 Roo.bootstrap.Brick = function(config){
32494     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32495     
32496     this.addEvents({
32497         // raw events
32498         /**
32499          * @event click
32500          * When a Brick is click
32501          * @param {Roo.bootstrap.Brick} this
32502          * @param {Roo.EventObject} e
32503          */
32504         "click" : true
32505     });
32506 };
32507
32508 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32509     
32510     /**
32511      * @cfg {String} title
32512      */   
32513     title : '',
32514     /**
32515      * @cfg {String} html
32516      */   
32517     html : '',
32518     /**
32519      * @cfg {String} bgimage
32520      */   
32521     bgimage : '',
32522     /**
32523      * @cfg {String} cls
32524      */   
32525     cls : '',
32526     /**
32527      * @cfg {String} href
32528      */   
32529     href : '',
32530     /**
32531      * @cfg {String} video
32532      */   
32533     video : '',
32534     /**
32535      * @cfg {Boolean} square
32536      */   
32537     square : true,
32538     
32539     getAutoCreate : function()
32540     {
32541         var cls = 'roo-brick';
32542         
32543         if(this.href.length){
32544             cls += ' roo-brick-link';
32545         }
32546         
32547         if(this.bgimage.length){
32548             cls += ' roo-brick-image';
32549         }
32550         
32551         if(!this.html.length && !this.bgimage.length){
32552             cls += ' roo-brick-center-title';
32553         }
32554         
32555         if(!this.html.length && this.bgimage.length){
32556             cls += ' roo-brick-bottom-title';
32557         }
32558         
32559         if(this.cls){
32560             cls += ' ' + this.cls;
32561         }
32562         
32563         var cfg = {
32564             tag: (this.href.length) ? 'a' : 'div',
32565             cls: cls,
32566             cn: [
32567                 {
32568                     tag: 'div',
32569                     cls: 'roo-brick-paragraph',
32570                     cn: []
32571                 }
32572             ]
32573         };
32574         
32575         if(this.href.length){
32576             cfg.href = this.href;
32577         }
32578         
32579         var cn = cfg.cn[0].cn;
32580         
32581         if(this.title.length){
32582             cn.push({
32583                 tag: 'h4',
32584                 cls: 'roo-brick-title',
32585                 html: this.title
32586             });
32587         }
32588         
32589         if(this.html.length){
32590             cn.push({
32591                 tag: 'p',
32592                 cls: 'roo-brick-text',
32593                 html: this.html
32594             });
32595         } else {
32596             cn.cls += ' hide';
32597         }
32598         
32599         if(this.bgimage.length){
32600             cfg.cn.push({
32601                 tag: 'img',
32602                 cls: 'roo-brick-image-view',
32603                 src: this.bgimage
32604             });
32605         }
32606         
32607         return cfg;
32608     },
32609     
32610     initEvents: function() 
32611     {
32612         if(this.title.length || this.html.length){
32613             this.el.on('mouseenter'  ,this.enter, this);
32614             this.el.on('mouseleave', this.leave, this);
32615         }
32616         
32617         
32618         Roo.EventManager.onWindowResize(this.resize, this); 
32619         
32620         this.resize();
32621     },
32622     
32623     resize : function()
32624     {
32625         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32626         
32627         paragraph.setWidth(paragraph.getWidth() + paragraph.getPadding('lr'));
32628         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32629         
32630         if(this.bgimage.length){
32631             var image = this.el.select('.roo-brick-image-view', true).first();
32632             image.setWidth(paragraph.getWidth());
32633 //            image.setHeight(paragraph.getWidth());
32634             
32635             this.el.setHeight(image.getHeight());
32636             paragraph.setHeight(image.getHeight());
32637             
32638         }
32639         
32640     },
32641     
32642     enter: function(e, el)
32643     {
32644         e.preventDefault();
32645         
32646         if(this.bgimage.length){
32647             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32648             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32649         }
32650     },
32651     
32652     leave: function(e, el)
32653     {
32654         e.preventDefault();
32655         
32656         if(this.bgimage.length){
32657             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32658             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32659         }
32660     }
32661     
32662 });
32663
32664  
32665
32666  /*
32667  * - LGPL
32668  *
32669  * Input
32670  * 
32671  */
32672
32673 /**
32674  * @class Roo.bootstrap.NumberField
32675  * @extends Roo.bootstrap.Input
32676  * Bootstrap NumberField class
32677  * 
32678  * 
32679  * 
32680  * 
32681  * @constructor
32682  * Create a new NumberField
32683  * @param {Object} config The config object
32684  */
32685
32686 Roo.bootstrap.NumberField = function(config){
32687     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32688 };
32689
32690 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32691     
32692     /**
32693      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32694      */
32695     allowDecimals : true,
32696     /**
32697      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32698      */
32699     decimalSeparator : ".",
32700     /**
32701      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32702      */
32703     decimalPrecision : 2,
32704     /**
32705      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32706      */
32707     allowNegative : true,
32708     /**
32709      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32710      */
32711     minValue : Number.NEGATIVE_INFINITY,
32712     /**
32713      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32714      */
32715     maxValue : Number.MAX_VALUE,
32716     /**
32717      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32718      */
32719     minText : "The minimum value for this field is {0}",
32720     /**
32721      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32722      */
32723     maxText : "The maximum value for this field is {0}",
32724     /**
32725      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32726      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32727      */
32728     nanText : "{0} is not a valid number",
32729     /**
32730      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32731      */
32732     castInt : true,
32733
32734     // private
32735     initEvents : function()
32736     {   
32737         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32738         
32739         var allowed = "0123456789";
32740         
32741         if(this.allowDecimals){
32742             allowed += this.decimalSeparator;
32743         }
32744         
32745         if(this.allowNegative){
32746             allowed += "-";
32747         }
32748         
32749         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32750         
32751         var keyPress = function(e){
32752             
32753             var k = e.getKey();
32754             
32755             var c = e.getCharCode();
32756             
32757             if(
32758                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32759                     allowed.indexOf(String.fromCharCode(c)) === -1
32760             ){
32761                 e.stopEvent();
32762                 return;
32763             }
32764             
32765             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32766                 return;
32767             }
32768             
32769             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32770                 e.stopEvent();
32771             }
32772         };
32773         
32774         this.el.on("keypress", keyPress, this);
32775     },
32776     
32777     validateValue : function(value)
32778     {
32779         
32780         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32781             return false;
32782         }
32783         
32784         var num = this.parseValue(value);
32785         
32786         if(isNaN(num)){
32787             this.markInvalid(String.format(this.nanText, value));
32788             return false;
32789         }
32790         
32791         if(num < this.minValue){
32792             this.markInvalid(String.format(this.minText, this.minValue));
32793             return false;
32794         }
32795         
32796         if(num > this.maxValue){
32797             this.markInvalid(String.format(this.maxText, this.maxValue));
32798             return false;
32799         }
32800         
32801         return true;
32802     },
32803
32804     getValue : function()
32805     {
32806         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32807     },
32808
32809     parseValue : function(value)
32810     {
32811         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32812         return isNaN(value) ? '' : value;
32813     },
32814
32815     fixPrecision : function(value)
32816     {
32817         var nan = isNaN(value);
32818         
32819         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32820             return nan ? '' : value;
32821         }
32822         return parseFloat(value).toFixed(this.decimalPrecision);
32823     },
32824
32825     setValue : function(v)
32826     {
32827         v = this.fixPrecision(v);
32828         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32829     },
32830
32831     decimalPrecisionFcn : function(v)
32832     {
32833         return Math.floor(v);
32834     },
32835
32836     beforeBlur : function()
32837     {
32838         if(!this.castInt){
32839             return;
32840         }
32841         
32842         var v = this.parseValue(this.getRawValue());
32843         if(v){
32844             this.setValue(v);
32845         }
32846     }
32847     
32848 });
32849
32850  
32851
32852 /*
32853 * Licence: LGPL
32854 */
32855
32856 /**
32857  * @class Roo.bootstrap.DocumentSlider
32858  * @extends Roo.bootstrap.Component
32859  * Bootstrap DocumentSlider class
32860  * 
32861  * @constructor
32862  * Create a new DocumentViewer
32863  * @param {Object} config The config object
32864  */
32865
32866 Roo.bootstrap.DocumentSlider = function(config){
32867     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32868     
32869     this.files = [];
32870     
32871     this.addEvents({
32872         /**
32873          * @event initial
32874          * Fire after initEvent
32875          * @param {Roo.bootstrap.DocumentSlider} this
32876          */
32877         "initial" : true,
32878         /**
32879          * @event update
32880          * Fire after update
32881          * @param {Roo.bootstrap.DocumentSlider} this
32882          */
32883         "update" : true,
32884         /**
32885          * @event click
32886          * Fire after click
32887          * @param {Roo.bootstrap.DocumentSlider} this
32888          */
32889         "click" : true
32890     });
32891 };
32892
32893 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32894     
32895     files : false,
32896     
32897     indicator : 0,
32898     
32899     getAutoCreate : function()
32900     {
32901         var cfg = {
32902             tag : 'div',
32903             cls : 'roo-document-slider',
32904             cn : [
32905                 {
32906                     tag : 'div',
32907                     cls : 'roo-document-slider-header',
32908                     cn : [
32909                         {
32910                             tag : 'div',
32911                             cls : 'roo-document-slider-header-title'
32912                         }
32913                     ]
32914                 },
32915                 {
32916                     tag : 'div',
32917                     cls : 'roo-document-slider-body',
32918                     cn : [
32919                         {
32920                             tag : 'div',
32921                             cls : 'roo-document-slider-prev',
32922                             cn : [
32923                                 {
32924                                     tag : 'i',
32925                                     cls : 'fa fa-chevron-left'
32926                                 }
32927                             ]
32928                         },
32929                         {
32930                             tag : 'div',
32931                             cls : 'roo-document-slider-thumb',
32932                             cn : [
32933                                 {
32934                                     tag : 'img',
32935                                     cls : 'roo-document-slider-image'
32936                                 }
32937                             ]
32938                         },
32939                         {
32940                             tag : 'div',
32941                             cls : 'roo-document-slider-next',
32942                             cn : [
32943                                 {
32944                                     tag : 'i',
32945                                     cls : 'fa fa-chevron-right'
32946                                 }
32947                             ]
32948                         }
32949                     ]
32950                 }
32951             ]
32952         };
32953         
32954         return cfg;
32955     },
32956     
32957     initEvents : function()
32958     {
32959         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32960         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32961         
32962         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32963         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32964         
32965         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32966         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32967         
32968         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32969         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32970         
32971         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32972         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32973         
32974         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32975         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32976         
32977         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32978         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32979         
32980         this.thumbEl.on('click', this.onClick, this);
32981         
32982         this.prevIndicator.on('click', this.prev, this);
32983         
32984         this.nextIndicator.on('click', this.next, this);
32985         
32986     },
32987     
32988     initial : function()
32989     {
32990         if(this.files.length){
32991             this.indicator = 1;
32992             this.update()
32993         }
32994         
32995         this.fireEvent('initial', this);
32996     },
32997     
32998     update : function()
32999     {
33000         this.imageEl.attr('src', this.files[this.indicator - 1]);
33001         
33002         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33003         
33004         this.prevIndicator.show();
33005         
33006         if(this.indicator == 1){
33007             this.prevIndicator.hide();
33008         }
33009         
33010         this.nextIndicator.show();
33011         
33012         if(this.indicator == this.files.length){
33013             this.nextIndicator.hide();
33014         }
33015         
33016         this.thumbEl.scrollTo('top');
33017         
33018         this.fireEvent('update', this);
33019     },
33020     
33021     onClick : function(e)
33022     {
33023         e.preventDefault();
33024         
33025         this.fireEvent('click', this);
33026     },
33027     
33028     prev : function(e)
33029     {
33030         e.preventDefault();
33031         
33032         this.indicator = Math.max(1, this.indicator - 1);
33033         
33034         this.update();
33035     },
33036     
33037     next : function(e)
33038     {
33039         e.preventDefault();
33040         
33041         this.indicator = Math.min(this.files.length, this.indicator + 1);
33042         
33043         this.update();
33044     }
33045 });
33046 /*
33047  * - LGPL
33048  *
33049  * RadioSet
33050  *
33051  *
33052  */
33053
33054 /**
33055  * @class Roo.bootstrap.RadioSet
33056  * @extends Roo.bootstrap.Input
33057  * Bootstrap RadioSet class
33058  * @cfg {String} indicatorpos (left|right) default left
33059  * @cfg {Boolean} inline (true|false) inline the element (default true)
33060  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33061  * @constructor
33062  * Create a new RadioSet
33063  * @param {Object} config The config object
33064  */
33065
33066 Roo.bootstrap.RadioSet = function(config){
33067     
33068     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33069     
33070     this.radioes = [];
33071     
33072     Roo.bootstrap.RadioSet.register(this);
33073     
33074     this.addEvents({
33075         /**
33076         * @event check
33077         * Fires when the element is checked or unchecked.
33078         * @param {Roo.bootstrap.RadioSet} this This radio
33079         * @param {Roo.bootstrap.Radio} item The checked item
33080         */
33081        check : true
33082     });
33083     
33084 };
33085
33086 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33087
33088     radioes : false,
33089     
33090     inline : true,
33091     
33092     weight : '',
33093     
33094     indicatorpos : 'left',
33095     
33096     getAutoCreate : function()
33097     {
33098         var label = {
33099             tag : 'label',
33100             cls : 'roo-radio-set-label',
33101             cn : [
33102                 {
33103                     tag : 'span',
33104                     html : this.fieldLabel
33105                 }
33106             ]
33107         };
33108         
33109         if(this.indicatorpos == 'left'){
33110             label.cn.unshift({
33111                 tag : 'i',
33112                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33113                 tooltip : 'This field is required'
33114             });
33115         } else {
33116             label.cn.push({
33117                 tag : 'i',
33118                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33119                 tooltip : 'This field is required'
33120             });
33121         }
33122         
33123         var items = {
33124             tag : 'div',
33125             cls : 'roo-radio-set-items'
33126         };
33127         
33128         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33129         
33130         if (align === 'left' && this.fieldLabel.length) {
33131             
33132             items = {
33133                 cls : "roo-radio-set-right", 
33134                 cn: [
33135                     items
33136                 ]
33137             };
33138             
33139             if(this.labelWidth > 12){
33140                 label.style = "width: " + this.labelWidth + 'px';
33141             }
33142             
33143             if(this.labelWidth < 13 && this.labelmd == 0){
33144                 this.labelmd = this.labelWidth;
33145             }
33146             
33147             if(this.labellg > 0){
33148                 label.cls += ' col-lg-' + this.labellg;
33149                 items.cls += ' col-lg-' + (12 - this.labellg);
33150             }
33151             
33152             if(this.labelmd > 0){
33153                 label.cls += ' col-md-' + this.labelmd;
33154                 items.cls += ' col-md-' + (12 - this.labelmd);
33155             }
33156             
33157             if(this.labelsm > 0){
33158                 label.cls += ' col-sm-' + this.labelsm;
33159                 items.cls += ' col-sm-' + (12 - this.labelsm);
33160             }
33161             
33162             if(this.labelxs > 0){
33163                 label.cls += ' col-xs-' + this.labelxs;
33164                 items.cls += ' col-xs-' + (12 - this.labelxs);
33165             }
33166         }
33167         
33168         var cfg = {
33169             tag : 'div',
33170             cls : 'roo-radio-set',
33171             cn : [
33172                 {
33173                     tag : 'input',
33174                     cls : 'roo-radio-set-input',
33175                     type : 'hidden',
33176                     name : this.name,
33177                     value : this.value ? this.value :  ''
33178                 },
33179                 label,
33180                 items
33181             ]
33182         };
33183         
33184         if(this.weight.length){
33185             cfg.cls += ' roo-radio-' + this.weight;
33186         }
33187         
33188         if(this.inline) {
33189             cfg.cls += ' roo-radio-set-inline';
33190         }
33191         
33192         return cfg;
33193         
33194     },
33195
33196     initEvents : function()
33197     {
33198         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33199         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33200         
33201         if(!this.fieldLabel.length){
33202             this.labelEl.hide();
33203         }
33204         
33205         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33206         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33207         
33208         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33209         this.indicatorEl().hide();
33210         
33211         this.originalValue = this.getValue();
33212         
33213     },
33214     
33215     inputEl: function ()
33216     {
33217         return this.el.select('.roo-radio-set-input', true).first();
33218     },
33219     
33220     getChildContainer : function()
33221     {
33222         return this.itemsEl;
33223     },
33224     
33225     register : function(item)
33226     {
33227         this.radioes.push(item);
33228         
33229     },
33230     
33231     validate : function()
33232     {   
33233         var valid = false;
33234         
33235         Roo.each(this.radioes, function(i){
33236             if(!i.checked){
33237                 return;
33238             }
33239             
33240             valid = true;
33241             return false;
33242         });
33243         
33244         if(this.allowBlank) {
33245             return true;
33246         }
33247         
33248         if(this.disabled || valid){
33249             this.markValid();
33250             return true;
33251         }
33252         
33253         this.markInvalid();
33254         return false;
33255         
33256     },
33257     
33258     markValid : function()
33259     {
33260         if(this.labelEl.isVisible(true)){
33261             this.indicatorEl().hide();
33262         }
33263         
33264         this.el.removeClass([this.invalidClass, this.validClass]);
33265         this.el.addClass(this.validClass);
33266         
33267         this.fireEvent('valid', this);
33268     },
33269     
33270     markInvalid : function(msg)
33271     {
33272         if(this.allowBlank || this.disabled){
33273             return;
33274         }
33275         
33276         if(this.labelEl.isVisible(true)){
33277             this.indicatorEl().show();
33278         }
33279         
33280         this.el.removeClass([this.invalidClass, this.validClass]);
33281         this.el.addClass(this.invalidClass);
33282         
33283         this.fireEvent('invalid', this, msg);
33284         
33285     },
33286     
33287     setValue : function(v, suppressEvent)
33288     {   
33289         Roo.each(this.radioes, function(i){
33290             
33291             i.checked = false;
33292             i.el.removeClass('checked');
33293             
33294             if(i.value === v || i.value.toString() === v.toString()){
33295                 i.checked = true;
33296                 i.el.addClass('checked');
33297                 
33298                 if(suppressEvent !== true){
33299                     this.fireEvent('check', this, i);
33300                 }
33301             }
33302             
33303         }, this);
33304         
33305         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
33306         
33307     },
33308     
33309     clearInvalid : function(){
33310         
33311         if(!this.el || this.preventMark){
33312             return;
33313         }
33314          
33315         
33316         this.el.removeClass([this.invalidClass]);
33317         
33318         this.fireEvent('valid', this);
33319     }
33320     
33321 });
33322
33323 Roo.apply(Roo.bootstrap.RadioSet, {
33324     
33325     groups: {},
33326     
33327     register : function(set)
33328     {
33329         this.groups[set.name] = set;
33330     },
33331     
33332     get: function(name) 
33333     {
33334         if (typeof(this.groups[name]) == 'undefined') {
33335             return false;
33336         }
33337         
33338         return this.groups[name] ;
33339     }
33340     
33341 });
33342 /*
33343  * Based on:
33344  * Ext JS Library 1.1.1
33345  * Copyright(c) 2006-2007, Ext JS, LLC.
33346  *
33347  * Originally Released Under LGPL - original licence link has changed is not relivant.
33348  *
33349  * Fork - LGPL
33350  * <script type="text/javascript">
33351  */
33352
33353
33354 /**
33355  * @class Roo.bootstrap.SplitBar
33356  * @extends Roo.util.Observable
33357  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33358  * <br><br>
33359  * Usage:
33360  * <pre><code>
33361 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33362                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33363 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33364 split.minSize = 100;
33365 split.maxSize = 600;
33366 split.animate = true;
33367 split.on('moved', splitterMoved);
33368 </code></pre>
33369  * @constructor
33370  * Create a new SplitBar
33371  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33372  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33373  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33374  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33375                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33376                         position of the SplitBar).
33377  */
33378 Roo.bootstrap.SplitBar = function(cfg){
33379     
33380     /** @private */
33381     
33382     //{
33383     //  dragElement : elm
33384     //  resizingElement: el,
33385         // optional..
33386     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33387     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33388         // existingProxy ???
33389     //}
33390     
33391     this.el = Roo.get(cfg.dragElement, true);
33392     this.el.dom.unselectable = "on";
33393     /** @private */
33394     this.resizingEl = Roo.get(cfg.resizingElement, true);
33395
33396     /**
33397      * @private
33398      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33399      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33400      * @type Number
33401      */
33402     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33403     
33404     /**
33405      * The minimum size of the resizing element. (Defaults to 0)
33406      * @type Number
33407      */
33408     this.minSize = 0;
33409     
33410     /**
33411      * The maximum size of the resizing element. (Defaults to 2000)
33412      * @type Number
33413      */
33414     this.maxSize = 2000;
33415     
33416     /**
33417      * Whether to animate the transition to the new size
33418      * @type Boolean
33419      */
33420     this.animate = false;
33421     
33422     /**
33423      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33424      * @type Boolean
33425      */
33426     this.useShim = false;
33427     
33428     /** @private */
33429     this.shim = null;
33430     
33431     if(!cfg.existingProxy){
33432         /** @private */
33433         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33434     }else{
33435         this.proxy = Roo.get(cfg.existingProxy).dom;
33436     }
33437     /** @private */
33438     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33439     
33440     /** @private */
33441     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33442     
33443     /** @private */
33444     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33445     
33446     /** @private */
33447     this.dragSpecs = {};
33448     
33449     /**
33450      * @private The adapter to use to positon and resize elements
33451      */
33452     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33453     this.adapter.init(this);
33454     
33455     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33456         /** @private */
33457         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33458         this.el.addClass("roo-splitbar-h");
33459     }else{
33460         /** @private */
33461         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33462         this.el.addClass("roo-splitbar-v");
33463     }
33464     
33465     this.addEvents({
33466         /**
33467          * @event resize
33468          * Fires when the splitter is moved (alias for {@link #event-moved})
33469          * @param {Roo.bootstrap.SplitBar} this
33470          * @param {Number} newSize the new width or height
33471          */
33472         "resize" : true,
33473         /**
33474          * @event moved
33475          * Fires when the splitter is moved
33476          * @param {Roo.bootstrap.SplitBar} this
33477          * @param {Number} newSize the new width or height
33478          */
33479         "moved" : true,
33480         /**
33481          * @event beforeresize
33482          * Fires before the splitter is dragged
33483          * @param {Roo.bootstrap.SplitBar} this
33484          */
33485         "beforeresize" : true,
33486
33487         "beforeapply" : true
33488     });
33489
33490     Roo.util.Observable.call(this);
33491 };
33492
33493 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33494     onStartProxyDrag : function(x, y){
33495         this.fireEvent("beforeresize", this);
33496         if(!this.overlay){
33497             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33498             o.unselectable();
33499             o.enableDisplayMode("block");
33500             // all splitbars share the same overlay
33501             Roo.bootstrap.SplitBar.prototype.overlay = o;
33502         }
33503         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33504         this.overlay.show();
33505         Roo.get(this.proxy).setDisplayed("block");
33506         var size = this.adapter.getElementSize(this);
33507         this.activeMinSize = this.getMinimumSize();;
33508         this.activeMaxSize = this.getMaximumSize();;
33509         var c1 = size - this.activeMinSize;
33510         var c2 = Math.max(this.activeMaxSize - size, 0);
33511         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33512             this.dd.resetConstraints();
33513             this.dd.setXConstraint(
33514                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33515                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33516             );
33517             this.dd.setYConstraint(0, 0);
33518         }else{
33519             this.dd.resetConstraints();
33520             this.dd.setXConstraint(0, 0);
33521             this.dd.setYConstraint(
33522                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33523                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33524             );
33525          }
33526         this.dragSpecs.startSize = size;
33527         this.dragSpecs.startPoint = [x, y];
33528         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33529     },
33530     
33531     /** 
33532      * @private Called after the drag operation by the DDProxy
33533      */
33534     onEndProxyDrag : function(e){
33535         Roo.get(this.proxy).setDisplayed(false);
33536         var endPoint = Roo.lib.Event.getXY(e);
33537         if(this.overlay){
33538             this.overlay.hide();
33539         }
33540         var newSize;
33541         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33542             newSize = this.dragSpecs.startSize + 
33543                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33544                     endPoint[0] - this.dragSpecs.startPoint[0] :
33545                     this.dragSpecs.startPoint[0] - endPoint[0]
33546                 );
33547         }else{
33548             newSize = this.dragSpecs.startSize + 
33549                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33550                     endPoint[1] - this.dragSpecs.startPoint[1] :
33551                     this.dragSpecs.startPoint[1] - endPoint[1]
33552                 );
33553         }
33554         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33555         if(newSize != this.dragSpecs.startSize){
33556             if(this.fireEvent('beforeapply', this, newSize) !== false){
33557                 this.adapter.setElementSize(this, newSize);
33558                 this.fireEvent("moved", this, newSize);
33559                 this.fireEvent("resize", this, newSize);
33560             }
33561         }
33562     },
33563     
33564     /**
33565      * Get the adapter this SplitBar uses
33566      * @return The adapter object
33567      */
33568     getAdapter : function(){
33569         return this.adapter;
33570     },
33571     
33572     /**
33573      * Set the adapter this SplitBar uses
33574      * @param {Object} adapter A SplitBar adapter object
33575      */
33576     setAdapter : function(adapter){
33577         this.adapter = adapter;
33578         this.adapter.init(this);
33579     },
33580     
33581     /**
33582      * Gets the minimum size for the resizing element
33583      * @return {Number} The minimum size
33584      */
33585     getMinimumSize : function(){
33586         return this.minSize;
33587     },
33588     
33589     /**
33590      * Sets the minimum size for the resizing element
33591      * @param {Number} minSize The minimum size
33592      */
33593     setMinimumSize : function(minSize){
33594         this.minSize = minSize;
33595     },
33596     
33597     /**
33598      * Gets the maximum size for the resizing element
33599      * @return {Number} The maximum size
33600      */
33601     getMaximumSize : function(){
33602         return this.maxSize;
33603     },
33604     
33605     /**
33606      * Sets the maximum size for the resizing element
33607      * @param {Number} maxSize The maximum size
33608      */
33609     setMaximumSize : function(maxSize){
33610         this.maxSize = maxSize;
33611     },
33612     
33613     /**
33614      * Sets the initialize size for the resizing element
33615      * @param {Number} size The initial size
33616      */
33617     setCurrentSize : function(size){
33618         var oldAnimate = this.animate;
33619         this.animate = false;
33620         this.adapter.setElementSize(this, size);
33621         this.animate = oldAnimate;
33622     },
33623     
33624     /**
33625      * Destroy this splitbar. 
33626      * @param {Boolean} removeEl True to remove the element
33627      */
33628     destroy : function(removeEl){
33629         if(this.shim){
33630             this.shim.remove();
33631         }
33632         this.dd.unreg();
33633         this.proxy.parentNode.removeChild(this.proxy);
33634         if(removeEl){
33635             this.el.remove();
33636         }
33637     }
33638 });
33639
33640 /**
33641  * @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.
33642  */
33643 Roo.bootstrap.SplitBar.createProxy = function(dir){
33644     var proxy = new Roo.Element(document.createElement("div"));
33645     proxy.unselectable();
33646     var cls = 'roo-splitbar-proxy';
33647     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33648     document.body.appendChild(proxy.dom);
33649     return proxy.dom;
33650 };
33651
33652 /** 
33653  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33654  * Default Adapter. It assumes the splitter and resizing element are not positioned
33655  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33656  */
33657 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33658 };
33659
33660 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33661     // do nothing for now
33662     init : function(s){
33663     
33664     },
33665     /**
33666      * Called before drag operations to get the current size of the resizing element. 
33667      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33668      */
33669      getElementSize : function(s){
33670         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33671             return s.resizingEl.getWidth();
33672         }else{
33673             return s.resizingEl.getHeight();
33674         }
33675     },
33676     
33677     /**
33678      * Called after drag operations to set the size of the resizing element.
33679      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33680      * @param {Number} newSize The new size to set
33681      * @param {Function} onComplete A function to be invoked when resizing is complete
33682      */
33683     setElementSize : function(s, newSize, onComplete){
33684         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33685             if(!s.animate){
33686                 s.resizingEl.setWidth(newSize);
33687                 if(onComplete){
33688                     onComplete(s, newSize);
33689                 }
33690             }else{
33691                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33692             }
33693         }else{
33694             
33695             if(!s.animate){
33696                 s.resizingEl.setHeight(newSize);
33697                 if(onComplete){
33698                     onComplete(s, newSize);
33699                 }
33700             }else{
33701                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33702             }
33703         }
33704     }
33705 };
33706
33707 /** 
33708  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33709  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33710  * Adapter that  moves the splitter element to align with the resized sizing element. 
33711  * Used with an absolute positioned SplitBar.
33712  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33713  * document.body, make sure you assign an id to the body element.
33714  */
33715 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33716     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33717     this.container = Roo.get(container);
33718 };
33719
33720 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33721     init : function(s){
33722         this.basic.init(s);
33723     },
33724     
33725     getElementSize : function(s){
33726         return this.basic.getElementSize(s);
33727     },
33728     
33729     setElementSize : function(s, newSize, onComplete){
33730         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33731     },
33732     
33733     moveSplitter : function(s){
33734         var yes = Roo.bootstrap.SplitBar;
33735         switch(s.placement){
33736             case yes.LEFT:
33737                 s.el.setX(s.resizingEl.getRight());
33738                 break;
33739             case yes.RIGHT:
33740                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33741                 break;
33742             case yes.TOP:
33743                 s.el.setY(s.resizingEl.getBottom());
33744                 break;
33745             case yes.BOTTOM:
33746                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33747                 break;
33748         }
33749     }
33750 };
33751
33752 /**
33753  * Orientation constant - Create a vertical SplitBar
33754  * @static
33755  * @type Number
33756  */
33757 Roo.bootstrap.SplitBar.VERTICAL = 1;
33758
33759 /**
33760  * Orientation constant - Create a horizontal SplitBar
33761  * @static
33762  * @type Number
33763  */
33764 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33765
33766 /**
33767  * Placement constant - The resizing element is to the left of the splitter element
33768  * @static
33769  * @type Number
33770  */
33771 Roo.bootstrap.SplitBar.LEFT = 1;
33772
33773 /**
33774  * Placement constant - The resizing element is to the right of the splitter element
33775  * @static
33776  * @type Number
33777  */
33778 Roo.bootstrap.SplitBar.RIGHT = 2;
33779
33780 /**
33781  * Placement constant - The resizing element is positioned above the splitter element
33782  * @static
33783  * @type Number
33784  */
33785 Roo.bootstrap.SplitBar.TOP = 3;
33786
33787 /**
33788  * Placement constant - The resizing element is positioned under splitter element
33789  * @static
33790  * @type Number
33791  */
33792 Roo.bootstrap.SplitBar.BOTTOM = 4;
33793 Roo.namespace("Roo.bootstrap.layout");/*
33794  * Based on:
33795  * Ext JS Library 1.1.1
33796  * Copyright(c) 2006-2007, Ext JS, LLC.
33797  *
33798  * Originally Released Under LGPL - original licence link has changed is not relivant.
33799  *
33800  * Fork - LGPL
33801  * <script type="text/javascript">
33802  */
33803
33804 /**
33805  * @class Roo.bootstrap.layout.Manager
33806  * @extends Roo.bootstrap.Component
33807  * Base class for layout managers.
33808  */
33809 Roo.bootstrap.layout.Manager = function(config)
33810 {
33811     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33812
33813
33814
33815
33816
33817     /** false to disable window resize monitoring @type Boolean */
33818     this.monitorWindowResize = true;
33819     this.regions = {};
33820     this.addEvents({
33821         /**
33822          * @event layout
33823          * Fires when a layout is performed.
33824          * @param {Roo.LayoutManager} this
33825          */
33826         "layout" : true,
33827         /**
33828          * @event regionresized
33829          * Fires when the user resizes a region.
33830          * @param {Roo.LayoutRegion} region The resized region
33831          * @param {Number} newSize The new size (width for east/west, height for north/south)
33832          */
33833         "regionresized" : true,
33834         /**
33835          * @event regioncollapsed
33836          * Fires when a region is collapsed.
33837          * @param {Roo.LayoutRegion} region The collapsed region
33838          */
33839         "regioncollapsed" : true,
33840         /**
33841          * @event regionexpanded
33842          * Fires when a region is expanded.
33843          * @param {Roo.LayoutRegion} region The expanded region
33844          */
33845         "regionexpanded" : true
33846     });
33847     this.updating = false;
33848
33849     if (config.el) {
33850         this.el = Roo.get(config.el);
33851         this.initEvents();
33852     }
33853
33854 };
33855
33856 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33857
33858
33859     regions : null,
33860
33861     monitorWindowResize : true,
33862
33863
33864     updating : false,
33865
33866
33867     onRender : function(ct, position)
33868     {
33869         if(!this.el){
33870             this.el = Roo.get(ct);
33871             this.initEvents();
33872         }
33873         //this.fireEvent('render',this);
33874     },
33875
33876
33877     initEvents: function()
33878     {
33879
33880
33881         // ie scrollbar fix
33882         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33883             document.body.scroll = "no";
33884         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33885             this.el.position('relative');
33886         }
33887         this.id = this.el.id;
33888         this.el.addClass("roo-layout-container");
33889         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33890         if(this.el.dom != document.body ) {
33891             this.el.on('resize', this.layout,this);
33892             this.el.on('show', this.layout,this);
33893         }
33894
33895     },
33896
33897     /**
33898      * Returns true if this layout is currently being updated
33899      * @return {Boolean}
33900      */
33901     isUpdating : function(){
33902         return this.updating;
33903     },
33904
33905     /**
33906      * Suspend the LayoutManager from doing auto-layouts while
33907      * making multiple add or remove calls
33908      */
33909     beginUpdate : function(){
33910         this.updating = true;
33911     },
33912
33913     /**
33914      * Restore auto-layouts and optionally disable the manager from performing a layout
33915      * @param {Boolean} noLayout true to disable a layout update
33916      */
33917     endUpdate : function(noLayout){
33918         this.updating = false;
33919         if(!noLayout){
33920             this.layout();
33921         }
33922     },
33923
33924     layout: function(){
33925         // abstract...
33926     },
33927
33928     onRegionResized : function(region, newSize){
33929         this.fireEvent("regionresized", region, newSize);
33930         this.layout();
33931     },
33932
33933     onRegionCollapsed : function(region){
33934         this.fireEvent("regioncollapsed", region);
33935     },
33936
33937     onRegionExpanded : function(region){
33938         this.fireEvent("regionexpanded", region);
33939     },
33940
33941     /**
33942      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33943      * performs box-model adjustments.
33944      * @return {Object} The size as an object {width: (the width), height: (the height)}
33945      */
33946     getViewSize : function()
33947     {
33948         var size;
33949         if(this.el.dom != document.body){
33950             size = this.el.getSize();
33951         }else{
33952             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33953         }
33954         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33955         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33956         return size;
33957     },
33958
33959     /**
33960      * Returns the Element this layout is bound to.
33961      * @return {Roo.Element}
33962      */
33963     getEl : function(){
33964         return this.el;
33965     },
33966
33967     /**
33968      * Returns the specified region.
33969      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33970      * @return {Roo.LayoutRegion}
33971      */
33972     getRegion : function(target){
33973         return this.regions[target.toLowerCase()];
33974     },
33975
33976     onWindowResize : function(){
33977         if(this.monitorWindowResize){
33978             this.layout();
33979         }
33980     }
33981 });
33982 /*
33983  * Based on:
33984  * Ext JS Library 1.1.1
33985  * Copyright(c) 2006-2007, Ext JS, LLC.
33986  *
33987  * Originally Released Under LGPL - original licence link has changed is not relivant.
33988  *
33989  * Fork - LGPL
33990  * <script type="text/javascript">
33991  */
33992 /**
33993  * @class Roo.bootstrap.layout.Border
33994  * @extends Roo.bootstrap.layout.Manager
33995  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33996  * please see: examples/bootstrap/nested.html<br><br>
33997  
33998 <b>The container the layout is rendered into can be either the body element or any other element.
33999 If it is not the body element, the container needs to either be an absolute positioned element,
34000 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34001 the container size if it is not the body element.</b>
34002
34003 * @constructor
34004 * Create a new Border
34005 * @param {Object} config Configuration options
34006  */
34007 Roo.bootstrap.layout.Border = function(config){
34008     config = config || {};
34009     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34010     
34011     
34012     
34013     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34014         if(config[region]){
34015             config[region].region = region;
34016             this.addRegion(config[region]);
34017         }
34018     },this);
34019     
34020 };
34021
34022 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34023
34024 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34025     /**
34026      * Creates and adds a new region if it doesn't already exist.
34027      * @param {String} target The target region key (north, south, east, west or center).
34028      * @param {Object} config The regions config object
34029      * @return {BorderLayoutRegion} The new region
34030      */
34031     addRegion : function(config)
34032     {
34033         if(!this.regions[config.region]){
34034             var r = this.factory(config);
34035             this.bindRegion(r);
34036         }
34037         return this.regions[config.region];
34038     },
34039
34040     // private (kinda)
34041     bindRegion : function(r){
34042         this.regions[r.config.region] = r;
34043         
34044         r.on("visibilitychange",    this.layout, this);
34045         r.on("paneladded",          this.layout, this);
34046         r.on("panelremoved",        this.layout, this);
34047         r.on("invalidated",         this.layout, this);
34048         r.on("resized",             this.onRegionResized, this);
34049         r.on("collapsed",           this.onRegionCollapsed, this);
34050         r.on("expanded",            this.onRegionExpanded, this);
34051     },
34052
34053     /**
34054      * Performs a layout update.
34055      */
34056     layout : function()
34057     {
34058         if(this.updating) {
34059             return;
34060         }
34061         
34062         // render all the rebions if they have not been done alreayd?
34063         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34064             if(this.regions[region] && !this.regions[region].bodyEl){
34065                 this.regions[region].onRender(this.el)
34066             }
34067         },this);
34068         
34069         var size = this.getViewSize();
34070         var w = size.width;
34071         var h = size.height;
34072         var centerW = w;
34073         var centerH = h;
34074         var centerY = 0;
34075         var centerX = 0;
34076         //var x = 0, y = 0;
34077
34078         var rs = this.regions;
34079         var north = rs["north"];
34080         var south = rs["south"]; 
34081         var west = rs["west"];
34082         var east = rs["east"];
34083         var center = rs["center"];
34084         //if(this.hideOnLayout){ // not supported anymore
34085             //c.el.setStyle("display", "none");
34086         //}
34087         if(north && north.isVisible()){
34088             var b = north.getBox();
34089             var m = north.getMargins();
34090             b.width = w - (m.left+m.right);
34091             b.x = m.left;
34092             b.y = m.top;
34093             centerY = b.height + b.y + m.bottom;
34094             centerH -= centerY;
34095             north.updateBox(this.safeBox(b));
34096         }
34097         if(south && south.isVisible()){
34098             var b = south.getBox();
34099             var m = south.getMargins();
34100             b.width = w - (m.left+m.right);
34101             b.x = m.left;
34102             var totalHeight = (b.height + m.top + m.bottom);
34103             b.y = h - totalHeight + m.top;
34104             centerH -= totalHeight;
34105             south.updateBox(this.safeBox(b));
34106         }
34107         if(west && west.isVisible()){
34108             var b = west.getBox();
34109             var m = west.getMargins();
34110             b.height = centerH - (m.top+m.bottom);
34111             b.x = m.left;
34112             b.y = centerY + m.top;
34113             var totalWidth = (b.width + m.left + m.right);
34114             centerX += totalWidth;
34115             centerW -= totalWidth;
34116             west.updateBox(this.safeBox(b));
34117         }
34118         if(east && east.isVisible()){
34119             var b = east.getBox();
34120             var m = east.getMargins();
34121             b.height = centerH - (m.top+m.bottom);
34122             var totalWidth = (b.width + m.left + m.right);
34123             b.x = w - totalWidth + m.left;
34124             b.y = centerY + m.top;
34125             centerW -= totalWidth;
34126             east.updateBox(this.safeBox(b));
34127         }
34128         if(center){
34129             var m = center.getMargins();
34130             var centerBox = {
34131                 x: centerX + m.left,
34132                 y: centerY + m.top,
34133                 width: centerW - (m.left+m.right),
34134                 height: centerH - (m.top+m.bottom)
34135             };
34136             //if(this.hideOnLayout){
34137                 //center.el.setStyle("display", "block");
34138             //}
34139             center.updateBox(this.safeBox(centerBox));
34140         }
34141         this.el.repaint();
34142         this.fireEvent("layout", this);
34143     },
34144
34145     // private
34146     safeBox : function(box){
34147         box.width = Math.max(0, box.width);
34148         box.height = Math.max(0, box.height);
34149         return box;
34150     },
34151
34152     /**
34153      * Adds a ContentPanel (or subclass) to this layout.
34154      * @param {String} target The target region key (north, south, east, west or center).
34155      * @param {Roo.ContentPanel} panel The panel to add
34156      * @return {Roo.ContentPanel} The added panel
34157      */
34158     add : function(target, panel){
34159          
34160         target = target.toLowerCase();
34161         return this.regions[target].add(panel);
34162     },
34163
34164     /**
34165      * Remove a ContentPanel (or subclass) to this layout.
34166      * @param {String} target The target region key (north, south, east, west or center).
34167      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34168      * @return {Roo.ContentPanel} The removed panel
34169      */
34170     remove : function(target, panel){
34171         target = target.toLowerCase();
34172         return this.regions[target].remove(panel);
34173     },
34174
34175     /**
34176      * Searches all regions for a panel with the specified id
34177      * @param {String} panelId
34178      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34179      */
34180     findPanel : function(panelId){
34181         var rs = this.regions;
34182         for(var target in rs){
34183             if(typeof rs[target] != "function"){
34184                 var p = rs[target].getPanel(panelId);
34185                 if(p){
34186                     return p;
34187                 }
34188             }
34189         }
34190         return null;
34191     },
34192
34193     /**
34194      * Searches all regions for a panel with the specified id and activates (shows) it.
34195      * @param {String/ContentPanel} panelId The panels id or the panel itself
34196      * @return {Roo.ContentPanel} The shown panel or null
34197      */
34198     showPanel : function(panelId) {
34199       var rs = this.regions;
34200       for(var target in rs){
34201          var r = rs[target];
34202          if(typeof r != "function"){
34203             if(r.hasPanel(panelId)){
34204                return r.showPanel(panelId);
34205             }
34206          }
34207       }
34208       return null;
34209    },
34210
34211    /**
34212      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34213      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34214      */
34215    /*
34216     restoreState : function(provider){
34217         if(!provider){
34218             provider = Roo.state.Manager;
34219         }
34220         var sm = new Roo.LayoutStateManager();
34221         sm.init(this, provider);
34222     },
34223 */
34224  
34225  
34226     /**
34227      * Adds a xtype elements to the layout.
34228      * <pre><code>
34229
34230 layout.addxtype({
34231        xtype : 'ContentPanel',
34232        region: 'west',
34233        items: [ .... ]
34234    }
34235 );
34236
34237 layout.addxtype({
34238         xtype : 'NestedLayoutPanel',
34239         region: 'west',
34240         layout: {
34241            center: { },
34242            west: { }   
34243         },
34244         items : [ ... list of content panels or nested layout panels.. ]
34245    }
34246 );
34247 </code></pre>
34248      * @param {Object} cfg Xtype definition of item to add.
34249      */
34250     addxtype : function(cfg)
34251     {
34252         // basically accepts a pannel...
34253         // can accept a layout region..!?!?
34254         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34255         
34256         
34257         // theory?  children can only be panels??
34258         
34259         //if (!cfg.xtype.match(/Panel$/)) {
34260         //    return false;
34261         //}
34262         var ret = false;
34263         
34264         if (typeof(cfg.region) == 'undefined') {
34265             Roo.log("Failed to add Panel, region was not set");
34266             Roo.log(cfg);
34267             return false;
34268         }
34269         var region = cfg.region;
34270         delete cfg.region;
34271         
34272           
34273         var xitems = [];
34274         if (cfg.items) {
34275             xitems = cfg.items;
34276             delete cfg.items;
34277         }
34278         var nb = false;
34279         
34280         switch(cfg.xtype) 
34281         {
34282             case 'Content':  // ContentPanel (el, cfg)
34283             case 'Scroll':  // ContentPanel (el, cfg)
34284             case 'View': 
34285                 cfg.autoCreate = true;
34286                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34287                 //} else {
34288                 //    var el = this.el.createChild();
34289                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34290                 //}
34291                 
34292                 this.add(region, ret);
34293                 break;
34294             
34295             /*
34296             case 'TreePanel': // our new panel!
34297                 cfg.el = this.el.createChild();
34298                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34299                 this.add(region, ret);
34300                 break;
34301             */
34302             
34303             case 'Nest': 
34304                 // create a new Layout (which is  a Border Layout...
34305                 
34306                 var clayout = cfg.layout;
34307                 clayout.el  = this.el.createChild();
34308                 clayout.items   = clayout.items  || [];
34309                 
34310                 delete cfg.layout;
34311                 
34312                 // replace this exitems with the clayout ones..
34313                 xitems = clayout.items;
34314                  
34315                 // force background off if it's in center...
34316                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34317                     cfg.background = false;
34318                 }
34319                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34320                 
34321                 
34322                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34323                 //console.log('adding nested layout panel '  + cfg.toSource());
34324                 this.add(region, ret);
34325                 nb = {}; /// find first...
34326                 break;
34327             
34328             case 'Grid':
34329                 
34330                 // needs grid and region
34331                 
34332                 //var el = this.getRegion(region).el.createChild();
34333                 /*
34334                  *var el = this.el.createChild();
34335                 // create the grid first...
34336                 cfg.grid.container = el;
34337                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34338                 */
34339                 
34340                 if (region == 'center' && this.active ) {
34341                     cfg.background = false;
34342                 }
34343                 
34344                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34345                 
34346                 this.add(region, ret);
34347                 /*
34348                 if (cfg.background) {
34349                     // render grid on panel activation (if panel background)
34350                     ret.on('activate', function(gp) {
34351                         if (!gp.grid.rendered) {
34352                     //        gp.grid.render(el);
34353                         }
34354                     });
34355                 } else {
34356                   //  cfg.grid.render(el);
34357                 }
34358                 */
34359                 break;
34360            
34361            
34362             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34363                 // it was the old xcomponent building that caused this before.
34364                 // espeically if border is the top element in the tree.
34365                 ret = this;
34366                 break; 
34367                 
34368                     
34369                 
34370                 
34371                 
34372             default:
34373                 /*
34374                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34375                     
34376                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34377                     this.add(region, ret);
34378                 } else {
34379                 */
34380                     Roo.log(cfg);
34381                     throw "Can not add '" + cfg.xtype + "' to Border";
34382                     return null;
34383              
34384                                 
34385              
34386         }
34387         this.beginUpdate();
34388         // add children..
34389         var region = '';
34390         var abn = {};
34391         Roo.each(xitems, function(i)  {
34392             region = nb && i.region ? i.region : false;
34393             
34394             var add = ret.addxtype(i);
34395            
34396             if (region) {
34397                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34398                 if (!i.background) {
34399                     abn[region] = nb[region] ;
34400                 }
34401             }
34402             
34403         });
34404         this.endUpdate();
34405
34406         // make the last non-background panel active..
34407         //if (nb) { Roo.log(abn); }
34408         if (nb) {
34409             
34410             for(var r in abn) {
34411                 region = this.getRegion(r);
34412                 if (region) {
34413                     // tried using nb[r], but it does not work..
34414                      
34415                     region.showPanel(abn[r]);
34416                    
34417                 }
34418             }
34419         }
34420         return ret;
34421         
34422     },
34423     
34424     
34425 // private
34426     factory : function(cfg)
34427     {
34428         
34429         var validRegions = Roo.bootstrap.layout.Border.regions;
34430
34431         var target = cfg.region;
34432         cfg.mgr = this;
34433         
34434         var r = Roo.bootstrap.layout;
34435         Roo.log(target);
34436         switch(target){
34437             case "north":
34438                 return new r.North(cfg);
34439             case "south":
34440                 return new r.South(cfg);
34441             case "east":
34442                 return new r.East(cfg);
34443             case "west":
34444                 return new r.West(cfg);
34445             case "center":
34446                 return new r.Center(cfg);
34447         }
34448         throw 'Layout region "'+target+'" not supported.';
34449     }
34450     
34451     
34452 });
34453  /*
34454  * Based on:
34455  * Ext JS Library 1.1.1
34456  * Copyright(c) 2006-2007, Ext JS, LLC.
34457  *
34458  * Originally Released Under LGPL - original licence link has changed is not relivant.
34459  *
34460  * Fork - LGPL
34461  * <script type="text/javascript">
34462  */
34463  
34464 /**
34465  * @class Roo.bootstrap.layout.Basic
34466  * @extends Roo.util.Observable
34467  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34468  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34469  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34470  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34471  * @cfg {string}   region  the region that it inhabits..
34472  * @cfg {bool}   skipConfig skip config?
34473  * 
34474
34475  */
34476 Roo.bootstrap.layout.Basic = function(config){
34477     
34478     this.mgr = config.mgr;
34479     
34480     this.position = config.region;
34481     
34482     var skipConfig = config.skipConfig;
34483     
34484     this.events = {
34485         /**
34486          * @scope Roo.BasicLayoutRegion
34487          */
34488         
34489         /**
34490          * @event beforeremove
34491          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34492          * @param {Roo.LayoutRegion} this
34493          * @param {Roo.ContentPanel} panel The panel
34494          * @param {Object} e The cancel event object
34495          */
34496         "beforeremove" : true,
34497         /**
34498          * @event invalidated
34499          * Fires when the layout for this region is changed.
34500          * @param {Roo.LayoutRegion} this
34501          */
34502         "invalidated" : true,
34503         /**
34504          * @event visibilitychange
34505          * Fires when this region is shown or hidden 
34506          * @param {Roo.LayoutRegion} this
34507          * @param {Boolean} visibility true or false
34508          */
34509         "visibilitychange" : true,
34510         /**
34511          * @event paneladded
34512          * Fires when a panel is added. 
34513          * @param {Roo.LayoutRegion} this
34514          * @param {Roo.ContentPanel} panel The panel
34515          */
34516         "paneladded" : true,
34517         /**
34518          * @event panelremoved
34519          * Fires when a panel is removed. 
34520          * @param {Roo.LayoutRegion} this
34521          * @param {Roo.ContentPanel} panel The panel
34522          */
34523         "panelremoved" : true,
34524         /**
34525          * @event beforecollapse
34526          * Fires when this region before collapse.
34527          * @param {Roo.LayoutRegion} this
34528          */
34529         "beforecollapse" : true,
34530         /**
34531          * @event collapsed
34532          * Fires when this region is collapsed.
34533          * @param {Roo.LayoutRegion} this
34534          */
34535         "collapsed" : true,
34536         /**
34537          * @event expanded
34538          * Fires when this region is expanded.
34539          * @param {Roo.LayoutRegion} this
34540          */
34541         "expanded" : true,
34542         /**
34543          * @event slideshow
34544          * Fires when this region is slid into view.
34545          * @param {Roo.LayoutRegion} this
34546          */
34547         "slideshow" : true,
34548         /**
34549          * @event slidehide
34550          * Fires when this region slides out of view. 
34551          * @param {Roo.LayoutRegion} this
34552          */
34553         "slidehide" : true,
34554         /**
34555          * @event panelactivated
34556          * Fires when a panel is activated. 
34557          * @param {Roo.LayoutRegion} this
34558          * @param {Roo.ContentPanel} panel The activated panel
34559          */
34560         "panelactivated" : true,
34561         /**
34562          * @event resized
34563          * Fires when the user resizes this region. 
34564          * @param {Roo.LayoutRegion} this
34565          * @param {Number} newSize The new size (width for east/west, height for north/south)
34566          */
34567         "resized" : true
34568     };
34569     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34570     this.panels = new Roo.util.MixedCollection();
34571     this.panels.getKey = this.getPanelId.createDelegate(this);
34572     this.box = null;
34573     this.activePanel = null;
34574     // ensure listeners are added...
34575     
34576     if (config.listeners || config.events) {
34577         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34578             listeners : config.listeners || {},
34579             events : config.events || {}
34580         });
34581     }
34582     
34583     if(skipConfig !== true){
34584         this.applyConfig(config);
34585     }
34586 };
34587
34588 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34589 {
34590     getPanelId : function(p){
34591         return p.getId();
34592     },
34593     
34594     applyConfig : function(config){
34595         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34596         this.config = config;
34597         
34598     },
34599     
34600     /**
34601      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34602      * the width, for horizontal (north, south) the height.
34603      * @param {Number} newSize The new width or height
34604      */
34605     resizeTo : function(newSize){
34606         var el = this.el ? this.el :
34607                  (this.activePanel ? this.activePanel.getEl() : null);
34608         if(el){
34609             switch(this.position){
34610                 case "east":
34611                 case "west":
34612                     el.setWidth(newSize);
34613                     this.fireEvent("resized", this, newSize);
34614                 break;
34615                 case "north":
34616                 case "south":
34617                     el.setHeight(newSize);
34618                     this.fireEvent("resized", this, newSize);
34619                 break;                
34620             }
34621         }
34622     },
34623     
34624     getBox : function(){
34625         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34626     },
34627     
34628     getMargins : function(){
34629         return this.margins;
34630     },
34631     
34632     updateBox : function(box){
34633         this.box = box;
34634         var el = this.activePanel.getEl();
34635         el.dom.style.left = box.x + "px";
34636         el.dom.style.top = box.y + "px";
34637         this.activePanel.setSize(box.width, box.height);
34638     },
34639     
34640     /**
34641      * Returns the container element for this region.
34642      * @return {Roo.Element}
34643      */
34644     getEl : function(){
34645         return this.activePanel;
34646     },
34647     
34648     /**
34649      * Returns true if this region is currently visible.
34650      * @return {Boolean}
34651      */
34652     isVisible : function(){
34653         return this.activePanel ? true : false;
34654     },
34655     
34656     setActivePanel : function(panel){
34657         panel = this.getPanel(panel);
34658         if(this.activePanel && this.activePanel != panel){
34659             this.activePanel.setActiveState(false);
34660             this.activePanel.getEl().setLeftTop(-10000,-10000);
34661         }
34662         this.activePanel = panel;
34663         panel.setActiveState(true);
34664         if(this.box){
34665             panel.setSize(this.box.width, this.box.height);
34666         }
34667         this.fireEvent("panelactivated", this, panel);
34668         this.fireEvent("invalidated");
34669     },
34670     
34671     /**
34672      * Show the specified panel.
34673      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34674      * @return {Roo.ContentPanel} The shown panel or null
34675      */
34676     showPanel : function(panel){
34677         panel = this.getPanel(panel);
34678         if(panel){
34679             this.setActivePanel(panel);
34680         }
34681         return panel;
34682     },
34683     
34684     /**
34685      * Get the active panel for this region.
34686      * @return {Roo.ContentPanel} The active panel or null
34687      */
34688     getActivePanel : function(){
34689         return this.activePanel;
34690     },
34691     
34692     /**
34693      * Add the passed ContentPanel(s)
34694      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34695      * @return {Roo.ContentPanel} The panel added (if only one was added)
34696      */
34697     add : function(panel){
34698         if(arguments.length > 1){
34699             for(var i = 0, len = arguments.length; i < len; i++) {
34700                 this.add(arguments[i]);
34701             }
34702             return null;
34703         }
34704         if(this.hasPanel(panel)){
34705             this.showPanel(panel);
34706             return panel;
34707         }
34708         var el = panel.getEl();
34709         if(el.dom.parentNode != this.mgr.el.dom){
34710             this.mgr.el.dom.appendChild(el.dom);
34711         }
34712         if(panel.setRegion){
34713             panel.setRegion(this);
34714         }
34715         this.panels.add(panel);
34716         el.setStyle("position", "absolute");
34717         if(!panel.background){
34718             this.setActivePanel(panel);
34719             if(this.config.initialSize && this.panels.getCount()==1){
34720                 this.resizeTo(this.config.initialSize);
34721             }
34722         }
34723         this.fireEvent("paneladded", this, panel);
34724         return panel;
34725     },
34726     
34727     /**
34728      * Returns true if the panel is in this region.
34729      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34730      * @return {Boolean}
34731      */
34732     hasPanel : function(panel){
34733         if(typeof panel == "object"){ // must be panel obj
34734             panel = panel.getId();
34735         }
34736         return this.getPanel(panel) ? true : false;
34737     },
34738     
34739     /**
34740      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34741      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34742      * @param {Boolean} preservePanel Overrides the config preservePanel option
34743      * @return {Roo.ContentPanel} The panel that was removed
34744      */
34745     remove : function(panel, preservePanel){
34746         panel = this.getPanel(panel);
34747         if(!panel){
34748             return null;
34749         }
34750         var e = {};
34751         this.fireEvent("beforeremove", this, panel, e);
34752         if(e.cancel === true){
34753             return null;
34754         }
34755         var panelId = panel.getId();
34756         this.panels.removeKey(panelId);
34757         return panel;
34758     },
34759     
34760     /**
34761      * Returns the panel specified or null if it's not in this region.
34762      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34763      * @return {Roo.ContentPanel}
34764      */
34765     getPanel : function(id){
34766         if(typeof id == "object"){ // must be panel obj
34767             return id;
34768         }
34769         return this.panels.get(id);
34770     },
34771     
34772     /**
34773      * Returns this regions position (north/south/east/west/center).
34774      * @return {String} 
34775      */
34776     getPosition: function(){
34777         return this.position;    
34778     }
34779 });/*
34780  * Based on:
34781  * Ext JS Library 1.1.1
34782  * Copyright(c) 2006-2007, Ext JS, LLC.
34783  *
34784  * Originally Released Under LGPL - original licence link has changed is not relivant.
34785  *
34786  * Fork - LGPL
34787  * <script type="text/javascript">
34788  */
34789  
34790 /**
34791  * @class Roo.bootstrap.layout.Region
34792  * @extends Roo.bootstrap.layout.Basic
34793  * This class represents a region in a layout manager.
34794  
34795  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34796  * @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})
34797  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34798  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34799  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34800  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34801  * @cfg {String}    title           The title for the region (overrides panel titles)
34802  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34803  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34804  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34805  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34806  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34807  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34808  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34809  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34810  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34811  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34812
34813  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34814  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34815  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34816  * @cfg {Number}    width           For East/West panels
34817  * @cfg {Number}    height          For North/South panels
34818  * @cfg {Boolean}   split           To show the splitter
34819  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34820  * 
34821  * @cfg {string}   cls             Extra CSS classes to add to region
34822  * 
34823  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34824  * @cfg {string}   region  the region that it inhabits..
34825  *
34826
34827  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34828  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34829
34830  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34831  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34832  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34833  */
34834 Roo.bootstrap.layout.Region = function(config)
34835 {
34836     this.applyConfig(config);
34837
34838     var mgr = config.mgr;
34839     var pos = config.region;
34840     config.skipConfig = true;
34841     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34842     
34843     if (mgr.el) {
34844         this.onRender(mgr.el);   
34845     }
34846      
34847     this.visible = true;
34848     this.collapsed = false;
34849     this.unrendered_panels = [];
34850 };
34851
34852 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34853
34854     position: '', // set by wrapper (eg. north/south etc..)
34855     unrendered_panels : null,  // unrendered panels.
34856     createBody : function(){
34857         /** This region's body element 
34858         * @type Roo.Element */
34859         this.bodyEl = this.el.createChild({
34860                 tag: "div",
34861                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34862         });
34863     },
34864
34865     onRender: function(ctr, pos)
34866     {
34867         var dh = Roo.DomHelper;
34868         /** This region's container element 
34869         * @type Roo.Element */
34870         this.el = dh.append(ctr.dom, {
34871                 tag: "div",
34872                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34873             }, true);
34874         /** This region's title element 
34875         * @type Roo.Element */
34876     
34877         this.titleEl = dh.append(this.el.dom,
34878             {
34879                     tag: "div",
34880                     unselectable: "on",
34881                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34882                     children:[
34883                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34884                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34885                     ]}, true);
34886         
34887         this.titleEl.enableDisplayMode();
34888         /** This region's title text element 
34889         * @type HTMLElement */
34890         this.titleTextEl = this.titleEl.dom.firstChild;
34891         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34892         /*
34893         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34894         this.closeBtn.enableDisplayMode();
34895         this.closeBtn.on("click", this.closeClicked, this);
34896         this.closeBtn.hide();
34897     */
34898         this.createBody(this.config);
34899         if(this.config.hideWhenEmpty){
34900             this.hide();
34901             this.on("paneladded", this.validateVisibility, this);
34902             this.on("panelremoved", this.validateVisibility, this);
34903         }
34904         if(this.autoScroll){
34905             this.bodyEl.setStyle("overflow", "auto");
34906         }else{
34907             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34908         }
34909         //if(c.titlebar !== false){
34910             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34911                 this.titleEl.hide();
34912             }else{
34913                 this.titleEl.show();
34914                 if(this.config.title){
34915                     this.titleTextEl.innerHTML = this.config.title;
34916                 }
34917             }
34918         //}
34919         if(this.config.collapsed){
34920             this.collapse(true);
34921         }
34922         if(this.config.hidden){
34923             this.hide();
34924         }
34925         
34926         if (this.unrendered_panels && this.unrendered_panels.length) {
34927             for (var i =0;i< this.unrendered_panels.length; i++) {
34928                 this.add(this.unrendered_panels[i]);
34929             }
34930             this.unrendered_panels = null;
34931             
34932         }
34933         
34934     },
34935     
34936     applyConfig : function(c)
34937     {
34938         /*
34939          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34940             var dh = Roo.DomHelper;
34941             if(c.titlebar !== false){
34942                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34943                 this.collapseBtn.on("click", this.collapse, this);
34944                 this.collapseBtn.enableDisplayMode();
34945                 /*
34946                 if(c.showPin === true || this.showPin){
34947                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34948                     this.stickBtn.enableDisplayMode();
34949                     this.stickBtn.on("click", this.expand, this);
34950                     this.stickBtn.hide();
34951                 }
34952                 
34953             }
34954             */
34955             /** This region's collapsed element
34956             * @type Roo.Element */
34957             /*
34958              *
34959             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34960                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34961             ]}, true);
34962             
34963             if(c.floatable !== false){
34964                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34965                this.collapsedEl.on("click", this.collapseClick, this);
34966             }
34967
34968             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34969                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34970                    id: "message", unselectable: "on", style:{"float":"left"}});
34971                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34972              }
34973             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34974             this.expandBtn.on("click", this.expand, this);
34975             
34976         }
34977         
34978         if(this.collapseBtn){
34979             this.collapseBtn.setVisible(c.collapsible == true);
34980         }
34981         
34982         this.cmargins = c.cmargins || this.cmargins ||
34983                          (this.position == "west" || this.position == "east" ?
34984                              {top: 0, left: 2, right:2, bottom: 0} :
34985                              {top: 2, left: 0, right:0, bottom: 2});
34986         */
34987         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34988         
34989         
34990         this.bottomTabs = c.tabPosition != "top";
34991         
34992         this.autoScroll = c.autoScroll || false;
34993         
34994         
34995        
34996         
34997         this.duration = c.duration || .30;
34998         this.slideDuration = c.slideDuration || .45;
34999         this.config = c;
35000        
35001     },
35002     /**
35003      * Returns true if this region is currently visible.
35004      * @return {Boolean}
35005      */
35006     isVisible : function(){
35007         return this.visible;
35008     },
35009
35010     /**
35011      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35012      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35013      */
35014     //setCollapsedTitle : function(title){
35015     //    title = title || "&#160;";
35016      //   if(this.collapsedTitleTextEl){
35017       //      this.collapsedTitleTextEl.innerHTML = title;
35018        // }
35019     //},
35020
35021     getBox : function(){
35022         var b;
35023       //  if(!this.collapsed){
35024             b = this.el.getBox(false, true);
35025        // }else{
35026           //  b = this.collapsedEl.getBox(false, true);
35027         //}
35028         return b;
35029     },
35030
35031     getMargins : function(){
35032         return this.margins;
35033         //return this.collapsed ? this.cmargins : this.margins;
35034     },
35035 /*
35036     highlight : function(){
35037         this.el.addClass("x-layout-panel-dragover");
35038     },
35039
35040     unhighlight : function(){
35041         this.el.removeClass("x-layout-panel-dragover");
35042     },
35043 */
35044     updateBox : function(box)
35045     {
35046         if (!this.bodyEl) {
35047             return; // not rendered yet..
35048         }
35049         
35050         this.box = box;
35051         if(!this.collapsed){
35052             this.el.dom.style.left = box.x + "px";
35053             this.el.dom.style.top = box.y + "px";
35054             this.updateBody(box.width, box.height);
35055         }else{
35056             this.collapsedEl.dom.style.left = box.x + "px";
35057             this.collapsedEl.dom.style.top = box.y + "px";
35058             this.collapsedEl.setSize(box.width, box.height);
35059         }
35060         if(this.tabs){
35061             this.tabs.autoSizeTabs();
35062         }
35063     },
35064
35065     updateBody : function(w, h)
35066     {
35067         if(w !== null){
35068             this.el.setWidth(w);
35069             w -= this.el.getBorderWidth("rl");
35070             if(this.config.adjustments){
35071                 w += this.config.adjustments[0];
35072             }
35073         }
35074         if(h !== null && h > 0){
35075             this.el.setHeight(h);
35076             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35077             h -= this.el.getBorderWidth("tb");
35078             if(this.config.adjustments){
35079                 h += this.config.adjustments[1];
35080             }
35081             this.bodyEl.setHeight(h);
35082             if(this.tabs){
35083                 h = this.tabs.syncHeight(h);
35084             }
35085         }
35086         if(this.panelSize){
35087             w = w !== null ? w : this.panelSize.width;
35088             h = h !== null ? h : this.panelSize.height;
35089         }
35090         if(this.activePanel){
35091             var el = this.activePanel.getEl();
35092             w = w !== null ? w : el.getWidth();
35093             h = h !== null ? h : el.getHeight();
35094             this.panelSize = {width: w, height: h};
35095             this.activePanel.setSize(w, h);
35096         }
35097         if(Roo.isIE && this.tabs){
35098             this.tabs.el.repaint();
35099         }
35100     },
35101
35102     /**
35103      * Returns the container element for this region.
35104      * @return {Roo.Element}
35105      */
35106     getEl : function(){
35107         return this.el;
35108     },
35109
35110     /**
35111      * Hides this region.
35112      */
35113     hide : function(){
35114         //if(!this.collapsed){
35115             this.el.dom.style.left = "-2000px";
35116             this.el.hide();
35117         //}else{
35118          //   this.collapsedEl.dom.style.left = "-2000px";
35119          //   this.collapsedEl.hide();
35120        // }
35121         this.visible = false;
35122         this.fireEvent("visibilitychange", this, false);
35123     },
35124
35125     /**
35126      * Shows this region if it was previously hidden.
35127      */
35128     show : function(){
35129         //if(!this.collapsed){
35130             this.el.show();
35131         //}else{
35132         //    this.collapsedEl.show();
35133        // }
35134         this.visible = true;
35135         this.fireEvent("visibilitychange", this, true);
35136     },
35137 /*
35138     closeClicked : function(){
35139         if(this.activePanel){
35140             this.remove(this.activePanel);
35141         }
35142     },
35143
35144     collapseClick : function(e){
35145         if(this.isSlid){
35146            e.stopPropagation();
35147            this.slideIn();
35148         }else{
35149            e.stopPropagation();
35150            this.slideOut();
35151         }
35152     },
35153 */
35154     /**
35155      * Collapses this region.
35156      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35157      */
35158     /*
35159     collapse : function(skipAnim, skipCheck = false){
35160         if(this.collapsed) {
35161             return;
35162         }
35163         
35164         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35165             
35166             this.collapsed = true;
35167             if(this.split){
35168                 this.split.el.hide();
35169             }
35170             if(this.config.animate && skipAnim !== true){
35171                 this.fireEvent("invalidated", this);
35172                 this.animateCollapse();
35173             }else{
35174                 this.el.setLocation(-20000,-20000);
35175                 this.el.hide();
35176                 this.collapsedEl.show();
35177                 this.fireEvent("collapsed", this);
35178                 this.fireEvent("invalidated", this);
35179             }
35180         }
35181         
35182     },
35183 */
35184     animateCollapse : function(){
35185         // overridden
35186     },
35187
35188     /**
35189      * Expands this region if it was previously collapsed.
35190      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35191      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35192      */
35193     /*
35194     expand : function(e, skipAnim){
35195         if(e) {
35196             e.stopPropagation();
35197         }
35198         if(!this.collapsed || this.el.hasActiveFx()) {
35199             return;
35200         }
35201         if(this.isSlid){
35202             this.afterSlideIn();
35203             skipAnim = true;
35204         }
35205         this.collapsed = false;
35206         if(this.config.animate && skipAnim !== true){
35207             this.animateExpand();
35208         }else{
35209             this.el.show();
35210             if(this.split){
35211                 this.split.el.show();
35212             }
35213             this.collapsedEl.setLocation(-2000,-2000);
35214             this.collapsedEl.hide();
35215             this.fireEvent("invalidated", this);
35216             this.fireEvent("expanded", this);
35217         }
35218     },
35219 */
35220     animateExpand : function(){
35221         // overridden
35222     },
35223
35224     initTabs : function()
35225     {
35226         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35227         
35228         var ts = new Roo.bootstrap.panel.Tabs({
35229                 el: this.bodyEl.dom,
35230                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35231                 disableTooltips: this.config.disableTabTips,
35232                 toolbar : this.config.toolbar
35233             });
35234         
35235         if(this.config.hideTabs){
35236             ts.stripWrap.setDisplayed(false);
35237         }
35238         this.tabs = ts;
35239         ts.resizeTabs = this.config.resizeTabs === true;
35240         ts.minTabWidth = this.config.minTabWidth || 40;
35241         ts.maxTabWidth = this.config.maxTabWidth || 250;
35242         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35243         ts.monitorResize = false;
35244         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35245         ts.bodyEl.addClass('roo-layout-tabs-body');
35246         this.panels.each(this.initPanelAsTab, this);
35247     },
35248
35249     initPanelAsTab : function(panel){
35250         var ti = this.tabs.addTab(
35251             panel.getEl().id,
35252             panel.getTitle(),
35253             null,
35254             this.config.closeOnTab && panel.isClosable(),
35255             panel.tpl
35256         );
35257         if(panel.tabTip !== undefined){
35258             ti.setTooltip(panel.tabTip);
35259         }
35260         ti.on("activate", function(){
35261               this.setActivePanel(panel);
35262         }, this);
35263         
35264         if(this.config.closeOnTab){
35265             ti.on("beforeclose", function(t, e){
35266                 e.cancel = true;
35267                 this.remove(panel);
35268             }, this);
35269         }
35270         
35271         panel.tabItem = ti;
35272         
35273         return ti;
35274     },
35275
35276     updatePanelTitle : function(panel, title)
35277     {
35278         if(this.activePanel == panel){
35279             this.updateTitle(title);
35280         }
35281         if(this.tabs){
35282             var ti = this.tabs.getTab(panel.getEl().id);
35283             ti.setText(title);
35284             if(panel.tabTip !== undefined){
35285                 ti.setTooltip(panel.tabTip);
35286             }
35287         }
35288     },
35289
35290     updateTitle : function(title){
35291         if(this.titleTextEl && !this.config.title){
35292             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35293         }
35294     },
35295
35296     setActivePanel : function(panel)
35297     {
35298         panel = this.getPanel(panel);
35299         if(this.activePanel && this.activePanel != panel){
35300             this.activePanel.setActiveState(false);
35301         }
35302         this.activePanel = panel;
35303         panel.setActiveState(true);
35304         if(this.panelSize){
35305             panel.setSize(this.panelSize.width, this.panelSize.height);
35306         }
35307         if(this.closeBtn){
35308             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35309         }
35310         this.updateTitle(panel.getTitle());
35311         if(this.tabs){
35312             this.fireEvent("invalidated", this);
35313         }
35314         this.fireEvent("panelactivated", this, panel);
35315     },
35316
35317     /**
35318      * Shows the specified panel.
35319      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35320      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35321      */
35322     showPanel : function(panel)
35323     {
35324         panel = this.getPanel(panel);
35325         if(panel){
35326             if(this.tabs){
35327                 var tab = this.tabs.getTab(panel.getEl().id);
35328                 if(tab.isHidden()){
35329                     this.tabs.unhideTab(tab.id);
35330                 }
35331                 tab.activate();
35332             }else{
35333                 this.setActivePanel(panel);
35334             }
35335         }
35336         return panel;
35337     },
35338
35339     /**
35340      * Get the active panel for this region.
35341      * @return {Roo.ContentPanel} The active panel or null
35342      */
35343     getActivePanel : function(){
35344         return this.activePanel;
35345     },
35346
35347     validateVisibility : function(){
35348         if(this.panels.getCount() < 1){
35349             this.updateTitle("&#160;");
35350             this.closeBtn.hide();
35351             this.hide();
35352         }else{
35353             if(!this.isVisible()){
35354                 this.show();
35355             }
35356         }
35357     },
35358
35359     /**
35360      * Adds the passed ContentPanel(s) to this region.
35361      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35362      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35363      */
35364     add : function(panel)
35365     {
35366         if(arguments.length > 1){
35367             for(var i = 0, len = arguments.length; i < len; i++) {
35368                 this.add(arguments[i]);
35369             }
35370             return null;
35371         }
35372         
35373         // if we have not been rendered yet, then we can not really do much of this..
35374         if (!this.bodyEl) {
35375             this.unrendered_panels.push(panel);
35376             return panel;
35377         }
35378         
35379         
35380         
35381         
35382         if(this.hasPanel(panel)){
35383             this.showPanel(panel);
35384             return panel;
35385         }
35386         panel.setRegion(this);
35387         this.panels.add(panel);
35388        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35389             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35390             // and hide them... ???
35391             this.bodyEl.dom.appendChild(panel.getEl().dom);
35392             if(panel.background !== true){
35393                 this.setActivePanel(panel);
35394             }
35395             this.fireEvent("paneladded", this, panel);
35396             return panel;
35397         }
35398         */
35399         if(!this.tabs){
35400             this.initTabs();
35401         }else{
35402             this.initPanelAsTab(panel);
35403         }
35404         
35405         
35406         if(panel.background !== true){
35407             this.tabs.activate(panel.getEl().id);
35408         }
35409         this.fireEvent("paneladded", this, panel);
35410         return panel;
35411     },
35412
35413     /**
35414      * Hides the tab for the specified panel.
35415      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35416      */
35417     hidePanel : function(panel){
35418         if(this.tabs && (panel = this.getPanel(panel))){
35419             this.tabs.hideTab(panel.getEl().id);
35420         }
35421     },
35422
35423     /**
35424      * Unhides the tab for a previously hidden panel.
35425      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35426      */
35427     unhidePanel : function(panel){
35428         if(this.tabs && (panel = this.getPanel(panel))){
35429             this.tabs.unhideTab(panel.getEl().id);
35430         }
35431     },
35432
35433     clearPanels : function(){
35434         while(this.panels.getCount() > 0){
35435              this.remove(this.panels.first());
35436         }
35437     },
35438
35439     /**
35440      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35441      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35442      * @param {Boolean} preservePanel Overrides the config preservePanel option
35443      * @return {Roo.ContentPanel} The panel that was removed
35444      */
35445     remove : function(panel, preservePanel)
35446     {
35447         panel = this.getPanel(panel);
35448         if(!panel){
35449             return null;
35450         }
35451         var e = {};
35452         this.fireEvent("beforeremove", this, panel, e);
35453         if(e.cancel === true){
35454             return null;
35455         }
35456         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35457         var panelId = panel.getId();
35458         this.panels.removeKey(panelId);
35459         if(preservePanel){
35460             document.body.appendChild(panel.getEl().dom);
35461         }
35462         if(this.tabs){
35463             this.tabs.removeTab(panel.getEl().id);
35464         }else if (!preservePanel){
35465             this.bodyEl.dom.removeChild(panel.getEl().dom);
35466         }
35467         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35468             var p = this.panels.first();
35469             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35470             tempEl.appendChild(p.getEl().dom);
35471             this.bodyEl.update("");
35472             this.bodyEl.dom.appendChild(p.getEl().dom);
35473             tempEl = null;
35474             this.updateTitle(p.getTitle());
35475             this.tabs = null;
35476             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35477             this.setActivePanel(p);
35478         }
35479         panel.setRegion(null);
35480         if(this.activePanel == panel){
35481             this.activePanel = null;
35482         }
35483         if(this.config.autoDestroy !== false && preservePanel !== true){
35484             try{panel.destroy();}catch(e){}
35485         }
35486         this.fireEvent("panelremoved", this, panel);
35487         return panel;
35488     },
35489
35490     /**
35491      * Returns the TabPanel component used by this region
35492      * @return {Roo.TabPanel}
35493      */
35494     getTabs : function(){
35495         return this.tabs;
35496     },
35497
35498     createTool : function(parentEl, className){
35499         var btn = Roo.DomHelper.append(parentEl, {
35500             tag: "div",
35501             cls: "x-layout-tools-button",
35502             children: [ {
35503                 tag: "div",
35504                 cls: "roo-layout-tools-button-inner " + className,
35505                 html: "&#160;"
35506             }]
35507         }, true);
35508         btn.addClassOnOver("roo-layout-tools-button-over");
35509         return btn;
35510     }
35511 });/*
35512  * Based on:
35513  * Ext JS Library 1.1.1
35514  * Copyright(c) 2006-2007, Ext JS, LLC.
35515  *
35516  * Originally Released Under LGPL - original licence link has changed is not relivant.
35517  *
35518  * Fork - LGPL
35519  * <script type="text/javascript">
35520  */
35521  
35522
35523
35524 /**
35525  * @class Roo.SplitLayoutRegion
35526  * @extends Roo.LayoutRegion
35527  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35528  */
35529 Roo.bootstrap.layout.Split = function(config){
35530     this.cursor = config.cursor;
35531     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35532 };
35533
35534 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35535 {
35536     splitTip : "Drag to resize.",
35537     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35538     useSplitTips : false,
35539
35540     applyConfig : function(config){
35541         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35542     },
35543     
35544     onRender : function(ctr,pos) {
35545         
35546         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35547         if(!this.config.split){
35548             return;
35549         }
35550         if(!this.split){
35551             
35552             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35553                             tag: "div",
35554                             id: this.el.id + "-split",
35555                             cls: "roo-layout-split roo-layout-split-"+this.position,
35556                             html: "&#160;"
35557             });
35558             /** The SplitBar for this region 
35559             * @type Roo.SplitBar */
35560             // does not exist yet...
35561             Roo.log([this.position, this.orientation]);
35562             
35563             this.split = new Roo.bootstrap.SplitBar({
35564                 dragElement : splitEl,
35565                 resizingElement: this.el,
35566                 orientation : this.orientation
35567             });
35568             
35569             this.split.on("moved", this.onSplitMove, this);
35570             this.split.useShim = this.config.useShim === true;
35571             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35572             if(this.useSplitTips){
35573                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35574             }
35575             //if(config.collapsible){
35576             //    this.split.el.on("dblclick", this.collapse,  this);
35577             //}
35578         }
35579         if(typeof this.config.minSize != "undefined"){
35580             this.split.minSize = this.config.minSize;
35581         }
35582         if(typeof this.config.maxSize != "undefined"){
35583             this.split.maxSize = this.config.maxSize;
35584         }
35585         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35586             this.hideSplitter();
35587         }
35588         
35589     },
35590
35591     getHMaxSize : function(){
35592          var cmax = this.config.maxSize || 10000;
35593          var center = this.mgr.getRegion("center");
35594          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35595     },
35596
35597     getVMaxSize : function(){
35598          var cmax = this.config.maxSize || 10000;
35599          var center = this.mgr.getRegion("center");
35600          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35601     },
35602
35603     onSplitMove : function(split, newSize){
35604         this.fireEvent("resized", this, newSize);
35605     },
35606     
35607     /** 
35608      * Returns the {@link Roo.SplitBar} for this region.
35609      * @return {Roo.SplitBar}
35610      */
35611     getSplitBar : function(){
35612         return this.split;
35613     },
35614     
35615     hide : function(){
35616         this.hideSplitter();
35617         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35618     },
35619
35620     hideSplitter : function(){
35621         if(this.split){
35622             this.split.el.setLocation(-2000,-2000);
35623             this.split.el.hide();
35624         }
35625     },
35626
35627     show : function(){
35628         if(this.split){
35629             this.split.el.show();
35630         }
35631         Roo.bootstrap.layout.Split.superclass.show.call(this);
35632     },
35633     
35634     beforeSlide: function(){
35635         if(Roo.isGecko){// firefox overflow auto bug workaround
35636             this.bodyEl.clip();
35637             if(this.tabs) {
35638                 this.tabs.bodyEl.clip();
35639             }
35640             if(this.activePanel){
35641                 this.activePanel.getEl().clip();
35642                 
35643                 if(this.activePanel.beforeSlide){
35644                     this.activePanel.beforeSlide();
35645                 }
35646             }
35647         }
35648     },
35649     
35650     afterSlide : function(){
35651         if(Roo.isGecko){// firefox overflow auto bug workaround
35652             this.bodyEl.unclip();
35653             if(this.tabs) {
35654                 this.tabs.bodyEl.unclip();
35655             }
35656             if(this.activePanel){
35657                 this.activePanel.getEl().unclip();
35658                 if(this.activePanel.afterSlide){
35659                     this.activePanel.afterSlide();
35660                 }
35661             }
35662         }
35663     },
35664
35665     initAutoHide : function(){
35666         if(this.autoHide !== false){
35667             if(!this.autoHideHd){
35668                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35669                 this.autoHideHd = {
35670                     "mouseout": function(e){
35671                         if(!e.within(this.el, true)){
35672                             st.delay(500);
35673                         }
35674                     },
35675                     "mouseover" : function(e){
35676                         st.cancel();
35677                     },
35678                     scope : this
35679                 };
35680             }
35681             this.el.on(this.autoHideHd);
35682         }
35683     },
35684
35685     clearAutoHide : function(){
35686         if(this.autoHide !== false){
35687             this.el.un("mouseout", this.autoHideHd.mouseout);
35688             this.el.un("mouseover", this.autoHideHd.mouseover);
35689         }
35690     },
35691
35692     clearMonitor : function(){
35693         Roo.get(document).un("click", this.slideInIf, this);
35694     },
35695
35696     // these names are backwards but not changed for compat
35697     slideOut : function(){
35698         if(this.isSlid || this.el.hasActiveFx()){
35699             return;
35700         }
35701         this.isSlid = true;
35702         if(this.collapseBtn){
35703             this.collapseBtn.hide();
35704         }
35705         this.closeBtnState = this.closeBtn.getStyle('display');
35706         this.closeBtn.hide();
35707         if(this.stickBtn){
35708             this.stickBtn.show();
35709         }
35710         this.el.show();
35711         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35712         this.beforeSlide();
35713         this.el.setStyle("z-index", 10001);
35714         this.el.slideIn(this.getSlideAnchor(), {
35715             callback: function(){
35716                 this.afterSlide();
35717                 this.initAutoHide();
35718                 Roo.get(document).on("click", this.slideInIf, this);
35719                 this.fireEvent("slideshow", this);
35720             },
35721             scope: this,
35722             block: true
35723         });
35724     },
35725
35726     afterSlideIn : function(){
35727         this.clearAutoHide();
35728         this.isSlid = false;
35729         this.clearMonitor();
35730         this.el.setStyle("z-index", "");
35731         if(this.collapseBtn){
35732             this.collapseBtn.show();
35733         }
35734         this.closeBtn.setStyle('display', this.closeBtnState);
35735         if(this.stickBtn){
35736             this.stickBtn.hide();
35737         }
35738         this.fireEvent("slidehide", this);
35739     },
35740
35741     slideIn : function(cb){
35742         if(!this.isSlid || this.el.hasActiveFx()){
35743             Roo.callback(cb);
35744             return;
35745         }
35746         this.isSlid = false;
35747         this.beforeSlide();
35748         this.el.slideOut(this.getSlideAnchor(), {
35749             callback: function(){
35750                 this.el.setLeftTop(-10000, -10000);
35751                 this.afterSlide();
35752                 this.afterSlideIn();
35753                 Roo.callback(cb);
35754             },
35755             scope: this,
35756             block: true
35757         });
35758     },
35759     
35760     slideInIf : function(e){
35761         if(!e.within(this.el)){
35762             this.slideIn();
35763         }
35764     },
35765
35766     animateCollapse : function(){
35767         this.beforeSlide();
35768         this.el.setStyle("z-index", 20000);
35769         var anchor = this.getSlideAnchor();
35770         this.el.slideOut(anchor, {
35771             callback : function(){
35772                 this.el.setStyle("z-index", "");
35773                 this.collapsedEl.slideIn(anchor, {duration:.3});
35774                 this.afterSlide();
35775                 this.el.setLocation(-10000,-10000);
35776                 this.el.hide();
35777                 this.fireEvent("collapsed", this);
35778             },
35779             scope: this,
35780             block: true
35781         });
35782     },
35783
35784     animateExpand : function(){
35785         this.beforeSlide();
35786         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35787         this.el.setStyle("z-index", 20000);
35788         this.collapsedEl.hide({
35789             duration:.1
35790         });
35791         this.el.slideIn(this.getSlideAnchor(), {
35792             callback : function(){
35793                 this.el.setStyle("z-index", "");
35794                 this.afterSlide();
35795                 if(this.split){
35796                     this.split.el.show();
35797                 }
35798                 this.fireEvent("invalidated", this);
35799                 this.fireEvent("expanded", this);
35800             },
35801             scope: this,
35802             block: true
35803         });
35804     },
35805
35806     anchors : {
35807         "west" : "left",
35808         "east" : "right",
35809         "north" : "top",
35810         "south" : "bottom"
35811     },
35812
35813     sanchors : {
35814         "west" : "l",
35815         "east" : "r",
35816         "north" : "t",
35817         "south" : "b"
35818     },
35819
35820     canchors : {
35821         "west" : "tl-tr",
35822         "east" : "tr-tl",
35823         "north" : "tl-bl",
35824         "south" : "bl-tl"
35825     },
35826
35827     getAnchor : function(){
35828         return this.anchors[this.position];
35829     },
35830
35831     getCollapseAnchor : function(){
35832         return this.canchors[this.position];
35833     },
35834
35835     getSlideAnchor : function(){
35836         return this.sanchors[this.position];
35837     },
35838
35839     getAlignAdj : function(){
35840         var cm = this.cmargins;
35841         switch(this.position){
35842             case "west":
35843                 return [0, 0];
35844             break;
35845             case "east":
35846                 return [0, 0];
35847             break;
35848             case "north":
35849                 return [0, 0];
35850             break;
35851             case "south":
35852                 return [0, 0];
35853             break;
35854         }
35855     },
35856
35857     getExpandAdj : function(){
35858         var c = this.collapsedEl, cm = this.cmargins;
35859         switch(this.position){
35860             case "west":
35861                 return [-(cm.right+c.getWidth()+cm.left), 0];
35862             break;
35863             case "east":
35864                 return [cm.right+c.getWidth()+cm.left, 0];
35865             break;
35866             case "north":
35867                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35868             break;
35869             case "south":
35870                 return [0, cm.top+cm.bottom+c.getHeight()];
35871             break;
35872         }
35873     }
35874 });/*
35875  * Based on:
35876  * Ext JS Library 1.1.1
35877  * Copyright(c) 2006-2007, Ext JS, LLC.
35878  *
35879  * Originally Released Under LGPL - original licence link has changed is not relivant.
35880  *
35881  * Fork - LGPL
35882  * <script type="text/javascript">
35883  */
35884 /*
35885  * These classes are private internal classes
35886  */
35887 Roo.bootstrap.layout.Center = function(config){
35888     config.region = "center";
35889     Roo.bootstrap.layout.Region.call(this, config);
35890     this.visible = true;
35891     this.minWidth = config.minWidth || 20;
35892     this.minHeight = config.minHeight || 20;
35893 };
35894
35895 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35896     hide : function(){
35897         // center panel can't be hidden
35898     },
35899     
35900     show : function(){
35901         // center panel can't be hidden
35902     },
35903     
35904     getMinWidth: function(){
35905         return this.minWidth;
35906     },
35907     
35908     getMinHeight: function(){
35909         return this.minHeight;
35910     }
35911 });
35912
35913
35914
35915
35916  
35917
35918
35919
35920
35921
35922 Roo.bootstrap.layout.North = function(config)
35923 {
35924     config.region = 'north';
35925     config.cursor = 'n-resize';
35926     
35927     Roo.bootstrap.layout.Split.call(this, config);
35928     
35929     
35930     if(this.split){
35931         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35932         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35933         this.split.el.addClass("roo-layout-split-v");
35934     }
35935     var size = config.initialSize || config.height;
35936     if(typeof size != "undefined"){
35937         this.el.setHeight(size);
35938     }
35939 };
35940 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35941 {
35942     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35943     
35944     
35945     
35946     getBox : function(){
35947         if(this.collapsed){
35948             return this.collapsedEl.getBox();
35949         }
35950         var box = this.el.getBox();
35951         if(this.split){
35952             box.height += this.split.el.getHeight();
35953         }
35954         return box;
35955     },
35956     
35957     updateBox : function(box){
35958         if(this.split && !this.collapsed){
35959             box.height -= this.split.el.getHeight();
35960             this.split.el.setLeft(box.x);
35961             this.split.el.setTop(box.y+box.height);
35962             this.split.el.setWidth(box.width);
35963         }
35964         if(this.collapsed){
35965             this.updateBody(box.width, null);
35966         }
35967         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35968     }
35969 });
35970
35971
35972
35973
35974
35975 Roo.bootstrap.layout.South = function(config){
35976     config.region = 'south';
35977     config.cursor = 's-resize';
35978     Roo.bootstrap.layout.Split.call(this, config);
35979     if(this.split){
35980         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35981         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35982         this.split.el.addClass("roo-layout-split-v");
35983     }
35984     var size = config.initialSize || config.height;
35985     if(typeof size != "undefined"){
35986         this.el.setHeight(size);
35987     }
35988 };
35989
35990 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35991     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35992     getBox : function(){
35993         if(this.collapsed){
35994             return this.collapsedEl.getBox();
35995         }
35996         var box = this.el.getBox();
35997         if(this.split){
35998             var sh = this.split.el.getHeight();
35999             box.height += sh;
36000             box.y -= sh;
36001         }
36002         return box;
36003     },
36004     
36005     updateBox : function(box){
36006         if(this.split && !this.collapsed){
36007             var sh = this.split.el.getHeight();
36008             box.height -= sh;
36009             box.y += sh;
36010             this.split.el.setLeft(box.x);
36011             this.split.el.setTop(box.y-sh);
36012             this.split.el.setWidth(box.width);
36013         }
36014         if(this.collapsed){
36015             this.updateBody(box.width, null);
36016         }
36017         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36018     }
36019 });
36020
36021 Roo.bootstrap.layout.East = function(config){
36022     config.region = "east";
36023     config.cursor = "e-resize";
36024     Roo.bootstrap.layout.Split.call(this, config);
36025     if(this.split){
36026         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36027         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36028         this.split.el.addClass("roo-layout-split-h");
36029     }
36030     var size = config.initialSize || config.width;
36031     if(typeof size != "undefined"){
36032         this.el.setWidth(size);
36033     }
36034 };
36035 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36036     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36037     getBox : function(){
36038         if(this.collapsed){
36039             return this.collapsedEl.getBox();
36040         }
36041         var box = this.el.getBox();
36042         if(this.split){
36043             var sw = this.split.el.getWidth();
36044             box.width += sw;
36045             box.x -= sw;
36046         }
36047         return box;
36048     },
36049
36050     updateBox : function(box){
36051         if(this.split && !this.collapsed){
36052             var sw = this.split.el.getWidth();
36053             box.width -= sw;
36054             this.split.el.setLeft(box.x);
36055             this.split.el.setTop(box.y);
36056             this.split.el.setHeight(box.height);
36057             box.x += sw;
36058         }
36059         if(this.collapsed){
36060             this.updateBody(null, box.height);
36061         }
36062         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36063     }
36064 });
36065
36066 Roo.bootstrap.layout.West = function(config){
36067     config.region = "west";
36068     config.cursor = "w-resize";
36069     
36070     Roo.bootstrap.layout.Split.call(this, config);
36071     if(this.split){
36072         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36073         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36074         this.split.el.addClass("roo-layout-split-h");
36075     }
36076     
36077 };
36078 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36079     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36080     
36081     onRender: function(ctr, pos)
36082     {
36083         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36084         var size = this.config.initialSize || this.config.width;
36085         if(typeof size != "undefined"){
36086             this.el.setWidth(size);
36087         }
36088     },
36089     
36090     getBox : function(){
36091         if(this.collapsed){
36092             return this.collapsedEl.getBox();
36093         }
36094         var box = this.el.getBox();
36095         if(this.split){
36096             box.width += this.split.el.getWidth();
36097         }
36098         return box;
36099     },
36100     
36101     updateBox : function(box){
36102         if(this.split && !this.collapsed){
36103             var sw = this.split.el.getWidth();
36104             box.width -= sw;
36105             this.split.el.setLeft(box.x+box.width);
36106             this.split.el.setTop(box.y);
36107             this.split.el.setHeight(box.height);
36108         }
36109         if(this.collapsed){
36110             this.updateBody(null, box.height);
36111         }
36112         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36113     }
36114 });
36115 Roo.namespace("Roo.bootstrap.panel");/*
36116  * Based on:
36117  * Ext JS Library 1.1.1
36118  * Copyright(c) 2006-2007, Ext JS, LLC.
36119  *
36120  * Originally Released Under LGPL - original licence link has changed is not relivant.
36121  *
36122  * Fork - LGPL
36123  * <script type="text/javascript">
36124  */
36125 /**
36126  * @class Roo.ContentPanel
36127  * @extends Roo.util.Observable
36128  * A basic ContentPanel element.
36129  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36130  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36131  * @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
36132  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36133  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36134  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36135  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36136  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36137  * @cfg {String} title          The title for this panel
36138  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36139  * @cfg {String} url            Calls {@link #setUrl} with this value
36140  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36141  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36142  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36143  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36144  * @cfg {Boolean} badges render the badges
36145
36146  * @constructor
36147  * Create a new ContentPanel.
36148  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36149  * @param {String/Object} config A string to set only the title or a config object
36150  * @param {String} content (optional) Set the HTML content for this panel
36151  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36152  */
36153 Roo.bootstrap.panel.Content = function( config){
36154     
36155     this.tpl = config.tpl || false;
36156     
36157     var el = config.el;
36158     var content = config.content;
36159
36160     if(config.autoCreate){ // xtype is available if this is called from factory
36161         el = Roo.id();
36162     }
36163     this.el = Roo.get(el);
36164     if(!this.el && config && config.autoCreate){
36165         if(typeof config.autoCreate == "object"){
36166             if(!config.autoCreate.id){
36167                 config.autoCreate.id = config.id||el;
36168             }
36169             this.el = Roo.DomHelper.append(document.body,
36170                         config.autoCreate, true);
36171         }else{
36172             var elcfg =  {   tag: "div",
36173                             cls: "roo-layout-inactive-content",
36174                             id: config.id||el
36175                             };
36176             if (config.html) {
36177                 elcfg.html = config.html;
36178                 
36179             }
36180                         
36181             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36182         }
36183     } 
36184     this.closable = false;
36185     this.loaded = false;
36186     this.active = false;
36187    
36188       
36189     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36190         
36191         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36192         
36193         this.wrapEl = this.el; //this.el.wrap();
36194         var ti = [];
36195         if (config.toolbar.items) {
36196             ti = config.toolbar.items ;
36197             delete config.toolbar.items ;
36198         }
36199         
36200         var nitems = [];
36201         this.toolbar.render(this.wrapEl, 'before');
36202         for(var i =0;i < ti.length;i++) {
36203           //  Roo.log(['add child', items[i]]);
36204             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36205         }
36206         this.toolbar.items = nitems;
36207         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36208         delete config.toolbar;
36209         
36210     }
36211     /*
36212     // xtype created footer. - not sure if will work as we normally have to render first..
36213     if (this.footer && !this.footer.el && this.footer.xtype) {
36214         if (!this.wrapEl) {
36215             this.wrapEl = this.el.wrap();
36216         }
36217     
36218         this.footer.container = this.wrapEl.createChild();
36219          
36220         this.footer = Roo.factory(this.footer, Roo);
36221         
36222     }
36223     */
36224     
36225      if(typeof config == "string"){
36226         this.title = config;
36227     }else{
36228         Roo.apply(this, config);
36229     }
36230     
36231     if(this.resizeEl){
36232         this.resizeEl = Roo.get(this.resizeEl, true);
36233     }else{
36234         this.resizeEl = this.el;
36235     }
36236     // handle view.xtype
36237     
36238  
36239     
36240     
36241     this.addEvents({
36242         /**
36243          * @event activate
36244          * Fires when this panel is activated. 
36245          * @param {Roo.ContentPanel} this
36246          */
36247         "activate" : true,
36248         /**
36249          * @event deactivate
36250          * Fires when this panel is activated. 
36251          * @param {Roo.ContentPanel} this
36252          */
36253         "deactivate" : true,
36254
36255         /**
36256          * @event resize
36257          * Fires when this panel is resized if fitToFrame is true.
36258          * @param {Roo.ContentPanel} this
36259          * @param {Number} width The width after any component adjustments
36260          * @param {Number} height The height after any component adjustments
36261          */
36262         "resize" : true,
36263         
36264          /**
36265          * @event render
36266          * Fires when this tab is created
36267          * @param {Roo.ContentPanel} this
36268          */
36269         "render" : true
36270         
36271         
36272         
36273     });
36274     
36275
36276     
36277     
36278     if(this.autoScroll){
36279         this.resizeEl.setStyle("overflow", "auto");
36280     } else {
36281         // fix randome scrolling
36282         //this.el.on('scroll', function() {
36283         //    Roo.log('fix random scolling');
36284         //    this.scrollTo('top',0); 
36285         //});
36286     }
36287     content = content || this.content;
36288     if(content){
36289         this.setContent(content);
36290     }
36291     if(config && config.url){
36292         this.setUrl(this.url, this.params, this.loadOnce);
36293     }
36294     
36295     
36296     
36297     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36298     
36299     if (this.view && typeof(this.view.xtype) != 'undefined') {
36300         this.view.el = this.el.appendChild(document.createElement("div"));
36301         this.view = Roo.factory(this.view); 
36302         this.view.render  &&  this.view.render(false, '');  
36303     }
36304     
36305     
36306     this.fireEvent('render', this);
36307 };
36308
36309 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36310     
36311     tabTip : '',
36312     
36313     setRegion : function(region){
36314         this.region = region;
36315         this.setActiveClass(region && !this.background);
36316     },
36317     
36318     
36319     setActiveClass: function(state)
36320     {
36321         if(state){
36322            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36323            this.el.setStyle('position','relative');
36324         }else{
36325            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36326            this.el.setStyle('position', 'absolute');
36327         } 
36328     },
36329     
36330     /**
36331      * Returns the toolbar for this Panel if one was configured. 
36332      * @return {Roo.Toolbar} 
36333      */
36334     getToolbar : function(){
36335         return this.toolbar;
36336     },
36337     
36338     setActiveState : function(active)
36339     {
36340         this.active = active;
36341         this.setActiveClass(active);
36342         if(!active){
36343             this.fireEvent("deactivate", this);
36344         }else{
36345             this.fireEvent("activate", this);
36346         }
36347     },
36348     /**
36349      * Updates this panel's element
36350      * @param {String} content The new content
36351      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36352     */
36353     setContent : function(content, loadScripts){
36354         this.el.update(content, loadScripts);
36355     },
36356
36357     ignoreResize : function(w, h){
36358         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36359             return true;
36360         }else{
36361             this.lastSize = {width: w, height: h};
36362             return false;
36363         }
36364     },
36365     /**
36366      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36367      * @return {Roo.UpdateManager} The UpdateManager
36368      */
36369     getUpdateManager : function(){
36370         return this.el.getUpdateManager();
36371     },
36372      /**
36373      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36374      * @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:
36375 <pre><code>
36376 panel.load({
36377     url: "your-url.php",
36378     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36379     callback: yourFunction,
36380     scope: yourObject, //(optional scope)
36381     discardUrl: false,
36382     nocache: false,
36383     text: "Loading...",
36384     timeout: 30,
36385     scripts: false
36386 });
36387 </code></pre>
36388      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36389      * 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.
36390      * @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}
36391      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36392      * @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.
36393      * @return {Roo.ContentPanel} this
36394      */
36395     load : function(){
36396         var um = this.el.getUpdateManager();
36397         um.update.apply(um, arguments);
36398         return this;
36399     },
36400
36401
36402     /**
36403      * 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.
36404      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36405      * @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)
36406      * @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)
36407      * @return {Roo.UpdateManager} The UpdateManager
36408      */
36409     setUrl : function(url, params, loadOnce){
36410         if(this.refreshDelegate){
36411             this.removeListener("activate", this.refreshDelegate);
36412         }
36413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36414         this.on("activate", this.refreshDelegate);
36415         return this.el.getUpdateManager();
36416     },
36417     
36418     _handleRefresh : function(url, params, loadOnce){
36419         if(!loadOnce || !this.loaded){
36420             var updater = this.el.getUpdateManager();
36421             updater.update(url, params, this._setLoaded.createDelegate(this));
36422         }
36423     },
36424     
36425     _setLoaded : function(){
36426         this.loaded = true;
36427     }, 
36428     
36429     /**
36430      * Returns this panel's id
36431      * @return {String} 
36432      */
36433     getId : function(){
36434         return this.el.id;
36435     },
36436     
36437     /** 
36438      * Returns this panel's element - used by regiosn to add.
36439      * @return {Roo.Element} 
36440      */
36441     getEl : function(){
36442         return this.wrapEl || this.el;
36443     },
36444     
36445    
36446     
36447     adjustForComponents : function(width, height)
36448     {
36449         //Roo.log('adjustForComponents ');
36450         if(this.resizeEl != this.el){
36451             width -= this.el.getFrameWidth('lr');
36452             height -= this.el.getFrameWidth('tb');
36453         }
36454         if(this.toolbar){
36455             var te = this.toolbar.getEl();
36456             te.setWidth(width);
36457             height -= te.getHeight();
36458         }
36459         if(this.footer){
36460             var te = this.footer.getEl();
36461             te.setWidth(width);
36462             height -= te.getHeight();
36463         }
36464         
36465         
36466         if(this.adjustments){
36467             width += this.adjustments[0];
36468             height += this.adjustments[1];
36469         }
36470         return {"width": width, "height": height};
36471     },
36472     
36473     setSize : function(width, height){
36474         if(this.fitToFrame && !this.ignoreResize(width, height)){
36475             if(this.fitContainer && this.resizeEl != this.el){
36476                 this.el.setSize(width, height);
36477             }
36478             var size = this.adjustForComponents(width, height);
36479             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36480             this.fireEvent('resize', this, size.width, size.height);
36481         }
36482     },
36483     
36484     /**
36485      * Returns this panel's title
36486      * @return {String} 
36487      */
36488     getTitle : function(){
36489         
36490         if (typeof(this.title) != 'object') {
36491             return this.title;
36492         }
36493         
36494         var t = '';
36495         for (var k in this.title) {
36496             if (!this.title.hasOwnProperty(k)) {
36497                 continue;
36498             }
36499             
36500             if (k.indexOf('-') >= 0) {
36501                 var s = k.split('-');
36502                 for (var i = 0; i<s.length; i++) {
36503                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36504                 }
36505             } else {
36506                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36507             }
36508         }
36509         return t;
36510     },
36511     
36512     /**
36513      * Set this panel's title
36514      * @param {String} title
36515      */
36516     setTitle : function(title){
36517         this.title = title;
36518         if(this.region){
36519             this.region.updatePanelTitle(this, title);
36520         }
36521     },
36522     
36523     /**
36524      * Returns true is this panel was configured to be closable
36525      * @return {Boolean} 
36526      */
36527     isClosable : function(){
36528         return this.closable;
36529     },
36530     
36531     beforeSlide : function(){
36532         this.el.clip();
36533         this.resizeEl.clip();
36534     },
36535     
36536     afterSlide : function(){
36537         this.el.unclip();
36538         this.resizeEl.unclip();
36539     },
36540     
36541     /**
36542      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36543      *   Will fail silently if the {@link #setUrl} method has not been called.
36544      *   This does not activate the panel, just updates its content.
36545      */
36546     refresh : function(){
36547         if(this.refreshDelegate){
36548            this.loaded = false;
36549            this.refreshDelegate();
36550         }
36551     },
36552     
36553     /**
36554      * Destroys this panel
36555      */
36556     destroy : function(){
36557         this.el.removeAllListeners();
36558         var tempEl = document.createElement("span");
36559         tempEl.appendChild(this.el.dom);
36560         tempEl.innerHTML = "";
36561         this.el.remove();
36562         this.el = null;
36563     },
36564     
36565     /**
36566      * form - if the content panel contains a form - this is a reference to it.
36567      * @type {Roo.form.Form}
36568      */
36569     form : false,
36570     /**
36571      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36572      *    This contains a reference to it.
36573      * @type {Roo.View}
36574      */
36575     view : false,
36576     
36577       /**
36578      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36579      * <pre><code>
36580
36581 layout.addxtype({
36582        xtype : 'Form',
36583        items: [ .... ]
36584    }
36585 );
36586
36587 </code></pre>
36588      * @param {Object} cfg Xtype definition of item to add.
36589      */
36590     
36591     
36592     getChildContainer: function () {
36593         return this.getEl();
36594     }
36595     
36596     
36597     /*
36598         var  ret = new Roo.factory(cfg);
36599         return ret;
36600         
36601         
36602         // add form..
36603         if (cfg.xtype.match(/^Form$/)) {
36604             
36605             var el;
36606             //if (this.footer) {
36607             //    el = this.footer.container.insertSibling(false, 'before');
36608             //} else {
36609                 el = this.el.createChild();
36610             //}
36611
36612             this.form = new  Roo.form.Form(cfg);
36613             
36614             
36615             if ( this.form.allItems.length) {
36616                 this.form.render(el.dom);
36617             }
36618             return this.form;
36619         }
36620         // should only have one of theses..
36621         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36622             // views.. should not be just added - used named prop 'view''
36623             
36624             cfg.el = this.el.appendChild(document.createElement("div"));
36625             // factory?
36626             
36627             var ret = new Roo.factory(cfg);
36628              
36629              ret.render && ret.render(false, ''); // render blank..
36630             this.view = ret;
36631             return ret;
36632         }
36633         return false;
36634     }
36635     \*/
36636 });
36637  
36638 /**
36639  * @class Roo.bootstrap.panel.Grid
36640  * @extends Roo.bootstrap.panel.Content
36641  * @constructor
36642  * Create a new GridPanel.
36643  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36644  * @param {Object} config A the config object
36645   
36646  */
36647
36648
36649
36650 Roo.bootstrap.panel.Grid = function(config)
36651 {
36652     
36653       
36654     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36655         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36656
36657     config.el = this.wrapper;
36658     //this.el = this.wrapper;
36659     
36660       if (config.container) {
36661         // ctor'ed from a Border/panel.grid
36662         
36663         
36664         this.wrapper.setStyle("overflow", "hidden");
36665         this.wrapper.addClass('roo-grid-container');
36666
36667     }
36668     
36669     
36670     if(config.toolbar){
36671         var tool_el = this.wrapper.createChild();    
36672         this.toolbar = Roo.factory(config.toolbar);
36673         var ti = [];
36674         if (config.toolbar.items) {
36675             ti = config.toolbar.items ;
36676             delete config.toolbar.items ;
36677         }
36678         
36679         var nitems = [];
36680         this.toolbar.render(tool_el);
36681         for(var i =0;i < ti.length;i++) {
36682           //  Roo.log(['add child', items[i]]);
36683             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36684         }
36685         this.toolbar.items = nitems;
36686         
36687         delete config.toolbar;
36688     }
36689     
36690     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36691     config.grid.scrollBody = true;;
36692     config.grid.monitorWindowResize = false; // turn off autosizing
36693     config.grid.autoHeight = false;
36694     config.grid.autoWidth = false;
36695     
36696     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36697     
36698     if (config.background) {
36699         // render grid on panel activation (if panel background)
36700         this.on('activate', function(gp) {
36701             if (!gp.grid.rendered) {
36702                 gp.grid.render(this.wrapper);
36703                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36704             }
36705         });
36706             
36707     } else {
36708         this.grid.render(this.wrapper);
36709         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36710
36711     }
36712     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36713     // ??? needed ??? config.el = this.wrapper;
36714     
36715     
36716     
36717   
36718     // xtype created footer. - not sure if will work as we normally have to render first..
36719     if (this.footer && !this.footer.el && this.footer.xtype) {
36720         
36721         var ctr = this.grid.getView().getFooterPanel(true);
36722         this.footer.dataSource = this.grid.dataSource;
36723         this.footer = Roo.factory(this.footer, Roo);
36724         this.footer.render(ctr);
36725         
36726     }
36727     
36728     
36729     
36730     
36731      
36732 };
36733
36734 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36735     getId : function(){
36736         return this.grid.id;
36737     },
36738     
36739     /**
36740      * Returns the grid for this panel
36741      * @return {Roo.bootstrap.Table} 
36742      */
36743     getGrid : function(){
36744         return this.grid;    
36745     },
36746     
36747     setSize : function(width, height){
36748         if(!this.ignoreResize(width, height)){
36749             var grid = this.grid;
36750             var size = this.adjustForComponents(width, height);
36751             var gridel = grid.getGridEl();
36752             gridel.setSize(size.width, size.height);
36753             /*
36754             var thd = grid.getGridEl().select('thead',true).first();
36755             var tbd = grid.getGridEl().select('tbody', true).first();
36756             if (tbd) {
36757                 tbd.setSize(width, height - thd.getHeight());
36758             }
36759             */
36760             grid.autoSize();
36761         }
36762     },
36763      
36764     
36765     
36766     beforeSlide : function(){
36767         this.grid.getView().scroller.clip();
36768     },
36769     
36770     afterSlide : function(){
36771         this.grid.getView().scroller.unclip();
36772     },
36773     
36774     destroy : function(){
36775         this.grid.destroy();
36776         delete this.grid;
36777         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36778     }
36779 });
36780
36781 /**
36782  * @class Roo.bootstrap.panel.Nest
36783  * @extends Roo.bootstrap.panel.Content
36784  * @constructor
36785  * Create a new Panel, that can contain a layout.Border.
36786  * 
36787  * 
36788  * @param {Roo.BorderLayout} layout The layout for this panel
36789  * @param {String/Object} config A string to set only the title or a config object
36790  */
36791 Roo.bootstrap.panel.Nest = function(config)
36792 {
36793     // construct with only one argument..
36794     /* FIXME - implement nicer consturctors
36795     if (layout.layout) {
36796         config = layout;
36797         layout = config.layout;
36798         delete config.layout;
36799     }
36800     if (layout.xtype && !layout.getEl) {
36801         // then layout needs constructing..
36802         layout = Roo.factory(layout, Roo);
36803     }
36804     */
36805     
36806     config.el =  config.layout.getEl();
36807     
36808     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36809     
36810     config.layout.monitorWindowResize = false; // turn off autosizing
36811     this.layout = config.layout;
36812     this.layout.getEl().addClass("roo-layout-nested-layout");
36813     
36814     
36815     
36816     
36817 };
36818
36819 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36820
36821     setSize : function(width, height){
36822         if(!this.ignoreResize(width, height)){
36823             var size = this.adjustForComponents(width, height);
36824             var el = this.layout.getEl();
36825             if (size.height < 1) {
36826                 el.setWidth(size.width);   
36827             } else {
36828                 el.setSize(size.width, size.height);
36829             }
36830             var touch = el.dom.offsetWidth;
36831             this.layout.layout();
36832             // ie requires a double layout on the first pass
36833             if(Roo.isIE && !this.initialized){
36834                 this.initialized = true;
36835                 this.layout.layout();
36836             }
36837         }
36838     },
36839     
36840     // activate all subpanels if not currently active..
36841     
36842     setActiveState : function(active){
36843         this.active = active;
36844         this.setActiveClass(active);
36845         
36846         if(!active){
36847             this.fireEvent("deactivate", this);
36848             return;
36849         }
36850         
36851         this.fireEvent("activate", this);
36852         // not sure if this should happen before or after..
36853         if (!this.layout) {
36854             return; // should not happen..
36855         }
36856         var reg = false;
36857         for (var r in this.layout.regions) {
36858             reg = this.layout.getRegion(r);
36859             if (reg.getActivePanel()) {
36860                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36861                 reg.setActivePanel(reg.getActivePanel());
36862                 continue;
36863             }
36864             if (!reg.panels.length) {
36865                 continue;
36866             }
36867             reg.showPanel(reg.getPanel(0));
36868         }
36869         
36870         
36871         
36872         
36873     },
36874     
36875     /**
36876      * Returns the nested BorderLayout for this panel
36877      * @return {Roo.BorderLayout} 
36878      */
36879     getLayout : function(){
36880         return this.layout;
36881     },
36882     
36883      /**
36884      * Adds a xtype elements to the layout of the nested panel
36885      * <pre><code>
36886
36887 panel.addxtype({
36888        xtype : 'ContentPanel',
36889        region: 'west',
36890        items: [ .... ]
36891    }
36892 );
36893
36894 panel.addxtype({
36895         xtype : 'NestedLayoutPanel',
36896         region: 'west',
36897         layout: {
36898            center: { },
36899            west: { }   
36900         },
36901         items : [ ... list of content panels or nested layout panels.. ]
36902    }
36903 );
36904 </code></pre>
36905      * @param {Object} cfg Xtype definition of item to add.
36906      */
36907     addxtype : function(cfg) {
36908         return this.layout.addxtype(cfg);
36909     
36910     }
36911 });        /*
36912  * Based on:
36913  * Ext JS Library 1.1.1
36914  * Copyright(c) 2006-2007, Ext JS, LLC.
36915  *
36916  * Originally Released Under LGPL - original licence link has changed is not relivant.
36917  *
36918  * Fork - LGPL
36919  * <script type="text/javascript">
36920  */
36921 /**
36922  * @class Roo.TabPanel
36923  * @extends Roo.util.Observable
36924  * A lightweight tab container.
36925  * <br><br>
36926  * Usage:
36927  * <pre><code>
36928 // basic tabs 1, built from existing content
36929 var tabs = new Roo.TabPanel("tabs1");
36930 tabs.addTab("script", "View Script");
36931 tabs.addTab("markup", "View Markup");
36932 tabs.activate("script");
36933
36934 // more advanced tabs, built from javascript
36935 var jtabs = new Roo.TabPanel("jtabs");
36936 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36937
36938 // set up the UpdateManager
36939 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36940 var updater = tab2.getUpdateManager();
36941 updater.setDefaultUrl("ajax1.htm");
36942 tab2.on('activate', updater.refresh, updater, true);
36943
36944 // Use setUrl for Ajax loading
36945 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36946 tab3.setUrl("ajax2.htm", null, true);
36947
36948 // Disabled tab
36949 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36950 tab4.disable();
36951
36952 jtabs.activate("jtabs-1");
36953  * </code></pre>
36954  * @constructor
36955  * Create a new TabPanel.
36956  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36957  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36958  */
36959 Roo.bootstrap.panel.Tabs = function(config){
36960     /**
36961     * The container element for this TabPanel.
36962     * @type Roo.Element
36963     */
36964     this.el = Roo.get(config.el);
36965     delete config.el;
36966     if(config){
36967         if(typeof config == "boolean"){
36968             this.tabPosition = config ? "bottom" : "top";
36969         }else{
36970             Roo.apply(this, config);
36971         }
36972     }
36973     
36974     if(this.tabPosition == "bottom"){
36975         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36976         this.el.addClass("roo-tabs-bottom");
36977     }
36978     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36979     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36980     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36981     if(Roo.isIE){
36982         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36983     }
36984     if(this.tabPosition != "bottom"){
36985         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36986          * @type Roo.Element
36987          */
36988         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36989         this.el.addClass("roo-tabs-top");
36990     }
36991     this.items = [];
36992
36993     this.bodyEl.setStyle("position", "relative");
36994
36995     this.active = null;
36996     this.activateDelegate = this.activate.createDelegate(this);
36997
36998     this.addEvents({
36999         /**
37000          * @event tabchange
37001          * Fires when the active tab changes
37002          * @param {Roo.TabPanel} this
37003          * @param {Roo.TabPanelItem} activePanel The new active tab
37004          */
37005         "tabchange": true,
37006         /**
37007          * @event beforetabchange
37008          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37009          * @param {Roo.TabPanel} this
37010          * @param {Object} e Set cancel to true on this object to cancel the tab change
37011          * @param {Roo.TabPanelItem} tab The tab being changed to
37012          */
37013         "beforetabchange" : true
37014     });
37015
37016     Roo.EventManager.onWindowResize(this.onResize, this);
37017     this.cpad = this.el.getPadding("lr");
37018     this.hiddenCount = 0;
37019
37020
37021     // toolbar on the tabbar support...
37022     if (this.toolbar) {
37023         alert("no toolbar support yet");
37024         this.toolbar  = false;
37025         /*
37026         var tcfg = this.toolbar;
37027         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37028         this.toolbar = new Roo.Toolbar(tcfg);
37029         if (Roo.isSafari) {
37030             var tbl = tcfg.container.child('table', true);
37031             tbl.setAttribute('width', '100%');
37032         }
37033         */
37034         
37035     }
37036    
37037
37038
37039     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37040 };
37041
37042 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37043     /*
37044      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37045      */
37046     tabPosition : "top",
37047     /*
37048      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37049      */
37050     currentTabWidth : 0,
37051     /*
37052      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37053      */
37054     minTabWidth : 40,
37055     /*
37056      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37057      */
37058     maxTabWidth : 250,
37059     /*
37060      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37061      */
37062     preferredTabWidth : 175,
37063     /*
37064      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37065      */
37066     resizeTabs : false,
37067     /*
37068      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37069      */
37070     monitorResize : true,
37071     /*
37072      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37073      */
37074     toolbar : false,
37075
37076     /**
37077      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37078      * @param {String} id The id of the div to use <b>or create</b>
37079      * @param {String} text The text for the tab
37080      * @param {String} content (optional) Content to put in the TabPanelItem body
37081      * @param {Boolean} closable (optional) True to create a close icon on the tab
37082      * @return {Roo.TabPanelItem} The created TabPanelItem
37083      */
37084     addTab : function(id, text, content, closable, tpl)
37085     {
37086         var item = new Roo.bootstrap.panel.TabItem({
37087             panel: this,
37088             id : id,
37089             text : text,
37090             closable : closable,
37091             tpl : tpl
37092         });
37093         this.addTabItem(item);
37094         if(content){
37095             item.setContent(content);
37096         }
37097         return item;
37098     },
37099
37100     /**
37101      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37102      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37103      * @return {Roo.TabPanelItem}
37104      */
37105     getTab : function(id){
37106         return this.items[id];
37107     },
37108
37109     /**
37110      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37111      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37112      */
37113     hideTab : function(id){
37114         var t = this.items[id];
37115         if(!t.isHidden()){
37116            t.setHidden(true);
37117            this.hiddenCount++;
37118            this.autoSizeTabs();
37119         }
37120     },
37121
37122     /**
37123      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37124      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37125      */
37126     unhideTab : function(id){
37127         var t = this.items[id];
37128         if(t.isHidden()){
37129            t.setHidden(false);
37130            this.hiddenCount--;
37131            this.autoSizeTabs();
37132         }
37133     },
37134
37135     /**
37136      * Adds an existing {@link Roo.TabPanelItem}.
37137      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37138      */
37139     addTabItem : function(item){
37140         this.items[item.id] = item;
37141         this.items.push(item);
37142       //  if(this.resizeTabs){
37143     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37144   //         this.autoSizeTabs();
37145 //        }else{
37146 //            item.autoSize();
37147        // }
37148     },
37149
37150     /**
37151      * Removes a {@link Roo.TabPanelItem}.
37152      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37153      */
37154     removeTab : function(id){
37155         var items = this.items;
37156         var tab = items[id];
37157         if(!tab) { return; }
37158         var index = items.indexOf(tab);
37159         if(this.active == tab && items.length > 1){
37160             var newTab = this.getNextAvailable(index);
37161             if(newTab) {
37162                 newTab.activate();
37163             }
37164         }
37165         this.stripEl.dom.removeChild(tab.pnode.dom);
37166         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37167             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37168         }
37169         items.splice(index, 1);
37170         delete this.items[tab.id];
37171         tab.fireEvent("close", tab);
37172         tab.purgeListeners();
37173         this.autoSizeTabs();
37174     },
37175
37176     getNextAvailable : function(start){
37177         var items = this.items;
37178         var index = start;
37179         // look for a next tab that will slide over to
37180         // replace the one being removed
37181         while(index < items.length){
37182             var item = items[++index];
37183             if(item && !item.isHidden()){
37184                 return item;
37185             }
37186         }
37187         // if one isn't found select the previous tab (on the left)
37188         index = start;
37189         while(index >= 0){
37190             var item = items[--index];
37191             if(item && !item.isHidden()){
37192                 return item;
37193             }
37194         }
37195         return null;
37196     },
37197
37198     /**
37199      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37200      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37201      */
37202     disableTab : function(id){
37203         var tab = this.items[id];
37204         if(tab && this.active != tab){
37205             tab.disable();
37206         }
37207     },
37208
37209     /**
37210      * Enables a {@link Roo.TabPanelItem} that is disabled.
37211      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37212      */
37213     enableTab : function(id){
37214         var tab = this.items[id];
37215         tab.enable();
37216     },
37217
37218     /**
37219      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37220      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37221      * @return {Roo.TabPanelItem} The TabPanelItem.
37222      */
37223     activate : function(id){
37224         var tab = this.items[id];
37225         if(!tab){
37226             return null;
37227         }
37228         if(tab == this.active || tab.disabled){
37229             return tab;
37230         }
37231         var e = {};
37232         this.fireEvent("beforetabchange", this, e, tab);
37233         if(e.cancel !== true && !tab.disabled){
37234             if(this.active){
37235                 this.active.hide();
37236             }
37237             this.active = this.items[id];
37238             this.active.show();
37239             this.fireEvent("tabchange", this, this.active);
37240         }
37241         return tab;
37242     },
37243
37244     /**
37245      * Gets the active {@link Roo.TabPanelItem}.
37246      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37247      */
37248     getActiveTab : function(){
37249         return this.active;
37250     },
37251
37252     /**
37253      * Updates the tab body element to fit the height of the container element
37254      * for overflow scrolling
37255      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37256      */
37257     syncHeight : function(targetHeight){
37258         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37259         var bm = this.bodyEl.getMargins();
37260         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37261         this.bodyEl.setHeight(newHeight);
37262         return newHeight;
37263     },
37264
37265     onResize : function(){
37266         if(this.monitorResize){
37267             this.autoSizeTabs();
37268         }
37269     },
37270
37271     /**
37272      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37273      */
37274     beginUpdate : function(){
37275         this.updating = true;
37276     },
37277
37278     /**
37279      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37280      */
37281     endUpdate : function(){
37282         this.updating = false;
37283         this.autoSizeTabs();
37284     },
37285
37286     /**
37287      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37288      */
37289     autoSizeTabs : function(){
37290         var count = this.items.length;
37291         var vcount = count - this.hiddenCount;
37292         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37293             return;
37294         }
37295         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37296         var availWidth = Math.floor(w / vcount);
37297         var b = this.stripBody;
37298         if(b.getWidth() > w){
37299             var tabs = this.items;
37300             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37301             if(availWidth < this.minTabWidth){
37302                 /*if(!this.sleft){    // incomplete scrolling code
37303                     this.createScrollButtons();
37304                 }
37305                 this.showScroll();
37306                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37307             }
37308         }else{
37309             if(this.currentTabWidth < this.preferredTabWidth){
37310                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37311             }
37312         }
37313     },
37314
37315     /**
37316      * Returns the number of tabs in this TabPanel.
37317      * @return {Number}
37318      */
37319      getCount : function(){
37320          return this.items.length;
37321      },
37322
37323     /**
37324      * Resizes all the tabs to the passed width
37325      * @param {Number} The new width
37326      */
37327     setTabWidth : function(width){
37328         this.currentTabWidth = width;
37329         for(var i = 0, len = this.items.length; i < len; i++) {
37330                 if(!this.items[i].isHidden()) {
37331                 this.items[i].setWidth(width);
37332             }
37333         }
37334     },
37335
37336     /**
37337      * Destroys this TabPanel
37338      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37339      */
37340     destroy : function(removeEl){
37341         Roo.EventManager.removeResizeListener(this.onResize, this);
37342         for(var i = 0, len = this.items.length; i < len; i++){
37343             this.items[i].purgeListeners();
37344         }
37345         if(removeEl === true){
37346             this.el.update("");
37347             this.el.remove();
37348         }
37349     },
37350     
37351     createStrip : function(container)
37352     {
37353         var strip = document.createElement("nav");
37354         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37355         container.appendChild(strip);
37356         return strip;
37357     },
37358     
37359     createStripList : function(strip)
37360     {
37361         // div wrapper for retard IE
37362         // returns the "tr" element.
37363         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37364         //'<div class="x-tabs-strip-wrap">'+
37365           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37366           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37367         return strip.firstChild; //.firstChild.firstChild.firstChild;
37368     },
37369     createBody : function(container)
37370     {
37371         var body = document.createElement("div");
37372         Roo.id(body, "tab-body");
37373         //Roo.fly(body).addClass("x-tabs-body");
37374         Roo.fly(body).addClass("tab-content");
37375         container.appendChild(body);
37376         return body;
37377     },
37378     createItemBody :function(bodyEl, id){
37379         var body = Roo.getDom(id);
37380         if(!body){
37381             body = document.createElement("div");
37382             body.id = id;
37383         }
37384         //Roo.fly(body).addClass("x-tabs-item-body");
37385         Roo.fly(body).addClass("tab-pane");
37386          bodyEl.insertBefore(body, bodyEl.firstChild);
37387         return body;
37388     },
37389     /** @private */
37390     createStripElements :  function(stripEl, text, closable, tpl)
37391     {
37392         var td = document.createElement("li"); // was td..
37393         
37394         
37395         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37396         
37397         
37398         stripEl.appendChild(td);
37399         /*if(closable){
37400             td.className = "x-tabs-closable";
37401             if(!this.closeTpl){
37402                 this.closeTpl = new Roo.Template(
37403                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37404                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37405                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37406                 );
37407             }
37408             var el = this.closeTpl.overwrite(td, {"text": text});
37409             var close = el.getElementsByTagName("div")[0];
37410             var inner = el.getElementsByTagName("em")[0];
37411             return {"el": el, "close": close, "inner": inner};
37412         } else {
37413         */
37414         // not sure what this is..
37415 //            if(!this.tabTpl){
37416                 //this.tabTpl = new Roo.Template(
37417                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37418                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37419                 //);
37420 //                this.tabTpl = new Roo.Template(
37421 //                   '<a href="#">' +
37422 //                   '<span unselectable="on"' +
37423 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37424 //                            ' >{text}</span></a>'
37425 //                );
37426 //                
37427 //            }
37428
37429
37430             var template = tpl || this.tabTpl || false;
37431             
37432             if(!template){
37433                 
37434                 template = new Roo.Template(
37435                    '<a href="#">' +
37436                    '<span unselectable="on"' +
37437                             (this.disableTooltips ? '' : ' title="{text}"') +
37438                             ' >{text}</span></a>'
37439                 );
37440             }
37441             
37442             switch (typeof(template)) {
37443                 case 'object' :
37444                     break;
37445                 case 'string' :
37446                     template = new Roo.Template(template);
37447                     break;
37448                 default :
37449                     break;
37450             }
37451             
37452             var el = template.overwrite(td, {"text": text});
37453             
37454             var inner = el.getElementsByTagName("span")[0];
37455             
37456             return {"el": el, "inner": inner};
37457             
37458     }
37459         
37460     
37461 });
37462
37463 /**
37464  * @class Roo.TabPanelItem
37465  * @extends Roo.util.Observable
37466  * Represents an individual item (tab plus body) in a TabPanel.
37467  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37468  * @param {String} id The id of this TabPanelItem
37469  * @param {String} text The text for the tab of this TabPanelItem
37470  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37471  */
37472 Roo.bootstrap.panel.TabItem = function(config){
37473     /**
37474      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37475      * @type Roo.TabPanel
37476      */
37477     this.tabPanel = config.panel;
37478     /**
37479      * The id for this TabPanelItem
37480      * @type String
37481      */
37482     this.id = config.id;
37483     /** @private */
37484     this.disabled = false;
37485     /** @private */
37486     this.text = config.text;
37487     /** @private */
37488     this.loaded = false;
37489     this.closable = config.closable;
37490
37491     /**
37492      * The body element for this TabPanelItem.
37493      * @type Roo.Element
37494      */
37495     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37496     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37497     this.bodyEl.setStyle("display", "block");
37498     this.bodyEl.setStyle("zoom", "1");
37499     //this.hideAction();
37500
37501     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37502     /** @private */
37503     this.el = Roo.get(els.el);
37504     this.inner = Roo.get(els.inner, true);
37505     this.textEl = Roo.get(this.el.dom.firstChild, true);
37506     this.pnode = Roo.get(els.el.parentNode, true);
37507     this.el.on("mousedown", this.onTabMouseDown, this);
37508     this.el.on("click", this.onTabClick, this);
37509     /** @private */
37510     if(config.closable){
37511         var c = Roo.get(els.close, true);
37512         c.dom.title = this.closeText;
37513         c.addClassOnOver("close-over");
37514         c.on("click", this.closeClick, this);
37515      }
37516
37517     this.addEvents({
37518          /**
37519          * @event activate
37520          * Fires when this tab becomes the active tab.
37521          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37522          * @param {Roo.TabPanelItem} this
37523          */
37524         "activate": true,
37525         /**
37526          * @event beforeclose
37527          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37528          * @param {Roo.TabPanelItem} this
37529          * @param {Object} e Set cancel to true on this object to cancel the close.
37530          */
37531         "beforeclose": true,
37532         /**
37533          * @event close
37534          * Fires when this tab is closed.
37535          * @param {Roo.TabPanelItem} this
37536          */
37537          "close": true,
37538         /**
37539          * @event deactivate
37540          * Fires when this tab is no longer the active tab.
37541          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37542          * @param {Roo.TabPanelItem} this
37543          */
37544          "deactivate" : true
37545     });
37546     this.hidden = false;
37547
37548     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37549 };
37550
37551 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37552            {
37553     purgeListeners : function(){
37554        Roo.util.Observable.prototype.purgeListeners.call(this);
37555        this.el.removeAllListeners();
37556     },
37557     /**
37558      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37559      */
37560     show : function(){
37561         this.pnode.addClass("active");
37562         this.showAction();
37563         if(Roo.isOpera){
37564             this.tabPanel.stripWrap.repaint();
37565         }
37566         this.fireEvent("activate", this.tabPanel, this);
37567     },
37568
37569     /**
37570      * Returns true if this tab is the active tab.
37571      * @return {Boolean}
37572      */
37573     isActive : function(){
37574         return this.tabPanel.getActiveTab() == this;
37575     },
37576
37577     /**
37578      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37579      */
37580     hide : function(){
37581         this.pnode.removeClass("active");
37582         this.hideAction();
37583         this.fireEvent("deactivate", this.tabPanel, this);
37584     },
37585
37586     hideAction : function(){
37587         this.bodyEl.hide();
37588         this.bodyEl.setStyle("position", "absolute");
37589         this.bodyEl.setLeft("-20000px");
37590         this.bodyEl.setTop("-20000px");
37591     },
37592
37593     showAction : function(){
37594         this.bodyEl.setStyle("position", "relative");
37595         this.bodyEl.setTop("");
37596         this.bodyEl.setLeft("");
37597         this.bodyEl.show();
37598     },
37599
37600     /**
37601      * Set the tooltip for the tab.
37602      * @param {String} tooltip The tab's tooltip
37603      */
37604     setTooltip : function(text){
37605         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37606             this.textEl.dom.qtip = text;
37607             this.textEl.dom.removeAttribute('title');
37608         }else{
37609             this.textEl.dom.title = text;
37610         }
37611     },
37612
37613     onTabClick : function(e){
37614         e.preventDefault();
37615         this.tabPanel.activate(this.id);
37616     },
37617
37618     onTabMouseDown : function(e){
37619         e.preventDefault();
37620         this.tabPanel.activate(this.id);
37621     },
37622 /*
37623     getWidth : function(){
37624         return this.inner.getWidth();
37625     },
37626
37627     setWidth : function(width){
37628         var iwidth = width - this.pnode.getPadding("lr");
37629         this.inner.setWidth(iwidth);
37630         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37631         this.pnode.setWidth(width);
37632     },
37633 */
37634     /**
37635      * Show or hide the tab
37636      * @param {Boolean} hidden True to hide or false to show.
37637      */
37638     setHidden : function(hidden){
37639         this.hidden = hidden;
37640         this.pnode.setStyle("display", hidden ? "none" : "");
37641     },
37642
37643     /**
37644      * Returns true if this tab is "hidden"
37645      * @return {Boolean}
37646      */
37647     isHidden : function(){
37648         return this.hidden;
37649     },
37650
37651     /**
37652      * Returns the text for this tab
37653      * @return {String}
37654      */
37655     getText : function(){
37656         return this.text;
37657     },
37658     /*
37659     autoSize : function(){
37660         //this.el.beginMeasure();
37661         this.textEl.setWidth(1);
37662         /*
37663          *  #2804 [new] Tabs in Roojs
37664          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37665          */
37666         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37667         //this.el.endMeasure();
37668     //},
37669
37670     /**
37671      * Sets the text for the tab (Note: this also sets the tooltip text)
37672      * @param {String} text The tab's text and tooltip
37673      */
37674     setText : function(text){
37675         this.text = text;
37676         this.textEl.update(text);
37677         this.setTooltip(text);
37678         //if(!this.tabPanel.resizeTabs){
37679         //    this.autoSize();
37680         //}
37681     },
37682     /**
37683      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37684      */
37685     activate : function(){
37686         this.tabPanel.activate(this.id);
37687     },
37688
37689     /**
37690      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37691      */
37692     disable : function(){
37693         if(this.tabPanel.active != this){
37694             this.disabled = true;
37695             this.pnode.addClass("disabled");
37696         }
37697     },
37698
37699     /**
37700      * Enables this TabPanelItem if it was previously disabled.
37701      */
37702     enable : function(){
37703         this.disabled = false;
37704         this.pnode.removeClass("disabled");
37705     },
37706
37707     /**
37708      * Sets the content for this TabPanelItem.
37709      * @param {String} content The content
37710      * @param {Boolean} loadScripts true to look for and load scripts
37711      */
37712     setContent : function(content, loadScripts){
37713         this.bodyEl.update(content, loadScripts);
37714     },
37715
37716     /**
37717      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37718      * @return {Roo.UpdateManager} The UpdateManager
37719      */
37720     getUpdateManager : function(){
37721         return this.bodyEl.getUpdateManager();
37722     },
37723
37724     /**
37725      * Set a URL to be used to load the content for this TabPanelItem.
37726      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37727      * @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)
37728      * @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)
37729      * @return {Roo.UpdateManager} The UpdateManager
37730      */
37731     setUrl : function(url, params, loadOnce){
37732         if(this.refreshDelegate){
37733             this.un('activate', this.refreshDelegate);
37734         }
37735         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37736         this.on("activate", this.refreshDelegate);
37737         return this.bodyEl.getUpdateManager();
37738     },
37739
37740     /** @private */
37741     _handleRefresh : function(url, params, loadOnce){
37742         if(!loadOnce || !this.loaded){
37743             var updater = this.bodyEl.getUpdateManager();
37744             updater.update(url, params, this._setLoaded.createDelegate(this));
37745         }
37746     },
37747
37748     /**
37749      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37750      *   Will fail silently if the setUrl method has not been called.
37751      *   This does not activate the panel, just updates its content.
37752      */
37753     refresh : function(){
37754         if(this.refreshDelegate){
37755            this.loaded = false;
37756            this.refreshDelegate();
37757         }
37758     },
37759
37760     /** @private */
37761     _setLoaded : function(){
37762         this.loaded = true;
37763     },
37764
37765     /** @private */
37766     closeClick : function(e){
37767         var o = {};
37768         e.stopEvent();
37769         this.fireEvent("beforeclose", this, o);
37770         if(o.cancel !== true){
37771             this.tabPanel.removeTab(this.id);
37772         }
37773     },
37774     /**
37775      * The text displayed in the tooltip for the close icon.
37776      * @type String
37777      */
37778     closeText : "Close this tab"
37779 });