Roo/bootstrap/ComboBox.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         if(!t.href.length || t.href == '#'){
2127             (function() { _this.hide(); }).defer(100);
2128         }
2129         
2130     },
2131     
2132     onMouseOver : function(e){
2133         var t  = this.findTargetItem(e);
2134         //Roo.log(t);
2135         //if(t){
2136         //    if(t.canActivate && !t.disabled){
2137         //        this.setActiveItem(t, true);
2138         //    }
2139         //}
2140         
2141         this.fireEvent("mouseover", this, e, t);
2142     },
2143     isVisible : function(){
2144         return !this.hidden;
2145     },
2146      onMouseOut : function(e){
2147         var t  = this.findTargetItem(e);
2148         
2149         //if(t ){
2150         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2151         //        this.activeItem.deactivate();
2152         //        delete this.activeItem;
2153         //    }
2154         //}
2155         this.fireEvent("mouseout", this, e, t);
2156     },
2157     
2158     
2159     /**
2160      * Displays this menu relative to another element
2161      * @param {String/HTMLElement/Roo.Element} element The element to align to
2162      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2163      * the element (defaults to this.defaultAlign)
2164      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2165      */
2166     show : function(el, pos, parentMenu){
2167         this.parentMenu = parentMenu;
2168         if(!this.el){
2169             this.render();
2170         }
2171         this.fireEvent("beforeshow", this);
2172         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2173     },
2174      /**
2175      * Displays this menu at a specific xy position
2176      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2177      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2178      */
2179     showAt : function(xy, parentMenu, /* private: */_e){
2180         this.parentMenu = parentMenu;
2181         if(!this.el){
2182             this.render();
2183         }
2184         if(_e !== false){
2185             this.fireEvent("beforeshow", this);
2186             //xy = this.el.adjustForConstraints(xy);
2187         }
2188         
2189         //this.el.show();
2190         this.hideMenuItems();
2191         this.hidden = false;
2192         this.triggerEl.addClass('open');
2193         
2194         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2195             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2196         }
2197         
2198         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2199             this.el.setXY(xy);
2200         }
2201         
2202         this.focus();
2203         this.fireEvent("show", this);
2204     },
2205     
2206     focus : function(){
2207         return;
2208         if(!this.hidden){
2209             this.doFocus.defer(50, this);
2210         }
2211     },
2212
2213     doFocus : function(){
2214         if(!this.hidden){
2215             this.focusEl.focus();
2216         }
2217     },
2218
2219     /**
2220      * Hides this menu and optionally all parent menus
2221      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2222      */
2223     hide : function(deep)
2224     {
2225         
2226         this.hideMenuItems();
2227         if(this.el && this.isVisible()){
2228             this.fireEvent("beforehide", this);
2229             if(this.activeItem){
2230                 this.activeItem.deactivate();
2231                 this.activeItem = null;
2232             }
2233             this.triggerEl.removeClass('open');;
2234             this.hidden = true;
2235             this.fireEvent("hide", this);
2236         }
2237         if(deep === true && this.parentMenu){
2238             this.parentMenu.hide(true);
2239         }
2240     },
2241     
2242     onTriggerClick : function(e)
2243     {
2244         Roo.log('trigger click');
2245         
2246         var target = e.getTarget();
2247         
2248         Roo.log(target.nodeName.toLowerCase());
2249         
2250         if(target.nodeName.toLowerCase() === 'i'){
2251             e.preventDefault();
2252         }
2253         
2254     },
2255     
2256     onTriggerPress  : function(e)
2257     {
2258         Roo.log('trigger press');
2259         //Roo.log(e.getTarget());
2260        // Roo.log(this.triggerEl.dom);
2261        
2262         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2263         var pel = Roo.get(e.getTarget());
2264         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2265             Roo.log('is treeview or dropdown?');
2266             return;
2267         }
2268         
2269         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2270             return;
2271         }
2272         
2273         if (this.isVisible()) {
2274             Roo.log('hide');
2275             this.hide();
2276         } else {
2277             Roo.log('show');
2278             this.show(this.triggerEl, false, false);
2279         }
2280         
2281         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2282             e.stopEvent();
2283         }
2284         
2285     },
2286        
2287     
2288     hideMenuItems : function()
2289     {
2290         Roo.log("hide Menu Items");
2291         if (!this.el) { 
2292             return;
2293         }
2294         //$(backdrop).remove()
2295         this.el.select('.open',true).each(function(aa) {
2296             
2297             aa.removeClass('open');
2298           //var parent = getParent($(this))
2299           //var relatedTarget = { relatedTarget: this }
2300           
2301            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2302           //if (e.isDefaultPrevented()) return
2303            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2304         });
2305     },
2306     addxtypeChild : function (tree, cntr) {
2307         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2308           
2309         this.menuitems.add(comp);
2310         return comp;
2311
2312     },
2313     getEl : function()
2314     {
2315         Roo.log(this.el);
2316         return this.el;
2317     }
2318 });
2319
2320  
2321  /*
2322  * - LGPL
2323  *
2324  * menu item
2325  * 
2326  */
2327
2328
2329 /**
2330  * @class Roo.bootstrap.MenuItem
2331  * @extends Roo.bootstrap.Component
2332  * Bootstrap MenuItem class
2333  * @cfg {String} html the menu label
2334  * @cfg {String} href the link
2335  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2336  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2337  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2338  * @cfg {String} fa favicon to show on left of menu item.
2339  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2340  * 
2341  * 
2342  * @constructor
2343  * Create a new MenuItem
2344  * @param {Object} config The config object
2345  */
2346
2347
2348 Roo.bootstrap.MenuItem = function(config){
2349     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2350     this.addEvents({
2351         // raw events
2352         /**
2353          * @event click
2354          * The raw click event for the entire grid.
2355          * @param {Roo.bootstrap.MenuItem} this
2356          * @param {Roo.EventObject} e
2357          */
2358         "click" : true
2359     });
2360 };
2361
2362 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2363     
2364     href : false,
2365     html : false,
2366     preventDefault: false,
2367     isContainer : false,
2368     active : false,
2369     fa: false,
2370     
2371     getAutoCreate : function(){
2372         
2373         if(this.isContainer){
2374             return {
2375                 tag: 'li',
2376                 cls: 'dropdown-menu-item'
2377             };
2378         }
2379         var ctag = {
2380             tag: 'span',
2381             html: 'Link'
2382         };
2383         
2384         var anc = {
2385             tag : 'a',
2386             href : '#',
2387             cn : [  ]
2388         };
2389         
2390         if (this.fa !== false) {
2391             anc.cn.push({
2392                 tag : 'i',
2393                 cls : 'fa fa-' + this.fa
2394             });
2395         }
2396         
2397         anc.cn.push(ctag);
2398         
2399         
2400         var cfg= {
2401             tag: 'li',
2402             cls: 'dropdown-menu-item',
2403             cn: [ anc ]
2404         };
2405         if (this.parent().type == 'treeview') {
2406             cfg.cls = 'treeview-menu';
2407         }
2408         if (this.active) {
2409             cfg.cls += ' active';
2410         }
2411         
2412         
2413         
2414         anc.href = this.href || cfg.cn[0].href ;
2415         ctag.html = this.html || cfg.cn[0].html ;
2416         return cfg;
2417     },
2418     
2419     initEvents: function()
2420     {
2421         if (this.parent().type == 'treeview') {
2422             this.el.select('a').on('click', this.onClick, this);
2423         }
2424         
2425         if (this.menu) {
2426             this.menu.parentType = this.xtype;
2427             this.menu.triggerEl = this.el;
2428             this.menu = this.addxtype(Roo.apply({}, this.menu));
2429         }
2430         
2431     },
2432     onClick : function(e)
2433     {
2434         Roo.log('item on click ');
2435         
2436         if(this.preventDefault){
2437             e.preventDefault();
2438         }
2439         //this.parent().hideMenuItems();
2440         
2441         this.fireEvent('click', this, e);
2442     },
2443     getEl : function()
2444     {
2445         return this.el;
2446     } 
2447 });
2448
2449  
2450
2451  /*
2452  * - LGPL
2453  *
2454  * menu separator
2455  * 
2456  */
2457
2458
2459 /**
2460  * @class Roo.bootstrap.MenuSeparator
2461  * @extends Roo.bootstrap.Component
2462  * Bootstrap MenuSeparator class
2463  * 
2464  * @constructor
2465  * Create a new MenuItem
2466  * @param {Object} config The config object
2467  */
2468
2469
2470 Roo.bootstrap.MenuSeparator = function(config){
2471     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2472 };
2473
2474 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2475     
2476     getAutoCreate : function(){
2477         var cfg = {
2478             cls: 'divider',
2479             tag : 'li'
2480         };
2481         
2482         return cfg;
2483     }
2484    
2485 });
2486
2487  
2488
2489  
2490 /*
2491 * Licence: LGPL
2492 */
2493
2494 /**
2495  * @class Roo.bootstrap.Modal
2496  * @extends Roo.bootstrap.Component
2497  * Bootstrap Modal class
2498  * @cfg {String} title Title of dialog
2499  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2500  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2501  * @cfg {Boolean} specificTitle default false
2502  * @cfg {Array} buttons Array of buttons or standard button set..
2503  * @cfg {String} buttonPosition (left|right|center) default right
2504  * @cfg {Boolean} animate default true
2505  * @cfg {Boolean} allow_close default true
2506  * @cfg {Boolean} fitwindow default false
2507  * @cfg {String} size (sm|lg) default empty
2508  *
2509  *
2510  * @constructor
2511  * Create a new Modal Dialog
2512  * @param {Object} config The config object
2513  */
2514
2515 Roo.bootstrap.Modal = function(config){
2516     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2517     this.addEvents({
2518         // raw events
2519         /**
2520          * @event btnclick
2521          * The raw btnclick event for the button
2522          * @param {Roo.EventObject} e
2523          */
2524         "btnclick" : true,
2525         /**
2526          * @event resize
2527          * Fire when dialog resize
2528          * @param {Roo.bootstrap.Modal} this
2529          * @param {Roo.EventObject} e
2530          */
2531         "resize" : true
2532     });
2533     this.buttons = this.buttons || [];
2534
2535     if (this.tmpl) {
2536         this.tmpl = Roo.factory(this.tmpl);
2537     }
2538
2539 };
2540
2541 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2542
2543     title : 'test dialog',
2544
2545     buttons : false,
2546
2547     // set on load...
2548
2549     html: false,
2550
2551     tmp: false,
2552
2553     specificTitle: false,
2554
2555     buttonPosition: 'right',
2556
2557     allow_close : true,
2558
2559     animate : true,
2560
2561     fitwindow: false,
2562
2563
2564      // private
2565     dialogEl: false,
2566     bodyEl:  false,
2567     footerEl:  false,
2568     titleEl:  false,
2569     closeEl:  false,
2570
2571     size: '',
2572
2573
2574     onRender : function(ct, position)
2575     {
2576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2577
2578         if(!this.el){
2579             var cfg = Roo.apply({},  this.getAutoCreate());
2580             cfg.id = Roo.id();
2581             //if(!cfg.name){
2582             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2583             //}
2584             //if (!cfg.name.length) {
2585             //    delete cfg.name;
2586            // }
2587             if (this.cls) {
2588                 cfg.cls += ' ' + this.cls;
2589             }
2590             if (this.style) {
2591                 cfg.style = this.style;
2592             }
2593             this.el = Roo.get(document.body).createChild(cfg, position);
2594         }
2595         //var type = this.el.dom.type;
2596
2597
2598         if(this.tabIndex !== undefined){
2599             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2600         }
2601
2602         this.dialogEl = this.el.select('.modal-dialog',true).first();
2603         this.bodyEl = this.el.select('.modal-body',true).first();
2604         this.closeEl = this.el.select('.modal-header .close', true).first();
2605         this.headerEl = this.el.select('.modal-header',true).first();
2606         this.titleEl = this.el.select('.modal-title',true).first();
2607         this.footerEl = this.el.select('.modal-footer',true).first();
2608
2609         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2610         this.maskEl.enableDisplayMode("block");
2611         this.maskEl.hide();
2612         //this.el.addClass("x-dlg-modal");
2613
2614         if (this.buttons.length) {
2615             Roo.each(this.buttons, function(bb) {
2616                 var b = Roo.apply({}, bb);
2617                 b.xns = b.xns || Roo.bootstrap;
2618                 b.xtype = b.xtype || 'Button';
2619                 if (typeof(b.listeners) == 'undefined') {
2620                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2621                 }
2622
2623                 var btn = Roo.factory(b);
2624
2625                 btn.render(this.el.select('.modal-footer div').first());
2626
2627             },this);
2628         }
2629         // render the children.
2630         var nitems = [];
2631
2632         if(typeof(this.items) != 'undefined'){
2633             var items = this.items;
2634             delete this.items;
2635
2636             for(var i =0;i < items.length;i++) {
2637                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2638             }
2639         }
2640
2641         this.items = nitems;
2642
2643         // where are these used - they used to be body/close/footer
2644
2645
2646         this.initEvents();
2647         //this.el.addClass([this.fieldClass, this.cls]);
2648
2649     },
2650
2651     getAutoCreate : function(){
2652
2653
2654         var bdy = {
2655                 cls : 'modal-body',
2656                 html : this.html || ''
2657         };
2658
2659         var title = {
2660             tag: 'h4',
2661             cls : 'modal-title',
2662             html : this.title
2663         };
2664
2665         if(this.specificTitle){
2666             title = this.title;
2667
2668         };
2669
2670         var header = [];
2671         if (this.allow_close) {
2672             header.push({
2673                 tag: 'button',
2674                 cls : 'close',
2675                 html : '&times'
2676             });
2677         }
2678
2679         header.push(title);
2680
2681         var size = '';
2682
2683         if(this.size.length){
2684             size = 'modal-' + this.size;
2685         }
2686
2687         var modal = {
2688             cls: "modal",
2689             style : 'display: none',
2690             cn : [
2691                 {
2692                     cls: "modal-dialog " + size,
2693                     cn : [
2694                         {
2695                             cls : "modal-content",
2696                             cn : [
2697                                 {
2698                                     cls : 'modal-header',
2699                                     cn : header
2700                                 },
2701                                 bdy,
2702                                 {
2703                                     cls : 'modal-footer',
2704                                     cn : [
2705                                         {
2706                                             tag: 'div',
2707                                             cls: 'btn-' + this.buttonPosition
2708                                         }
2709                                     ]
2710
2711                                 }
2712
2713
2714                             ]
2715
2716                         }
2717                     ]
2718
2719                 }
2720             ]
2721         };
2722
2723         if(this.animate){
2724             modal.cls += ' fade';
2725         }
2726
2727         return modal;
2728
2729     },
2730     getChildContainer : function() {
2731
2732          return this.bodyEl;
2733
2734     },
2735     getButtonContainer : function() {
2736          return this.el.select('.modal-footer div',true).first();
2737
2738     },
2739     initEvents : function()
2740     {
2741         if (this.allow_close) {
2742             this.closeEl.on('click', this.hide, this);
2743         }
2744         Roo.EventManager.onWindowResize(this.resize, this, true);
2745
2746
2747     },
2748
2749     resize : function()
2750     {
2751         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2752         if (this.fitwindow) {
2753             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2754             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2755             this.setSize(w,h);
2756         }
2757     },
2758
2759     setSize : function(w,h)
2760     {
2761         if (!w && !h) {
2762             return;
2763         }
2764         this.resizeTo(w,h);
2765     },
2766
2767     show : function() {
2768
2769         if (!this.rendered) {
2770             this.render();
2771         }
2772
2773         this.el.setStyle('display', 'block');
2774
2775         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2776             var _this = this;
2777             (function(){
2778                 this.el.addClass('in');
2779             }).defer(50, this);
2780         }else{
2781             this.el.addClass('in');
2782
2783         }
2784
2785         // not sure how we can show data in here..
2786         //if (this.tmpl) {
2787         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2788         //}
2789
2790         Roo.get(document.body).addClass("x-body-masked");
2791         
2792         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2793         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2794         this.maskEl.show();
2795         
2796         this.resize();
2797         
2798         this.fireEvent('show', this);
2799
2800         // set zindex here - otherwise it appears to be ignored...
2801         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2802
2803         (function () {
2804             this.items.forEach( function(e) {
2805                 e.layout ? e.layout() : false;
2806
2807             });
2808         }).defer(100,this);
2809
2810     },
2811     hide : function()
2812     {
2813         if(this.fireEvent("beforehide", this) !== false){
2814             this.maskEl.hide();
2815             Roo.get(document.body).removeClass("x-body-masked");
2816             this.el.removeClass('in');
2817             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2818
2819             if(this.animate){ // why
2820                 var _this = this;
2821                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2822             }else{
2823                 this.el.setStyle('display', 'none');
2824             }
2825             this.fireEvent('hide', this);
2826         }
2827     },
2828
2829     addButton : function(str, cb)
2830     {
2831
2832
2833         var b = Roo.apply({}, { html : str } );
2834         b.xns = b.xns || Roo.bootstrap;
2835         b.xtype = b.xtype || 'Button';
2836         if (typeof(b.listeners) == 'undefined') {
2837             b.listeners = { click : cb.createDelegate(this)  };
2838         }
2839
2840         var btn = Roo.factory(b);
2841
2842         btn.render(this.el.select('.modal-footer div').first());
2843
2844         return btn;
2845
2846     },
2847
2848     setDefaultButton : function(btn)
2849     {
2850         //this.el.select('.modal-footer').()
2851     },
2852     diff : false,
2853
2854     resizeTo: function(w,h)
2855     {
2856         // skip.. ?? why??
2857
2858         this.dialogEl.setWidth(w);
2859         if (this.diff === false) {
2860             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2861         }
2862
2863         this.bodyEl.setHeight(h-this.diff);
2864
2865         this.fireEvent('resize', this);
2866
2867     },
2868     setContentSize  : function(w, h)
2869     {
2870
2871     },
2872     onButtonClick: function(btn,e)
2873     {
2874         //Roo.log([a,b,c]);
2875         this.fireEvent('btnclick', btn.name, e);
2876     },
2877      /**
2878      * Set the title of the Dialog
2879      * @param {String} str new Title
2880      */
2881     setTitle: function(str) {
2882         this.titleEl.dom.innerHTML = str;
2883     },
2884     /**
2885      * Set the body of the Dialog
2886      * @param {String} str new Title
2887      */
2888     setBody: function(str) {
2889         this.bodyEl.dom.innerHTML = str;
2890     },
2891     /**
2892      * Set the body of the Dialog using the template
2893      * @param {Obj} data - apply this data to the template and replace the body contents.
2894      */
2895     applyBody: function(obj)
2896     {
2897         if (!this.tmpl) {
2898             Roo.log("Error - using apply Body without a template");
2899             //code
2900         }
2901         this.tmpl.overwrite(this.bodyEl, obj);
2902     }
2903
2904 });
2905
2906
2907 Roo.apply(Roo.bootstrap.Modal,  {
2908     /**
2909          * Button config that displays a single OK button
2910          * @type Object
2911          */
2912         OK :  [{
2913             name : 'ok',
2914             weight : 'primary',
2915             html : 'OK'
2916         }],
2917         /**
2918          * Button config that displays Yes and No buttons
2919          * @type Object
2920          */
2921         YESNO : [
2922             {
2923                 name  : 'no',
2924                 html : 'No'
2925             },
2926             {
2927                 name  :'yes',
2928                 weight : 'primary',
2929                 html : 'Yes'
2930             }
2931         ],
2932
2933         /**
2934          * Button config that displays OK and Cancel buttons
2935          * @type Object
2936          */
2937         OKCANCEL : [
2938             {
2939                name : 'cancel',
2940                 html : 'Cancel'
2941             },
2942             {
2943                 name : 'ok',
2944                 weight : 'primary',
2945                 html : 'OK'
2946             }
2947         ],
2948         /**
2949          * Button config that displays Yes, No and Cancel buttons
2950          * @type Object
2951          */
2952         YESNOCANCEL : [
2953             {
2954                 name : 'yes',
2955                 weight : 'primary',
2956                 html : 'Yes'
2957             },
2958             {
2959                 name : 'no',
2960                 html : 'No'
2961             },
2962             {
2963                 name : 'cancel',
2964                 html : 'Cancel'
2965             }
2966         ],
2967         
2968         zIndex : 10001
2969 });
2970 /*
2971  * - LGPL
2972  *
2973  * messagebox - can be used as a replace
2974  * 
2975  */
2976 /**
2977  * @class Roo.MessageBox
2978  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2979  * Example usage:
2980  *<pre><code>
2981 // Basic alert:
2982 Roo.Msg.alert('Status', 'Changes saved successfully.');
2983
2984 // Prompt for user data:
2985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2986     if (btn == 'ok'){
2987         // process text value...
2988     }
2989 });
2990
2991 // Show a dialog using config options:
2992 Roo.Msg.show({
2993    title:'Save Changes?',
2994    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2995    buttons: Roo.Msg.YESNOCANCEL,
2996    fn: processResult,
2997    animEl: 'elId'
2998 });
2999 </code></pre>
3000  * @singleton
3001  */
3002 Roo.bootstrap.MessageBox = function(){
3003     var dlg, opt, mask, waitTimer;
3004     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3005     var buttons, activeTextEl, bwidth;
3006
3007     
3008     // private
3009     var handleButton = function(button){
3010         dlg.hide();
3011         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3012     };
3013
3014     // private
3015     var handleHide = function(){
3016         if(opt && opt.cls){
3017             dlg.el.removeClass(opt.cls);
3018         }
3019         //if(waitTimer){
3020         //    Roo.TaskMgr.stop(waitTimer);
3021         //    waitTimer = null;
3022         //}
3023     };
3024
3025     // private
3026     var updateButtons = function(b){
3027         var width = 0;
3028         if(!b){
3029             buttons["ok"].hide();
3030             buttons["cancel"].hide();
3031             buttons["yes"].hide();
3032             buttons["no"].hide();
3033             //dlg.footer.dom.style.display = 'none';
3034             return width;
3035         }
3036         dlg.footerEl.dom.style.display = '';
3037         for(var k in buttons){
3038             if(typeof buttons[k] != "function"){
3039                 if(b[k]){
3040                     buttons[k].show();
3041                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3042                     width += buttons[k].el.getWidth()+15;
3043                 }else{
3044                     buttons[k].hide();
3045                 }
3046             }
3047         }
3048         return width;
3049     };
3050
3051     // private
3052     var handleEsc = function(d, k, e){
3053         if(opt && opt.closable !== false){
3054             dlg.hide();
3055         }
3056         if(e){
3057             e.stopEvent();
3058         }
3059     };
3060
3061     return {
3062         /**
3063          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3064          * @return {Roo.BasicDialog} The BasicDialog element
3065          */
3066         getDialog : function(){
3067            if(!dlg){
3068                 dlg = new Roo.bootstrap.Modal( {
3069                     //draggable: true,
3070                     //resizable:false,
3071                     //constraintoviewport:false,
3072                     //fixedcenter:true,
3073                     //collapsible : false,
3074                     //shim:true,
3075                     //modal: true,
3076                 //    width: 'auto',
3077                   //  height:100,
3078                     //buttonAlign:"center",
3079                     closeClick : function(){
3080                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3081                             handleButton("no");
3082                         }else{
3083                             handleButton("cancel");
3084                         }
3085                     }
3086                 });
3087                 dlg.render();
3088                 dlg.on("hide", handleHide);
3089                 mask = dlg.mask;
3090                 //dlg.addKeyListener(27, handleEsc);
3091                 buttons = {};
3092                 this.buttons = buttons;
3093                 var bt = this.buttonText;
3094                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3095                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3096                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3097                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3098                 //Roo.log(buttons);
3099                 bodyEl = dlg.bodyEl.createChild({
3100
3101                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3102                         '<textarea class="roo-mb-textarea"></textarea>' +
3103                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3104                 });
3105                 msgEl = bodyEl.dom.firstChild;
3106                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3107                 textboxEl.enableDisplayMode();
3108                 textboxEl.addKeyListener([10,13], function(){
3109                     if(dlg.isVisible() && opt && opt.buttons){
3110                         if(opt.buttons.ok){
3111                             handleButton("ok");
3112                         }else if(opt.buttons.yes){
3113                             handleButton("yes");
3114                         }
3115                     }
3116                 });
3117                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3118                 textareaEl.enableDisplayMode();
3119                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3120                 progressEl.enableDisplayMode();
3121                 
3122                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3123                 //var pf = progressEl.dom.firstChild;
3124                 //if (pf) {
3125                     //pp = Roo.get(pf.firstChild);
3126                     //pp.setHeight(pf.offsetHeight);
3127                 //}
3128                 
3129             }
3130             return dlg;
3131         },
3132
3133         /**
3134          * Updates the message box body text
3135          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3136          * the XHTML-compliant non-breaking space character '&amp;#160;')
3137          * @return {Roo.MessageBox} This message box
3138          */
3139         updateText : function(text)
3140         {
3141             if(!dlg.isVisible() && !opt.width){
3142                 dlg.dialogEl.setWidth(this.maxWidth);
3143                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3144             }
3145             msgEl.innerHTML = text || '&#160;';
3146       
3147             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3148             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3149             var w = Math.max(
3150                     Math.min(opt.width || cw , this.maxWidth), 
3151                     Math.max(opt.minWidth || this.minWidth, bwidth)
3152             );
3153             if(opt.prompt){
3154                 activeTextEl.setWidth(w);
3155             }
3156             if(dlg.isVisible()){
3157                 dlg.fixedcenter = false;
3158             }
3159             // to big, make it scroll. = But as usual stupid IE does not support
3160             // !important..
3161             
3162             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3163                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3164                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3165             } else {
3166                 bodyEl.dom.style.height = '';
3167                 bodyEl.dom.style.overflowY = '';
3168             }
3169             if (cw > w) {
3170                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3171             } else {
3172                 bodyEl.dom.style.overflowX = '';
3173             }
3174             
3175             dlg.setContentSize(w, bodyEl.getHeight());
3176             if(dlg.isVisible()){
3177                 dlg.fixedcenter = true;
3178             }
3179             return this;
3180         },
3181
3182         /**
3183          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3184          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3185          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3186          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3187          * @return {Roo.MessageBox} This message box
3188          */
3189         updateProgress : function(value, text){
3190             if(text){
3191                 this.updateText(text);
3192             }
3193             if (pp) { // weird bug on my firefox - for some reason this is not defined
3194                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3195             }
3196             return this;
3197         },        
3198
3199         /**
3200          * Returns true if the message box is currently displayed
3201          * @return {Boolean} True if the message box is visible, else false
3202          */
3203         isVisible : function(){
3204             return dlg && dlg.isVisible();  
3205         },
3206
3207         /**
3208          * Hides the message box if it is displayed
3209          */
3210         hide : function(){
3211             if(this.isVisible()){
3212                 dlg.hide();
3213             }  
3214         },
3215
3216         /**
3217          * Displays a new message box, or reinitializes an existing message box, based on the config options
3218          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3219          * The following config object properties are supported:
3220          * <pre>
3221 Property    Type             Description
3222 ----------  ---------------  ------------------------------------------------------------------------------------
3223 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3224                                    closes (defaults to undefined)
3225 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3226                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3227 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3228                                    progress and wait dialogs will ignore this property and always hide the
3229                                    close button as they can only be closed programmatically.
3230 cls               String           A custom CSS class to apply to the message box element
3231 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3232                                    displayed (defaults to 75)
3233 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3234                                    function will be btn (the name of the button that was clicked, if applicable,
3235                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3236                                    Progress and wait dialogs will ignore this option since they do not respond to
3237                                    user actions and can only be closed programmatically, so any required function
3238                                    should be called by the same code after it closes the dialog.
3239 icon              String           A CSS class that provides a background image to be used as an icon for
3240                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3241 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3242 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3243 modal             Boolean          False to allow user interaction with the page while the message box is
3244                                    displayed (defaults to true)
3245 msg               String           A string that will replace the existing message box body text (defaults
3246                                    to the XHTML-compliant non-breaking space character '&#160;')
3247 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3248 progress          Boolean          True to display a progress bar (defaults to false)
3249 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3250 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3251 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3252 title             String           The title text
3253 value             String           The string value to set into the active textbox element if displayed
3254 wait              Boolean          True to display a progress bar (defaults to false)
3255 width             Number           The width of the dialog in pixels
3256 </pre>
3257          *
3258          * Example usage:
3259          * <pre><code>
3260 Roo.Msg.show({
3261    title: 'Address',
3262    msg: 'Please enter your address:',
3263    width: 300,
3264    buttons: Roo.MessageBox.OKCANCEL,
3265    multiline: true,
3266    fn: saveAddress,
3267    animEl: 'addAddressBtn'
3268 });
3269 </code></pre>
3270          * @param {Object} config Configuration options
3271          * @return {Roo.MessageBox} This message box
3272          */
3273         show : function(options)
3274         {
3275             
3276             // this causes nightmares if you show one dialog after another
3277             // especially on callbacks..
3278              
3279             if(this.isVisible()){
3280                 
3281                 this.hide();
3282                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3283                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3284                 Roo.log("New Dialog Message:" +  options.msg )
3285                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3286                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3287                 
3288             }
3289             var d = this.getDialog();
3290             opt = options;
3291             d.setTitle(opt.title || "&#160;");
3292             d.closeEl.setDisplayed(opt.closable !== false);
3293             activeTextEl = textboxEl;
3294             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3295             if(opt.prompt){
3296                 if(opt.multiline){
3297                     textboxEl.hide();
3298                     textareaEl.show();
3299                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3300                         opt.multiline : this.defaultTextHeight);
3301                     activeTextEl = textareaEl;
3302                 }else{
3303                     textboxEl.show();
3304                     textareaEl.hide();
3305                 }
3306             }else{
3307                 textboxEl.hide();
3308                 textareaEl.hide();
3309             }
3310             progressEl.setDisplayed(opt.progress === true);
3311             this.updateProgress(0);
3312             activeTextEl.dom.value = opt.value || "";
3313             if(opt.prompt){
3314                 dlg.setDefaultButton(activeTextEl);
3315             }else{
3316                 var bs = opt.buttons;
3317                 var db = null;
3318                 if(bs && bs.ok){
3319                     db = buttons["ok"];
3320                 }else if(bs && bs.yes){
3321                     db = buttons["yes"];
3322                 }
3323                 dlg.setDefaultButton(db);
3324             }
3325             bwidth = updateButtons(opt.buttons);
3326             this.updateText(opt.msg);
3327             if(opt.cls){
3328                 d.el.addClass(opt.cls);
3329             }
3330             d.proxyDrag = opt.proxyDrag === true;
3331             d.modal = opt.modal !== false;
3332             d.mask = opt.modal !== false ? mask : false;
3333             if(!d.isVisible()){
3334                 // force it to the end of the z-index stack so it gets a cursor in FF
3335                 document.body.appendChild(dlg.el.dom);
3336                 d.animateTarget = null;
3337                 d.show(options.animEl);
3338             }
3339             return this;
3340         },
3341
3342         /**
3343          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3344          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3345          * and closing the message box when the process is complete.
3346          * @param {String} title The title bar text
3347          * @param {String} msg The message box body text
3348          * @return {Roo.MessageBox} This message box
3349          */
3350         progress : function(title, msg){
3351             this.show({
3352                 title : title,
3353                 msg : msg,
3354                 buttons: false,
3355                 progress:true,
3356                 closable:false,
3357                 minWidth: this.minProgressWidth,
3358                 modal : true
3359             });
3360             return this;
3361         },
3362
3363         /**
3364          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3365          * If a callback function is passed it will be called after the user clicks the button, and the
3366          * id of the button that was clicked will be passed as the only parameter to the callback
3367          * (could also be the top-right close button).
3368          * @param {String} title The title bar text
3369          * @param {String} msg The message box body text
3370          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3371          * @param {Object} scope (optional) The scope of the callback function
3372          * @return {Roo.MessageBox} This message box
3373          */
3374         alert : function(title, msg, fn, scope)
3375         {
3376             this.show({
3377                 title : title,
3378                 msg : msg,
3379                 buttons: this.OK,
3380                 fn: fn,
3381                 closable : false,
3382                 scope : scope,
3383                 modal : true
3384             });
3385             return this;
3386         },
3387
3388         /**
3389          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3390          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3391          * You are responsible for closing the message box when the process is complete.
3392          * @param {String} msg The message box body text
3393          * @param {String} title (optional) The title bar text
3394          * @return {Roo.MessageBox} This message box
3395          */
3396         wait : function(msg, title){
3397             this.show({
3398                 title : title,
3399                 msg : msg,
3400                 buttons: false,
3401                 closable:false,
3402                 progress:true,
3403                 modal:true,
3404                 width:300,
3405                 wait:true
3406             });
3407             waitTimer = Roo.TaskMgr.start({
3408                 run: function(i){
3409                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3410                 },
3411                 interval: 1000
3412             });
3413             return this;
3414         },
3415
3416         /**
3417          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3418          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3419          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3420          * @param {String} title The title bar text
3421          * @param {String} msg The message box body text
3422          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3423          * @param {Object} scope (optional) The scope of the callback function
3424          * @return {Roo.MessageBox} This message box
3425          */
3426         confirm : function(title, msg, fn, scope){
3427             this.show({
3428                 title : title,
3429                 msg : msg,
3430                 buttons: this.YESNO,
3431                 fn: fn,
3432                 scope : scope,
3433                 modal : true
3434             });
3435             return this;
3436         },
3437
3438         /**
3439          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3440          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3441          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3442          * (could also be the top-right close button) and the text that was entered will be passed as the two
3443          * parameters to the callback.
3444          * @param {String} title The title bar text
3445          * @param {String} msg The message box body text
3446          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3447          * @param {Object} scope (optional) The scope of the callback function
3448          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3449          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3450          * @return {Roo.MessageBox} This message box
3451          */
3452         prompt : function(title, msg, fn, scope, multiline){
3453             this.show({
3454                 title : title,
3455                 msg : msg,
3456                 buttons: this.OKCANCEL,
3457                 fn: fn,
3458                 minWidth:250,
3459                 scope : scope,
3460                 prompt:true,
3461                 multiline: multiline,
3462                 modal : true
3463             });
3464             return this;
3465         },
3466
3467         /**
3468          * Button config that displays a single OK button
3469          * @type Object
3470          */
3471         OK : {ok:true},
3472         /**
3473          * Button config that displays Yes and No buttons
3474          * @type Object
3475          */
3476         YESNO : {yes:true, no:true},
3477         /**
3478          * Button config that displays OK and Cancel buttons
3479          * @type Object
3480          */
3481         OKCANCEL : {ok:true, cancel:true},
3482         /**
3483          * Button config that displays Yes, No and Cancel buttons
3484          * @type Object
3485          */
3486         YESNOCANCEL : {yes:true, no:true, cancel:true},
3487
3488         /**
3489          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3490          * @type Number
3491          */
3492         defaultTextHeight : 75,
3493         /**
3494          * The maximum width in pixels of the message box (defaults to 600)
3495          * @type Number
3496          */
3497         maxWidth : 600,
3498         /**
3499          * The minimum width in pixels of the message box (defaults to 100)
3500          * @type Number
3501          */
3502         minWidth : 100,
3503         /**
3504          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3505          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3506          * @type Number
3507          */
3508         minProgressWidth : 250,
3509         /**
3510          * An object containing the default button text strings that can be overriden for localized language support.
3511          * Supported properties are: ok, cancel, yes and no.
3512          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3513          * @type Object
3514          */
3515         buttonText : {
3516             ok : "OK",
3517             cancel : "Cancel",
3518             yes : "Yes",
3519             no : "No"
3520         }
3521     };
3522 }();
3523
3524 /**
3525  * Shorthand for {@link Roo.MessageBox}
3526  */
3527 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3528 Roo.Msg = Roo.Msg || Roo.MessageBox;
3529 /*
3530  * - LGPL
3531  *
3532  * navbar
3533  * 
3534  */
3535
3536 /**
3537  * @class Roo.bootstrap.Navbar
3538  * @extends Roo.bootstrap.Component
3539  * Bootstrap Navbar class
3540
3541  * @constructor
3542  * Create a new Navbar
3543  * @param {Object} config The config object
3544  */
3545
3546
3547 Roo.bootstrap.Navbar = function(config){
3548     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3549     this.addEvents({
3550         // raw events
3551         /**
3552          * @event beforetoggle
3553          * Fire before toggle the menu
3554          * @param {Roo.EventObject} e
3555          */
3556         "beforetoggle" : true
3557     });
3558 };
3559
3560 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3561     
3562     
3563    
3564     // private
3565     navItems : false,
3566     loadMask : false,
3567     
3568     
3569     getAutoCreate : function(){
3570         
3571         
3572         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3573         
3574     },
3575     
3576     initEvents :function ()
3577     {
3578         //Roo.log(this.el.select('.navbar-toggle',true));
3579         this.el.select('.navbar-toggle',true).on('click', function() {
3580             if(this.fireEvent('beforetoggle', this) !== false){
3581                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3582             }
3583             
3584         }, this);
3585         
3586         var mark = {
3587             tag: "div",
3588             cls:"x-dlg-mask"
3589         };
3590         
3591         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3592         
3593         var size = this.el.getSize();
3594         this.maskEl.setSize(size.width, size.height);
3595         this.maskEl.enableDisplayMode("block");
3596         this.maskEl.hide();
3597         
3598         if(this.loadMask){
3599             this.maskEl.show();
3600         }
3601     },
3602     
3603     
3604     getChildContainer : function()
3605     {
3606         if (this.el.select('.collapse').getCount()) {
3607             return this.el.select('.collapse',true).first();
3608         }
3609         
3610         return this.el;
3611     },
3612     
3613     mask : function()
3614     {
3615         this.maskEl.show();
3616     },
3617     
3618     unmask : function()
3619     {
3620         this.maskEl.hide();
3621     } 
3622     
3623     
3624     
3625     
3626 });
3627
3628
3629
3630  
3631
3632  /*
3633  * - LGPL
3634  *
3635  * navbar
3636  * 
3637  */
3638
3639 /**
3640  * @class Roo.bootstrap.NavSimplebar
3641  * @extends Roo.bootstrap.Navbar
3642  * Bootstrap Sidebar class
3643  *
3644  * @cfg {Boolean} inverse is inverted color
3645  * 
3646  * @cfg {String} type (nav | pills | tabs)
3647  * @cfg {Boolean} arrangement stacked | justified
3648  * @cfg {String} align (left | right) alignment
3649  * 
3650  * @cfg {Boolean} main (true|false) main nav bar? default false
3651  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3652  * 
3653  * @cfg {String} tag (header|footer|nav|div) default is nav 
3654
3655  * 
3656  * 
3657  * 
3658  * @constructor
3659  * Create a new Sidebar
3660  * @param {Object} config The config object
3661  */
3662
3663
3664 Roo.bootstrap.NavSimplebar = function(config){
3665     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3669     
3670     inverse: false,
3671     
3672     type: false,
3673     arrangement: '',
3674     align : false,
3675     
3676     
3677     
3678     main : false,
3679     
3680     
3681     tag : false,
3682     
3683     
3684     getAutoCreate : function(){
3685         
3686         
3687         var cfg = {
3688             tag : this.tag || 'div',
3689             cls : 'navbar'
3690         };
3691           
3692         
3693         cfg.cn = [
3694             {
3695                 cls: 'nav',
3696                 tag : 'ul'
3697             }
3698         ];
3699         
3700          
3701         this.type = this.type || 'nav';
3702         if (['tabs','pills'].indexOf(this.type)!==-1) {
3703             cfg.cn[0].cls += ' nav-' + this.type
3704         
3705         
3706         } else {
3707             if (this.type!=='nav') {
3708                 Roo.log('nav type must be nav/tabs/pills')
3709             }
3710             cfg.cn[0].cls += ' navbar-nav'
3711         }
3712         
3713         
3714         
3715         
3716         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3717             cfg.cn[0].cls += ' nav-' + this.arrangement;
3718         }
3719         
3720         
3721         if (this.align === 'right') {
3722             cfg.cn[0].cls += ' navbar-right';
3723         }
3724         
3725         if (this.inverse) {
3726             cfg.cls += ' navbar-inverse';
3727             
3728         }
3729         
3730         
3731         return cfg;
3732     
3733         
3734     }
3735     
3736     
3737     
3738 });
3739
3740
3741
3742  
3743
3744  
3745        /*
3746  * - LGPL
3747  *
3748  * navbar
3749  * 
3750  */
3751
3752 /**
3753  * @class Roo.bootstrap.NavHeaderbar
3754  * @extends Roo.bootstrap.NavSimplebar
3755  * Bootstrap Sidebar class
3756  *
3757  * @cfg {String} brand what is brand
3758  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3759  * @cfg {String} brand_href href of the brand
3760  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3761  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3762  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3763  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3764  * 
3765  * @constructor
3766  * Create a new Sidebar
3767  * @param {Object} config The config object
3768  */
3769
3770
3771 Roo.bootstrap.NavHeaderbar = function(config){
3772     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3773       
3774 };
3775
3776 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3777     
3778     position: '',
3779     brand: '',
3780     brand_href: false,
3781     srButton : true,
3782     autohide : false,
3783     desktopCenter : false,
3784    
3785     
3786     getAutoCreate : function(){
3787         
3788         var   cfg = {
3789             tag: this.nav || 'nav',
3790             cls: 'navbar',
3791             role: 'navigation',
3792             cn: []
3793         };
3794         
3795         var cn = cfg.cn;
3796         if (this.desktopCenter) {
3797             cn.push({cls : 'container', cn : []});
3798             cn = cn[0].cn;
3799         }
3800         
3801         if(this.srButton){
3802             cn.push({
3803                 tag: 'div',
3804                 cls: 'navbar-header',
3805                 cn: [
3806                     {
3807                         tag: 'button',
3808                         type: 'button',
3809                         cls: 'navbar-toggle',
3810                         'data-toggle': 'collapse',
3811                         cn: [
3812                             {
3813                                 tag: 'span',
3814                                 cls: 'sr-only',
3815                                 html: 'Toggle navigation'
3816                             },
3817                             {
3818                                 tag: 'span',
3819                                 cls: 'icon-bar'
3820                             },
3821                             {
3822                                 tag: 'span',
3823                                 cls: 'icon-bar'
3824                             },
3825                             {
3826                                 tag: 'span',
3827                                 cls: 'icon-bar'
3828                             }
3829                         ]
3830                     }
3831                 ]
3832             });
3833         }
3834         
3835         cn.push({
3836             tag: 'div',
3837             cls: 'collapse navbar-collapse',
3838             cn : []
3839         });
3840         
3841         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3842         
3843         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3844             cfg.cls += ' navbar-' + this.position;
3845             
3846             // tag can override this..
3847             
3848             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3849         }
3850         
3851         if (this.brand !== '') {
3852             cn[0].cn.push({
3853                 tag: 'a',
3854                 href: this.brand_href ? this.brand_href : '#',
3855                 cls: 'navbar-brand',
3856                 cn: [
3857                 this.brand
3858                 ]
3859             });
3860         }
3861         
3862         if(this.main){
3863             cfg.cls += ' main-nav';
3864         }
3865         
3866         
3867         return cfg;
3868
3869         
3870     },
3871     getHeaderChildContainer : function()
3872     {
3873         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3874             return this.el.select('.navbar-header',true).first();
3875         }
3876         
3877         return this.getChildContainer();
3878     },
3879     
3880     
3881     initEvents : function()
3882     {
3883         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3884         
3885         if (this.autohide) {
3886             
3887             var prevScroll = 0;
3888             var ft = this.el;
3889             
3890             Roo.get(document).on('scroll',function(e) {
3891                 var ns = Roo.get(document).getScroll().top;
3892                 var os = prevScroll;
3893                 prevScroll = ns;
3894                 
3895                 if(ns > os){
3896                     ft.removeClass('slideDown');
3897                     ft.addClass('slideUp');
3898                     return;
3899                 }
3900                 ft.removeClass('slideUp');
3901                 ft.addClass('slideDown');
3902                  
3903               
3904           },this);
3905         }
3906     }    
3907     
3908 });
3909
3910
3911
3912  
3913
3914  /*
3915  * - LGPL
3916  *
3917  * navbar
3918  * 
3919  */
3920
3921 /**
3922  * @class Roo.bootstrap.NavSidebar
3923  * @extends Roo.bootstrap.Navbar
3924  * Bootstrap Sidebar class
3925  * 
3926  * @constructor
3927  * Create a new Sidebar
3928  * @param {Object} config The config object
3929  */
3930
3931
3932 Roo.bootstrap.NavSidebar = function(config){
3933     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3934 };
3935
3936 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3937     
3938     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3939     
3940     getAutoCreate : function(){
3941         
3942         
3943         return  {
3944             tag: 'div',
3945             cls: 'sidebar sidebar-nav'
3946         };
3947     
3948         
3949     }
3950     
3951     
3952     
3953 });
3954
3955
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * nav group
3963  * 
3964  */
3965
3966 /**
3967  * @class Roo.bootstrap.NavGroup
3968  * @extends Roo.bootstrap.Component
3969  * Bootstrap NavGroup class
3970  * @cfg {String} align (left|right)
3971  * @cfg {Boolean} inverse
3972  * @cfg {String} type (nav|pills|tab) default nav
3973  * @cfg {String} navId - reference Id for navbar.
3974
3975  * 
3976  * @constructor
3977  * Create a new nav group
3978  * @param {Object} config The config object
3979  */
3980
3981 Roo.bootstrap.NavGroup = function(config){
3982     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3983     this.navItems = [];
3984    
3985     Roo.bootstrap.NavGroup.register(this);
3986      this.addEvents({
3987         /**
3988              * @event changed
3989              * Fires when the active item changes
3990              * @param {Roo.bootstrap.NavGroup} this
3991              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3992              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3993          */
3994         'changed': true
3995      });
3996     
3997 };
3998
3999 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4000     
4001     align: '',
4002     inverse: false,
4003     form: false,
4004     type: 'nav',
4005     navId : '',
4006     // private
4007     
4008     navItems : false, 
4009     
4010     getAutoCreate : function()
4011     {
4012         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4013         
4014         cfg = {
4015             tag : 'ul',
4016             cls: 'nav' 
4017         };
4018         
4019         if (['tabs','pills'].indexOf(this.type)!==-1) {
4020             cfg.cls += ' nav-' + this.type
4021         } else {
4022             if (this.type!=='nav') {
4023                 Roo.log('nav type must be nav/tabs/pills')
4024             }
4025             cfg.cls += ' navbar-nav'
4026         }
4027         
4028         if (this.parent().sidebar) {
4029             cfg = {
4030                 tag: 'ul',
4031                 cls: 'dashboard-menu sidebar-menu'
4032             };
4033             
4034             return cfg;
4035         }
4036         
4037         if (this.form === true) {
4038             cfg = {
4039                 tag: 'form',
4040                 cls: 'navbar-form'
4041             };
4042             
4043             if (this.align === 'right') {
4044                 cfg.cls += ' navbar-right';
4045             } else {
4046                 cfg.cls += ' navbar-left';
4047             }
4048         }
4049         
4050         if (this.align === 'right') {
4051             cfg.cls += ' navbar-right';
4052         }
4053         
4054         if (this.inverse) {
4055             cfg.cls += ' navbar-inverse';
4056             
4057         }
4058         
4059         
4060         return cfg;
4061     },
4062     /**
4063     * sets the active Navigation item
4064     * @param {Roo.bootstrap.NavItem} the new current navitem
4065     */
4066     setActiveItem : function(item)
4067     {
4068         var prev = false;
4069         Roo.each(this.navItems, function(v){
4070             if (v == item) {
4071                 return ;
4072             }
4073             if (v.isActive()) {
4074                 v.setActive(false, true);
4075                 prev = v;
4076                 
4077             }
4078             
4079         });
4080
4081         item.setActive(true, true);
4082         this.fireEvent('changed', this, item, prev);
4083         
4084         
4085     },
4086     /**
4087     * gets the active Navigation item
4088     * @return {Roo.bootstrap.NavItem} the current navitem
4089     */
4090     getActive : function()
4091     {
4092         
4093         var prev = false;
4094         Roo.each(this.navItems, function(v){
4095             
4096             if (v.isActive()) {
4097                 prev = v;
4098                 
4099             }
4100             
4101         });
4102         return prev;
4103     },
4104     
4105     indexOfNav : function()
4106     {
4107         
4108         var prev = false;
4109         Roo.each(this.navItems, function(v,i){
4110             
4111             if (v.isActive()) {
4112                 prev = i;
4113                 
4114             }
4115             
4116         });
4117         return prev;
4118     },
4119     /**
4120     * adds a Navigation item
4121     * @param {Roo.bootstrap.NavItem} the navitem to add
4122     */
4123     addItem : function(cfg)
4124     {
4125         var cn = new Roo.bootstrap.NavItem(cfg);
4126         this.register(cn);
4127         cn.parentId = this.id;
4128         cn.onRender(this.el, null);
4129         return cn;
4130     },
4131     /**
4132     * register a Navigation item
4133     * @param {Roo.bootstrap.NavItem} the navitem to add
4134     */
4135     register : function(item)
4136     {
4137         this.navItems.push( item);
4138         item.navId = this.navId;
4139     
4140     },
4141     
4142     /**
4143     * clear all the Navigation item
4144     */
4145    
4146     clearAll : function()
4147     {
4148         this.navItems = [];
4149         this.el.dom.innerHTML = '';
4150     },
4151     
4152     getNavItem: function(tabId)
4153     {
4154         var ret = false;
4155         Roo.each(this.navItems, function(e) {
4156             if (e.tabId == tabId) {
4157                ret =  e;
4158                return false;
4159             }
4160             return true;
4161             
4162         });
4163         return ret;
4164     },
4165     
4166     setActiveNext : function()
4167     {
4168         var i = this.indexOfNav(this.getActive());
4169         if (i > this.navItems.length) {
4170             return;
4171         }
4172         this.setActiveItem(this.navItems[i+1]);
4173     },
4174     setActivePrev : function()
4175     {
4176         var i = this.indexOfNav(this.getActive());
4177         if (i  < 1) {
4178             return;
4179         }
4180         this.setActiveItem(this.navItems[i-1]);
4181     },
4182     clearWasActive : function(except) {
4183         Roo.each(this.navItems, function(e) {
4184             if (e.tabId != except.tabId && e.was_active) {
4185                e.was_active = false;
4186                return false;
4187             }
4188             return true;
4189             
4190         });
4191     },
4192     getWasActive : function ()
4193     {
4194         var r = false;
4195         Roo.each(this.navItems, function(e) {
4196             if (e.was_active) {
4197                r = e;
4198                return false;
4199             }
4200             return true;
4201             
4202         });
4203         return r;
4204     }
4205     
4206     
4207 });
4208
4209  
4210 Roo.apply(Roo.bootstrap.NavGroup, {
4211     
4212     groups: {},
4213      /**
4214     * register a Navigation Group
4215     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4216     */
4217     register : function(navgrp)
4218     {
4219         this.groups[navgrp.navId] = navgrp;
4220         
4221     },
4222     /**
4223     * fetch a Navigation Group based on the navigation ID
4224     * @param {string} the navgroup to add
4225     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4226     */
4227     get: function(navId) {
4228         if (typeof(this.groups[navId]) == 'undefined') {
4229             return false;
4230             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4231         }
4232         return this.groups[navId] ;
4233     }
4234     
4235     
4236     
4237 });
4238
4239  /*
4240  * - LGPL
4241  *
4242  * row
4243  * 
4244  */
4245
4246 /**
4247  * @class Roo.bootstrap.NavItem
4248  * @extends Roo.bootstrap.Component
4249  * Bootstrap Navbar.NavItem class
4250  * @cfg {String} href  link to
4251  * @cfg {String} html content of button
4252  * @cfg {String} badge text inside badge
4253  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4254  * @cfg {String} glyphicon name of glyphicon
4255  * @cfg {String} icon name of font awesome icon
4256  * @cfg {Boolean} active Is item active
4257  * @cfg {Boolean} disabled Is item disabled
4258  
4259  * @cfg {Boolean} preventDefault (true | false) default false
4260  * @cfg {String} tabId the tab that this item activates.
4261  * @cfg {String} tagtype (a|span) render as a href or span?
4262  * @cfg {Boolean} animateRef (true|false) link to element default false  
4263   
4264  * @constructor
4265  * Create a new Navbar Item
4266  * @param {Object} config The config object
4267  */
4268 Roo.bootstrap.NavItem = function(config){
4269     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4270     this.addEvents({
4271         // raw events
4272         /**
4273          * @event click
4274          * The raw click event for the entire grid.
4275          * @param {Roo.EventObject} e
4276          */
4277         "click" : true,
4278          /**
4279             * @event changed
4280             * Fires when the active item active state changes
4281             * @param {Roo.bootstrap.NavItem} this
4282             * @param {boolean} state the new state
4283              
4284          */
4285         'changed': true,
4286         /**
4287             * @event scrollto
4288             * Fires when scroll to element
4289             * @param {Roo.bootstrap.NavItem} this
4290             * @param {Object} options
4291             * @param {Roo.EventObject} e
4292              
4293          */
4294         'scrollto': true
4295     });
4296    
4297 };
4298
4299 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4300     
4301     href: false,
4302     html: '',
4303     badge: '',
4304     icon: false,
4305     glyphicon: false,
4306     active: false,
4307     preventDefault : false,
4308     tabId : false,
4309     tagtype : 'a',
4310     disabled : false,
4311     animateRef : false,
4312     was_active : false,
4313     
4314     getAutoCreate : function(){
4315          
4316         var cfg = {
4317             tag: 'li',
4318             cls: 'nav-item'
4319             
4320         };
4321         
4322         if (this.active) {
4323             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4324         }
4325         if (this.disabled) {
4326             cfg.cls += ' disabled';
4327         }
4328         
4329         if (this.href || this.html || this.glyphicon || this.icon) {
4330             cfg.cn = [
4331                 {
4332                     tag: this.tagtype,
4333                     href : this.href || "#",
4334                     html: this.html || ''
4335                 }
4336             ];
4337             
4338             if (this.icon) {
4339                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4340             }
4341
4342             if(this.glyphicon) {
4343                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4344             }
4345             
4346             if (this.menu) {
4347                 
4348                 cfg.cn[0].html += " <span class='caret'></span>";
4349              
4350             }
4351             
4352             if (this.badge !== '') {
4353                  
4354                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4355             }
4356         }
4357         
4358         
4359         
4360         return cfg;
4361     },
4362     initEvents: function() 
4363     {
4364         if (typeof (this.menu) != 'undefined') {
4365             this.menu.parentType = this.xtype;
4366             this.menu.triggerEl = this.el;
4367             this.menu = this.addxtype(Roo.apply({}, this.menu));
4368         }
4369         
4370         this.el.select('a',true).on('click', this.onClick, this);
4371         
4372         if(this.tagtype == 'span'){
4373             this.el.select('span',true).on('click', this.onClick, this);
4374         }
4375        
4376         // at this point parent should be available..
4377         this.parent().register(this);
4378     },
4379     
4380     onClick : function(e)
4381     {
4382         if (e.getTarget('.dropdown-menu-item')) {
4383             // did you click on a menu itemm.... - then don't trigger onclick..
4384             return;
4385         }
4386         
4387         if(
4388                 this.preventDefault || 
4389                 this.href == '#' 
4390         ){
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393         }
4394         
4395         if (this.disabled) {
4396             return;
4397         }
4398         
4399         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4400         if (tg && tg.transition) {
4401             Roo.log("waiting for the transitionend");
4402             return;
4403         }
4404         
4405         
4406         
4407         //Roo.log("fire event clicked");
4408         if(this.fireEvent('click', this, e) === false){
4409             return;
4410         };
4411         
4412         if(this.tagtype == 'span'){
4413             return;
4414         }
4415         
4416         //Roo.log(this.href);
4417         var ael = this.el.select('a',true).first();
4418         //Roo.log(ael);
4419         
4420         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4421             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4422             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4423                 return; // ignore... - it's a 'hash' to another page.
4424             }
4425             Roo.log("NavItem - prevent Default?");
4426             e.preventDefault();
4427             this.scrollToElement(e);
4428         }
4429         
4430         
4431         var p =  this.parent();
4432    
4433         if (['tabs','pills'].indexOf(p.type)!==-1) {
4434             if (typeof(p.setActiveItem) !== 'undefined') {
4435                 p.setActiveItem(this);
4436             }
4437         }
4438         
4439         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4440         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4441             // remove the collapsed menu expand...
4442             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4443         }
4444     },
4445     
4446     isActive: function () {
4447         return this.active
4448     },
4449     setActive : function(state, fire, is_was_active)
4450     {
4451         if (this.active && !state && this.navId) {
4452             this.was_active = true;
4453             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454             if (nv) {
4455                 nv.clearWasActive(this);
4456             }
4457             
4458         }
4459         this.active = state;
4460         
4461         if (!state ) {
4462             this.el.removeClass('active');
4463         } else if (!this.el.hasClass('active')) {
4464             this.el.addClass('active');
4465         }
4466         if (fire) {
4467             this.fireEvent('changed', this, state);
4468         }
4469         
4470         // show a panel if it's registered and related..
4471         
4472         if (!this.navId || !this.tabId || !state || is_was_active) {
4473             return;
4474         }
4475         
4476         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4477         if (!tg) {
4478             return;
4479         }
4480         var pan = tg.getPanelByName(this.tabId);
4481         if (!pan) {
4482             return;
4483         }
4484         // if we can not flip to new panel - go back to old nav highlight..
4485         if (false == tg.showPanel(pan)) {
4486             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4487             if (nv) {
4488                 var onav = nv.getWasActive();
4489                 if (onav) {
4490                     onav.setActive(true, false, true);
4491                 }
4492             }
4493             
4494         }
4495         
4496         
4497         
4498     },
4499      // this should not be here...
4500     setDisabled : function(state)
4501     {
4502         this.disabled = state;
4503         if (!state ) {
4504             this.el.removeClass('disabled');
4505         } else if (!this.el.hasClass('disabled')) {
4506             this.el.addClass('disabled');
4507         }
4508         
4509     },
4510     
4511     /**
4512      * Fetch the element to display the tooltip on.
4513      * @return {Roo.Element} defaults to this.el
4514      */
4515     tooltipEl : function()
4516     {
4517         return this.el.select('' + this.tagtype + '', true).first();
4518     },
4519     
4520     scrollToElement : function(e)
4521     {
4522         var c = document.body;
4523         
4524         /*
4525          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4526          */
4527         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4528             c = document.documentElement;
4529         }
4530         
4531         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4532         
4533         if(!target){
4534             return;
4535         }
4536
4537         var o = target.calcOffsetsTo(c);
4538         
4539         var options = {
4540             target : target,
4541             value : o[1]
4542         };
4543         
4544         this.fireEvent('scrollto', this, options, e);
4545         
4546         Roo.get(c).scrollTo('top', options.value, true);
4547         
4548         return;
4549     }
4550 });
4551  
4552
4553  /*
4554  * - LGPL
4555  *
4556  * sidebar item
4557  *
4558  *  li
4559  *    <span> icon </span>
4560  *    <span> text </span>
4561  *    <span>badge </span>
4562  */
4563
4564 /**
4565  * @class Roo.bootstrap.NavSidebarItem
4566  * @extends Roo.bootstrap.NavItem
4567  * Bootstrap Navbar.NavSidebarItem class
4568  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4569  * {bool} open is the menu open
4570  * @constructor
4571  * Create a new Navbar Button
4572  * @param {Object} config The config object
4573  */
4574 Roo.bootstrap.NavSidebarItem = function(config){
4575     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4576     this.addEvents({
4577         // raw events
4578         /**
4579          * @event click
4580          * The raw click event for the entire grid.
4581          * @param {Roo.EventObject} e
4582          */
4583         "click" : true,
4584          /**
4585             * @event changed
4586             * Fires when the active item active state changes
4587             * @param {Roo.bootstrap.NavSidebarItem} this
4588             * @param {boolean} state the new state
4589              
4590          */
4591         'changed': true
4592     });
4593    
4594 };
4595
4596 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4597     
4598     badgeWeight : 'default',
4599     
4600     open: false,
4601     
4602     getAutoCreate : function(){
4603         
4604         
4605         var a = {
4606                 tag: 'a',
4607                 href : this.href || '#',
4608                 cls: '',
4609                 html : '',
4610                 cn : []
4611         };
4612         var cfg = {
4613             tag: 'li',
4614             cls: '',
4615             cn: [ a ]
4616         };
4617         var span = {
4618             tag: 'span',
4619             html : this.html || ''
4620         };
4621         
4622         
4623         if (this.active) {
4624             cfg.cls += ' active';
4625         }
4626         
4627         if (this.disabled) {
4628             cfg.cls += ' disabled';
4629         }
4630         if (this.open) {
4631             cfg.cls += ' open x-open';
4632         }
4633         // left icon..
4634         if (this.glyphicon || this.icon) {
4635             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4636             a.cn.push({ tag : 'i', cls : c }) ;
4637         }
4638         // html..
4639         a.cn.push(span);
4640         // then badge..
4641         if (this.badge !== '') {
4642             
4643             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4644         }
4645         // fi
4646         if (this.menu) {
4647             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4648             a.cls += 'dropdown-toggle treeview' ;
4649         }
4650         
4651         return cfg;
4652          
4653            
4654     },
4655     
4656     initEvents : function()
4657     { 
4658         if (typeof (this.menu) != 'undefined') {
4659             this.menu.parentType = this.xtype;
4660             this.menu.triggerEl = this.el;
4661             this.menu = this.addxtype(Roo.apply({}, this.menu));
4662         }
4663         
4664         this.el.on('click', this.onClick, this);
4665        
4666     
4667         if(this.badge !== ''){
4668  
4669             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4670         }
4671         
4672     },
4673     
4674     onClick : function(e)
4675     {
4676         if(this.disabled){
4677             e.preventDefault();
4678             return;
4679         }
4680         
4681         if(this.preventDefault){
4682             e.preventDefault();
4683         }
4684         
4685         this.fireEvent('click', this);
4686     },
4687     
4688     disable : function()
4689     {
4690         this.setDisabled(true);
4691     },
4692     
4693     enable : function()
4694     {
4695         this.setDisabled(false);
4696     },
4697     
4698     setDisabled : function(state)
4699     {
4700         if(this.disabled == state){
4701             return;
4702         }
4703         
4704         this.disabled = state;
4705         
4706         if (state) {
4707             this.el.addClass('disabled');
4708             return;
4709         }
4710         
4711         this.el.removeClass('disabled');
4712         
4713         return;
4714     },
4715     
4716     setActive : function(state)
4717     {
4718         if(this.active == state){
4719             return;
4720         }
4721         
4722         this.active = state;
4723         
4724         if (state) {
4725             this.el.addClass('active');
4726             return;
4727         }
4728         
4729         this.el.removeClass('active');
4730         
4731         return;
4732     },
4733     
4734     isActive: function () 
4735     {
4736         return this.active;
4737     },
4738     
4739     setBadge : function(str)
4740     {
4741         if(!this.badgeEl){
4742             return;
4743         }
4744         
4745         this.badgeEl.dom.innerHTML = str;
4746     }
4747     
4748    
4749      
4750  
4751 });
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * row
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Row
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Row class (contains columns...)
4765  * 
4766  * @constructor
4767  * Create a new Row
4768  * @param {Object} config The config object
4769  */
4770
4771 Roo.bootstrap.Row = function(config){
4772     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4773 };
4774
4775 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4776     
4777     getAutoCreate : function(){
4778        return {
4779             cls: 'row clearfix'
4780        };
4781     }
4782     
4783     
4784 });
4785
4786  
4787
4788  /*
4789  * - LGPL
4790  *
4791  * element
4792  * 
4793  */
4794
4795 /**
4796  * @class Roo.bootstrap.Element
4797  * @extends Roo.bootstrap.Component
4798  * Bootstrap Element class
4799  * @cfg {String} html contents of the element
4800  * @cfg {String} tag tag of the element
4801  * @cfg {String} cls class of the element
4802  * @cfg {Boolean} preventDefault (true|false) default false
4803  * @cfg {Boolean} clickable (true|false) default false
4804  * 
4805  * @constructor
4806  * Create a new Element
4807  * @param {Object} config The config object
4808  */
4809
4810 Roo.bootstrap.Element = function(config){
4811     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4812     
4813     this.addEvents({
4814         // raw events
4815         /**
4816          * @event click
4817          * When a element is chick
4818          * @param {Roo.bootstrap.Element} this
4819          * @param {Roo.EventObject} e
4820          */
4821         "click" : true
4822     });
4823 };
4824
4825 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4826     
4827     tag: 'div',
4828     cls: '',
4829     html: '',
4830     preventDefault: false, 
4831     clickable: false,
4832     
4833     getAutoCreate : function(){
4834         
4835         var cfg = {
4836             tag: this.tag,
4837             cls: this.cls,
4838             html: this.html
4839         };
4840         
4841         return cfg;
4842     },
4843     
4844     initEvents: function() 
4845     {
4846         Roo.bootstrap.Element.superclass.initEvents.call(this);
4847         
4848         if(this.clickable){
4849             this.el.on('click', this.onClick, this);
4850         }
4851         
4852     },
4853     
4854     onClick : function(e)
4855     {
4856         if(this.preventDefault){
4857             e.preventDefault();
4858         }
4859         
4860         this.fireEvent('click', this, e);
4861     },
4862     
4863     getValue : function()
4864     {
4865         return this.el.dom.innerHTML;
4866     },
4867     
4868     setValue : function(value)
4869     {
4870         this.el.dom.innerHTML = value;
4871     }
4872    
4873 });
4874
4875  
4876
4877  /*
4878  * - LGPL
4879  *
4880  * pagination
4881  * 
4882  */
4883
4884 /**
4885  * @class Roo.bootstrap.Pagination
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap Pagination class
4888  * @cfg {String} size xs | sm | md | lg
4889  * @cfg {Boolean} inverse false | true
4890  * 
4891  * @constructor
4892  * Create a new Pagination
4893  * @param {Object} config The config object
4894  */
4895
4896 Roo.bootstrap.Pagination = function(config){
4897     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4898 };
4899
4900 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4901     
4902     cls: false,
4903     size: false,
4904     inverse: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg = {
4908             tag: 'ul',
4909                 cls: 'pagination'
4910         };
4911         if (this.inverse) {
4912             cfg.cls += ' inverse';
4913         }
4914         if (this.html) {
4915             cfg.html=this.html;
4916         }
4917         if (this.cls) {
4918             cfg.cls += " " + this.cls;
4919         }
4920         return cfg;
4921     }
4922    
4923 });
4924
4925  
4926
4927  /*
4928  * - LGPL
4929  *
4930  * Pagination item
4931  * 
4932  */
4933
4934
4935 /**
4936  * @class Roo.bootstrap.PaginationItem
4937  * @extends Roo.bootstrap.Component
4938  * Bootstrap PaginationItem class
4939  * @cfg {String} html text
4940  * @cfg {String} href the link
4941  * @cfg {Boolean} preventDefault (true | false) default true
4942  * @cfg {Boolean} active (true | false) default false
4943  * @cfg {Boolean} disabled default false
4944  * 
4945  * 
4946  * @constructor
4947  * Create a new PaginationItem
4948  * @param {Object} config The config object
4949  */
4950
4951
4952 Roo.bootstrap.PaginationItem = function(config){
4953     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4954     this.addEvents({
4955         // raw events
4956         /**
4957          * @event click
4958          * The raw click event for the entire grid.
4959          * @param {Roo.EventObject} e
4960          */
4961         "click" : true
4962     });
4963 };
4964
4965 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4966     
4967     href : false,
4968     html : false,
4969     preventDefault: true,
4970     active : false,
4971     cls : false,
4972     disabled: false,
4973     
4974     getAutoCreate : function(){
4975         var cfg= {
4976             tag: 'li',
4977             cn: [
4978                 {
4979                     tag : 'a',
4980                     href : this.href ? this.href : '#',
4981                     html : this.html ? this.html : ''
4982                 }
4983             ]
4984         };
4985         
4986         if(this.cls){
4987             cfg.cls = this.cls;
4988         }
4989         
4990         if(this.disabled){
4991             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4992         }
4993         
4994         if(this.active){
4995             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4996         }
4997         
4998         return cfg;
4999     },
5000     
5001     initEvents: function() {
5002         
5003         this.el.on('click', this.onClick, this);
5004         
5005     },
5006     onClick : function(e)
5007     {
5008         Roo.log('PaginationItem on click ');
5009         if(this.preventDefault){
5010             e.preventDefault();
5011         }
5012         
5013         if(this.disabled){
5014             return;
5015         }
5016         
5017         this.fireEvent('click', this, e);
5018     }
5019    
5020 });
5021
5022  
5023
5024  /*
5025  * - LGPL
5026  *
5027  * slider
5028  * 
5029  */
5030
5031
5032 /**
5033  * @class Roo.bootstrap.Slider
5034  * @extends Roo.bootstrap.Component
5035  * Bootstrap Slider class
5036  *    
5037  * @constructor
5038  * Create a new Slider
5039  * @param {Object} config The config object
5040  */
5041
5042 Roo.bootstrap.Slider = function(config){
5043     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5044 };
5045
5046 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5047     
5048     getAutoCreate : function(){
5049         
5050         var cfg = {
5051             tag: 'div',
5052             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5053             cn: [
5054                 {
5055                     tag: 'a',
5056                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5057                 }
5058             ]
5059         };
5060         
5061         return cfg;
5062     }
5063    
5064 });
5065
5066  /*
5067  * Based on:
5068  * Ext JS Library 1.1.1
5069  * Copyright(c) 2006-2007, Ext JS, LLC.
5070  *
5071  * Originally Released Under LGPL - original licence link has changed is not relivant.
5072  *
5073  * Fork - LGPL
5074  * <script type="text/javascript">
5075  */
5076  
5077
5078 /**
5079  * @class Roo.grid.ColumnModel
5080  * @extends Roo.util.Observable
5081  * This is the default implementation of a ColumnModel used by the Grid. It defines
5082  * the columns in the grid.
5083  * <br>Usage:<br>
5084  <pre><code>
5085  var colModel = new Roo.grid.ColumnModel([
5086         {header: "Ticker", width: 60, sortable: true, locked: true},
5087         {header: "Company Name", width: 150, sortable: true},
5088         {header: "Market Cap.", width: 100, sortable: true},
5089         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5090         {header: "Employees", width: 100, sortable: true, resizable: false}
5091  ]);
5092  </code></pre>
5093  * <p>
5094  
5095  * The config options listed for this class are options which may appear in each
5096  * individual column definition.
5097  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5098  * @constructor
5099  * @param {Object} config An Array of column config objects. See this class's
5100  * config objects for details.
5101 */
5102 Roo.grid.ColumnModel = function(config){
5103         /**
5104      * The config passed into the constructor
5105      */
5106     this.config = config;
5107     this.lookup = {};
5108
5109     // if no id, create one
5110     // if the column does not have a dataIndex mapping,
5111     // map it to the order it is in the config
5112     for(var i = 0, len = config.length; i < len; i++){
5113         var c = config[i];
5114         if(typeof c.dataIndex == "undefined"){
5115             c.dataIndex = i;
5116         }
5117         if(typeof c.renderer == "string"){
5118             c.renderer = Roo.util.Format[c.renderer];
5119         }
5120         if(typeof c.id == "undefined"){
5121             c.id = Roo.id();
5122         }
5123         if(c.editor && c.editor.xtype){
5124             c.editor  = Roo.factory(c.editor, Roo.grid);
5125         }
5126         if(c.editor && c.editor.isFormField){
5127             c.editor = new Roo.grid.GridEditor(c.editor);
5128         }
5129         this.lookup[c.id] = c;
5130     }
5131
5132     /**
5133      * The width of columns which have no width specified (defaults to 100)
5134      * @type Number
5135      */
5136     this.defaultWidth = 100;
5137
5138     /**
5139      * Default sortable of columns which have no sortable specified (defaults to false)
5140      * @type Boolean
5141      */
5142     this.defaultSortable = false;
5143
5144     this.addEvents({
5145         /**
5146              * @event widthchange
5147              * Fires when the width of a column changes.
5148              * @param {ColumnModel} this
5149              * @param {Number} columnIndex The column index
5150              * @param {Number} newWidth The new width
5151              */
5152             "widthchange": true,
5153         /**
5154              * @event headerchange
5155              * Fires when the text of a header changes.
5156              * @param {ColumnModel} this
5157              * @param {Number} columnIndex The column index
5158              * @param {Number} newText The new header text
5159              */
5160             "headerchange": true,
5161         /**
5162              * @event hiddenchange
5163              * Fires when a column is hidden or "unhidden".
5164              * @param {ColumnModel} this
5165              * @param {Number} columnIndex The column index
5166              * @param {Boolean} hidden true if hidden, false otherwise
5167              */
5168             "hiddenchange": true,
5169             /**
5170          * @event columnmoved
5171          * Fires when a column is moved.
5172          * @param {ColumnModel} this
5173          * @param {Number} oldIndex
5174          * @param {Number} newIndex
5175          */
5176         "columnmoved" : true,
5177         /**
5178          * @event columlockchange
5179          * Fires when a column's locked state is changed
5180          * @param {ColumnModel} this
5181          * @param {Number} colIndex
5182          * @param {Boolean} locked true if locked
5183          */
5184         "columnlockchange" : true
5185     });
5186     Roo.grid.ColumnModel.superclass.constructor.call(this);
5187 };
5188 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5189     /**
5190      * @cfg {String} header The header text to display in the Grid view.
5191      */
5192     /**
5193      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5194      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5195      * specified, the column's index is used as an index into the Record's data Array.
5196      */
5197     /**
5198      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5199      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5200      */
5201     /**
5202      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5203      * Defaults to the value of the {@link #defaultSortable} property.
5204      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5205      */
5206     /**
5207      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5208      */
5209     /**
5210      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5211      */
5212     /**
5213      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5214      */
5215     /**
5216      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5217      */
5218     /**
5219      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5220      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5221      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5222      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5223      */
5224        /**
5225      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5226      */
5227     /**
5228      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5229      */
5230     /**
5231      * @cfg {String} cursor (Optional)
5232      */
5233     /**
5234      * @cfg {String} tooltip (Optional)
5235      */
5236     /**
5237      * @cfg {Number} xs (Optional)
5238      */
5239     /**
5240      * @cfg {Number} sm (Optional)
5241      */
5242     /**
5243      * @cfg {Number} md (Optional)
5244      */
5245     /**
5246      * @cfg {Number} lg (Optional)
5247      */
5248     /**
5249      * Returns the id of the column at the specified index.
5250      * @param {Number} index The column index
5251      * @return {String} the id
5252      */
5253     getColumnId : function(index){
5254         return this.config[index].id;
5255     },
5256
5257     /**
5258      * Returns the column for a specified id.
5259      * @param {String} id The column id
5260      * @return {Object} the column
5261      */
5262     getColumnById : function(id){
5263         return this.lookup[id];
5264     },
5265
5266     
5267     /**
5268      * Returns the column for a specified dataIndex.
5269      * @param {String} dataIndex The column dataIndex
5270      * @return {Object|Boolean} the column or false if not found
5271      */
5272     getColumnByDataIndex: function(dataIndex){
5273         var index = this.findColumnIndex(dataIndex);
5274         return index > -1 ? this.config[index] : false;
5275     },
5276     
5277     /**
5278      * Returns the index for a specified column id.
5279      * @param {String} id The column id
5280      * @return {Number} the index, or -1 if not found
5281      */
5282     getIndexById : function(id){
5283         for(var i = 0, len = this.config.length; i < len; i++){
5284             if(this.config[i].id == id){
5285                 return i;
5286             }
5287         }
5288         return -1;
5289     },
5290     
5291     /**
5292      * Returns the index for a specified column dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Number} the index, or -1 if not found
5295      */
5296     
5297     findColumnIndex : function(dataIndex){
5298         for(var i = 0, len = this.config.length; i < len; i++){
5299             if(this.config[i].dataIndex == dataIndex){
5300                 return i;
5301             }
5302         }
5303         return -1;
5304     },
5305     
5306     
5307     moveColumn : function(oldIndex, newIndex){
5308         var c = this.config[oldIndex];
5309         this.config.splice(oldIndex, 1);
5310         this.config.splice(newIndex, 0, c);
5311         this.dataMap = null;
5312         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5313     },
5314
5315     isLocked : function(colIndex){
5316         return this.config[colIndex].locked === true;
5317     },
5318
5319     setLocked : function(colIndex, value, suppressEvent){
5320         if(this.isLocked(colIndex) == value){
5321             return;
5322         }
5323         this.config[colIndex].locked = value;
5324         if(!suppressEvent){
5325             this.fireEvent("columnlockchange", this, colIndex, value);
5326         }
5327     },
5328
5329     getTotalLockedWidth : function(){
5330         var totalWidth = 0;
5331         for(var i = 0; i < this.config.length; i++){
5332             if(this.isLocked(i) && !this.isHidden(i)){
5333                 this.totalWidth += this.getColumnWidth(i);
5334             }
5335         }
5336         return totalWidth;
5337     },
5338
5339     getLockedCount : function(){
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             if(!this.isLocked(i)){
5342                 return i;
5343             }
5344         }
5345         
5346         return this.config.length;
5347     },
5348
5349     /**
5350      * Returns the number of columns.
5351      * @return {Number}
5352      */
5353     getColumnCount : function(visibleOnly){
5354         if(visibleOnly === true){
5355             var c = 0;
5356             for(var i = 0, len = this.config.length; i < len; i++){
5357                 if(!this.isHidden(i)){
5358                     c++;
5359                 }
5360             }
5361             return c;
5362         }
5363         return this.config.length;
5364     },
5365
5366     /**
5367      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5368      * @param {Function} fn
5369      * @param {Object} scope (optional)
5370      * @return {Array} result
5371      */
5372     getColumnsBy : function(fn, scope){
5373         var r = [];
5374         for(var i = 0, len = this.config.length; i < len; i++){
5375             var c = this.config[i];
5376             if(fn.call(scope||this, c, i) === true){
5377                 r[r.length] = c;
5378             }
5379         }
5380         return r;
5381     },
5382
5383     /**
5384      * Returns true if the specified column is sortable.
5385      * @param {Number} col The column index
5386      * @return {Boolean}
5387      */
5388     isSortable : function(col){
5389         if(typeof this.config[col].sortable == "undefined"){
5390             return this.defaultSortable;
5391         }
5392         return this.config[col].sortable;
5393     },
5394
5395     /**
5396      * Returns the rendering (formatting) function defined for the column.
5397      * @param {Number} col The column index.
5398      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5399      */
5400     getRenderer : function(col){
5401         if(!this.config[col].renderer){
5402             return Roo.grid.ColumnModel.defaultRenderer;
5403         }
5404         return this.config[col].renderer;
5405     },
5406
5407     /**
5408      * Sets the rendering (formatting) function for a column.
5409      * @param {Number} col The column index
5410      * @param {Function} fn The function to use to process the cell's raw data
5411      * to return HTML markup for the grid view. The render function is called with
5412      * the following parameters:<ul>
5413      * <li>Data value.</li>
5414      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5415      * <li>css A CSS style string to apply to the table cell.</li>
5416      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5417      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5418      * <li>Row index</li>
5419      * <li>Column index</li>
5420      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5421      */
5422     setRenderer : function(col, fn){
5423         this.config[col].renderer = fn;
5424     },
5425
5426     /**
5427      * Returns the width for the specified column.
5428      * @param {Number} col The column index
5429      * @return {Number}
5430      */
5431     getColumnWidth : function(col){
5432         return this.config[col].width * 1 || this.defaultWidth;
5433     },
5434
5435     /**
5436      * Sets the width for a column.
5437      * @param {Number} col The column index
5438      * @param {Number} width The new width
5439      */
5440     setColumnWidth : function(col, width, suppressEvent){
5441         this.config[col].width = width;
5442         this.totalWidth = null;
5443         if(!suppressEvent){
5444              this.fireEvent("widthchange", this, col, width);
5445         }
5446     },
5447
5448     /**
5449      * Returns the total width of all columns.
5450      * @param {Boolean} includeHidden True to include hidden column widths
5451      * @return {Number}
5452      */
5453     getTotalWidth : function(includeHidden){
5454         if(!this.totalWidth){
5455             this.totalWidth = 0;
5456             for(var i = 0, len = this.config.length; i < len; i++){
5457                 if(includeHidden || !this.isHidden(i)){
5458                     this.totalWidth += this.getColumnWidth(i);
5459                 }
5460             }
5461         }
5462         return this.totalWidth;
5463     },
5464
5465     /**
5466      * Returns the header for the specified column.
5467      * @param {Number} col The column index
5468      * @return {String}
5469      */
5470     getColumnHeader : function(col){
5471         return this.config[col].header;
5472     },
5473
5474     /**
5475      * Sets the header for a column.
5476      * @param {Number} col The column index
5477      * @param {String} header The new header
5478      */
5479     setColumnHeader : function(col, header){
5480         this.config[col].header = header;
5481         this.fireEvent("headerchange", this, col, header);
5482     },
5483
5484     /**
5485      * Returns the tooltip for the specified column.
5486      * @param {Number} col The column index
5487      * @return {String}
5488      */
5489     getColumnTooltip : function(col){
5490             return this.config[col].tooltip;
5491     },
5492     /**
5493      * Sets the tooltip for a column.
5494      * @param {Number} col The column index
5495      * @param {String} tooltip The new tooltip
5496      */
5497     setColumnTooltip : function(col, tooltip){
5498             this.config[col].tooltip = tooltip;
5499     },
5500
5501     /**
5502      * Returns the dataIndex for the specified column.
5503      * @param {Number} col The column index
5504      * @return {Number}
5505      */
5506     getDataIndex : function(col){
5507         return this.config[col].dataIndex;
5508     },
5509
5510     /**
5511      * Sets the dataIndex for a column.
5512      * @param {Number} col The column index
5513      * @param {Number} dataIndex The new dataIndex
5514      */
5515     setDataIndex : function(col, dataIndex){
5516         this.config[col].dataIndex = dataIndex;
5517     },
5518
5519     
5520     
5521     /**
5522      * Returns true if the cell is editable.
5523      * @param {Number} colIndex The column index
5524      * @param {Number} rowIndex The row index - this is nto actually used..?
5525      * @return {Boolean}
5526      */
5527     isCellEditable : function(colIndex, rowIndex){
5528         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5529     },
5530
5531     /**
5532      * Returns the editor defined for the cell/column.
5533      * return false or null to disable editing.
5534      * @param {Number} colIndex The column index
5535      * @param {Number} rowIndex The row index
5536      * @return {Object}
5537      */
5538     getCellEditor : function(colIndex, rowIndex){
5539         return this.config[colIndex].editor;
5540     },
5541
5542     /**
5543      * Sets if a column is editable.
5544      * @param {Number} col The column index
5545      * @param {Boolean} editable True if the column is editable
5546      */
5547     setEditable : function(col, editable){
5548         this.config[col].editable = editable;
5549     },
5550
5551
5552     /**
5553      * Returns true if the column is hidden.
5554      * @param {Number} colIndex The column index
5555      * @return {Boolean}
5556      */
5557     isHidden : function(colIndex){
5558         return this.config[colIndex].hidden;
5559     },
5560
5561
5562     /**
5563      * Returns true if the column width cannot be changed
5564      */
5565     isFixed : function(colIndex){
5566         return this.config[colIndex].fixed;
5567     },
5568
5569     /**
5570      * Returns true if the column can be resized
5571      * @return {Boolean}
5572      */
5573     isResizable : function(colIndex){
5574         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5575     },
5576     /**
5577      * Sets if a column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @param {Boolean} hidden True if the column is hidden
5580      */
5581     setHidden : function(colIndex, hidden){
5582         this.config[colIndex].hidden = hidden;
5583         this.totalWidth = null;
5584         this.fireEvent("hiddenchange", this, colIndex, hidden);
5585     },
5586
5587     /**
5588      * Sets the editor for a column.
5589      * @param {Number} col The column index
5590      * @param {Object} editor The editor object
5591      */
5592     setEditor : function(col, editor){
5593         this.config[col].editor = editor;
5594     }
5595 });
5596
5597 Roo.grid.ColumnModel.defaultRenderer = function(value)
5598 {
5599     if(typeof value == "object") {
5600         return value;
5601     }
5602         if(typeof value == "string" && value.length < 1){
5603             return "&#160;";
5604         }
5605     
5606         return String.format("{0}", value);
5607 };
5608
5609 // Alias for backwards compatibility
5610 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5611 /*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 /**
5623  * @class Roo.LoadMask
5624  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5625  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5626  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5627  * element's UpdateManager load indicator and will be destroyed after the initial load.
5628  * @constructor
5629  * Create a new LoadMask
5630  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5631  * @param {Object} config The config object
5632  */
5633 Roo.LoadMask = function(el, config){
5634     this.el = Roo.get(el);
5635     Roo.apply(this, config);
5636     if(this.store){
5637         this.store.on('beforeload', this.onBeforeLoad, this);
5638         this.store.on('load', this.onLoad, this);
5639         this.store.on('loadexception', this.onLoadException, this);
5640         this.removeMask = false;
5641     }else{
5642         var um = this.el.getUpdateManager();
5643         um.showLoadIndicator = false; // disable the default indicator
5644         um.on('beforeupdate', this.onBeforeLoad, this);
5645         um.on('update', this.onLoad, this);
5646         um.on('failure', this.onLoad, this);
5647         this.removeMask = true;
5648     }
5649 };
5650
5651 Roo.LoadMask.prototype = {
5652     /**
5653      * @cfg {Boolean} removeMask
5654      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5655      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5656      */
5657     /**
5658      * @cfg {String} msg
5659      * The text to display in a centered loading message box (defaults to 'Loading...')
5660      */
5661     msg : 'Loading...',
5662     /**
5663      * @cfg {String} msgCls
5664      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5665      */
5666     msgCls : 'x-mask-loading',
5667
5668     /**
5669      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5670      * @type Boolean
5671      */
5672     disabled: false,
5673
5674     /**
5675      * Disables the mask to prevent it from being displayed
5676      */
5677     disable : function(){
5678        this.disabled = true;
5679     },
5680
5681     /**
5682      * Enables the mask so that it can be displayed
5683      */
5684     enable : function(){
5685         this.disabled = false;
5686     },
5687     
5688     onLoadException : function()
5689     {
5690         Roo.log(arguments);
5691         
5692         if (typeof(arguments[3]) != 'undefined') {
5693             Roo.MessageBox.alert("Error loading",arguments[3]);
5694         } 
5695         /*
5696         try {
5697             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5698                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5699             }   
5700         } catch(e) {
5701             
5702         }
5703         */
5704     
5705         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5706     },
5707     // private
5708     onLoad : function()
5709     {
5710         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5711     },
5712
5713     // private
5714     onBeforeLoad : function(){
5715         if(!this.disabled){
5716             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5717         }
5718     },
5719
5720     // private
5721     destroy : function(){
5722         if(this.store){
5723             this.store.un('beforeload', this.onBeforeLoad, this);
5724             this.store.un('load', this.onLoad, this);
5725             this.store.un('loadexception', this.onLoadException, this);
5726         }else{
5727             var um = this.el.getUpdateManager();
5728             um.un('beforeupdate', this.onBeforeLoad, this);
5729             um.un('update', this.onLoad, this);
5730             um.un('failure', this.onLoad, this);
5731         }
5732     }
5733 };/*
5734  * - LGPL
5735  *
5736  * table
5737  * 
5738  */
5739
5740 /**
5741  * @class Roo.bootstrap.Table
5742  * @extends Roo.bootstrap.Component
5743  * Bootstrap Table class
5744  * @cfg {String} cls table class
5745  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5746  * @cfg {String} bgcolor Specifies the background color for a table
5747  * @cfg {Number} border Specifies whether the table cells should have borders or not
5748  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5749  * @cfg {Number} cellspacing Specifies the space between cells
5750  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5751  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5752  * @cfg {String} sortable Specifies that the table should be sortable
5753  * @cfg {String} summary Specifies a summary of the content of a table
5754  * @cfg {Number} width Specifies the width of a table
5755  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5756  * 
5757  * @cfg {boolean} striped Should the rows be alternative striped
5758  * @cfg {boolean} bordered Add borders to the table
5759  * @cfg {boolean} hover Add hover highlighting
5760  * @cfg {boolean} condensed Format condensed
5761  * @cfg {boolean} responsive Format condensed
5762  * @cfg {Boolean} loadMask (true|false) default false
5763  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5764  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5765  * @cfg {Boolean} rowSelection (true|false) default false
5766  * @cfg {Boolean} cellSelection (true|false) default false
5767  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5768  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5769  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5770  
5771  * 
5772  * @constructor
5773  * Create a new Table
5774  * @param {Object} config The config object
5775  */
5776
5777 Roo.bootstrap.Table = function(config){
5778     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5779     
5780   
5781     
5782     // BC...
5783     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5784     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5785     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5786     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5787     
5788     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5789     if (this.sm) {
5790         this.sm.grid = this;
5791         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5792         this.sm = this.selModel;
5793         this.sm.xmodule = this.xmodule || false;
5794     }
5795     
5796     if (this.cm && typeof(this.cm.config) == 'undefined') {
5797         this.colModel = new Roo.grid.ColumnModel(this.cm);
5798         this.cm = this.colModel;
5799         this.cm.xmodule = this.xmodule || false;
5800     }
5801     if (this.store) {
5802         this.store= Roo.factory(this.store, Roo.data);
5803         this.ds = this.store;
5804         this.ds.xmodule = this.xmodule || false;
5805          
5806     }
5807     if (this.footer && this.store) {
5808         this.footer.dataSource = this.ds;
5809         this.footer = Roo.factory(this.footer);
5810     }
5811     
5812     /** @private */
5813     this.addEvents({
5814         /**
5815          * @event cellclick
5816          * Fires when a cell is clicked
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Roo.Element} el
5819          * @param {Number} rowIndex
5820          * @param {Number} columnIndex
5821          * @param {Roo.EventObject} e
5822          */
5823         "cellclick" : true,
5824         /**
5825          * @event celldblclick
5826          * Fires when a cell is double clicked
5827          * @param {Roo.bootstrap.Table} this
5828          * @param {Roo.Element} el
5829          * @param {Number} rowIndex
5830          * @param {Number} columnIndex
5831          * @param {Roo.EventObject} e
5832          */
5833         "celldblclick" : true,
5834         /**
5835          * @event rowclick
5836          * Fires when a row is clicked
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Roo.Element} el
5839          * @param {Number} rowIndex
5840          * @param {Roo.EventObject} e
5841          */
5842         "rowclick" : true,
5843         /**
5844          * @event rowdblclick
5845          * Fires when a row is double clicked
5846          * @param {Roo.bootstrap.Table} this
5847          * @param {Roo.Element} el
5848          * @param {Number} rowIndex
5849          * @param {Roo.EventObject} e
5850          */
5851         "rowdblclick" : true,
5852         /**
5853          * @event mouseover
5854          * Fires when a mouseover occur
5855          * @param {Roo.bootstrap.Table} this
5856          * @param {Roo.Element} el
5857          * @param {Number} rowIndex
5858          * @param {Number} columnIndex
5859          * @param {Roo.EventObject} e
5860          */
5861         "mouseover" : true,
5862         /**
5863          * @event mouseout
5864          * Fires when a mouseout occur
5865          * @param {Roo.bootstrap.Table} this
5866          * @param {Roo.Element} el
5867          * @param {Number} rowIndex
5868          * @param {Number} columnIndex
5869          * @param {Roo.EventObject} e
5870          */
5871         "mouseout" : true,
5872         /**
5873          * @event rowclass
5874          * Fires when a row is rendered, so you can change add a style to it.
5875          * @param {Roo.bootstrap.Table} this
5876          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5877          */
5878         'rowclass' : true,
5879           /**
5880          * @event rowsrendered
5881          * Fires when all the  rows have been rendered
5882          * @param {Roo.bootstrap.Table} this
5883          */
5884         'rowsrendered' : true,
5885         /**
5886          * @event contextmenu
5887          * The raw contextmenu event for the entire grid.
5888          * @param {Roo.EventObject} e
5889          */
5890         "contextmenu" : true,
5891         /**
5892          * @event rowcontextmenu
5893          * Fires when a row is right clicked
5894          * @param {Roo.bootstrap.Table} this
5895          * @param {Number} rowIndex
5896          * @param {Roo.EventObject} e
5897          */
5898         "rowcontextmenu" : true,
5899         /**
5900          * @event cellcontextmenu
5901          * Fires when a cell is right clicked
5902          * @param {Roo.bootstrap.Table} this
5903          * @param {Number} rowIndex
5904          * @param {Number} cellIndex
5905          * @param {Roo.EventObject} e
5906          */
5907          "cellcontextmenu" : true,
5908          /**
5909          * @event headercontextmenu
5910          * Fires when a header is right clicked
5911          * @param {Roo.bootstrap.Table} this
5912          * @param {Number} columnIndex
5913          * @param {Roo.EventObject} e
5914          */
5915         "headercontextmenu" : true
5916     });
5917 };
5918
5919 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5920     
5921     cls: false,
5922     align: false,
5923     bgcolor: false,
5924     border: false,
5925     cellpadding: false,
5926     cellspacing: false,
5927     frame: false,
5928     rules: false,
5929     sortable: false,
5930     summary: false,
5931     width: false,
5932     striped : false,
5933     scrollBody : false,
5934     bordered: false,
5935     hover:  false,
5936     condensed : false,
5937     responsive : false,
5938     sm : false,
5939     cm : false,
5940     store : false,
5941     loadMask : false,
5942     footerShow : true,
5943     headerShow : true,
5944   
5945     rowSelection : false,
5946     cellSelection : false,
5947     layout : false,
5948     
5949     // Roo.Element - the tbody
5950     mainBody: false,
5951     // Roo.Element - thead element
5952     mainHead: false,
5953     
5954     container: false, // used by gridpanel...
5955     
5956     lazyLoad : false,
5957     
5958     getAutoCreate : function()
5959     {
5960         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5961         
5962         cfg = {
5963             tag: 'table',
5964             cls : 'table',
5965             cn : []
5966         };
5967         if (this.scrollBody) {
5968             cfg.cls += ' table-body-fixed';
5969         }    
5970         if (this.striped) {
5971             cfg.cls += ' table-striped';
5972         }
5973         
5974         if (this.hover) {
5975             cfg.cls += ' table-hover';
5976         }
5977         if (this.bordered) {
5978             cfg.cls += ' table-bordered';
5979         }
5980         if (this.condensed) {
5981             cfg.cls += ' table-condensed';
5982         }
5983         if (this.responsive) {
5984             cfg.cls += ' table-responsive';
5985         }
5986         
5987         if (this.cls) {
5988             cfg.cls+=  ' ' +this.cls;
5989         }
5990         
5991         // this lot should be simplifed...
5992         
5993         if (this.align) {
5994             cfg.align=this.align;
5995         }
5996         if (this.bgcolor) {
5997             cfg.bgcolor=this.bgcolor;
5998         }
5999         if (this.border) {
6000             cfg.border=this.border;
6001         }
6002         if (this.cellpadding) {
6003             cfg.cellpadding=this.cellpadding;
6004         }
6005         if (this.cellspacing) {
6006             cfg.cellspacing=this.cellspacing;
6007         }
6008         if (this.frame) {
6009             cfg.frame=this.frame;
6010         }
6011         if (this.rules) {
6012             cfg.rules=this.rules;
6013         }
6014         if (this.sortable) {
6015             cfg.sortable=this.sortable;
6016         }
6017         if (this.summary) {
6018             cfg.summary=this.summary;
6019         }
6020         if (this.width) {
6021             cfg.width=this.width;
6022         }
6023         if (this.layout) {
6024             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6025         }
6026         
6027         if(this.store || this.cm){
6028             if(this.headerShow){
6029                 cfg.cn.push(this.renderHeader());
6030             }
6031             
6032             cfg.cn.push(this.renderBody());
6033             
6034             if(this.footerShow){
6035                 cfg.cn.push(this.renderFooter());
6036             }
6037             // where does this come from?
6038             //cfg.cls+=  ' TableGrid';
6039         }
6040         
6041         return { cn : [ cfg ] };
6042     },
6043     
6044     initEvents : function()
6045     {   
6046         if(!this.store || !this.cm){
6047             return;
6048         }
6049         if (this.selModel) {
6050             this.selModel.initEvents();
6051         }
6052         
6053         
6054         //Roo.log('initEvents with ds!!!!');
6055         
6056         this.mainBody = this.el.select('tbody', true).first();
6057         this.mainHead = this.el.select('thead', true).first();
6058         
6059         
6060         
6061         
6062         var _this = this;
6063         
6064         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6065             e.on('click', _this.sort, _this);
6066         });
6067         
6068         this.mainBody.on("click", this.onClick, this);
6069         this.mainBody.on("dblclick", this.onDblClick, this);
6070         
6071         // why is this done????? = it breaks dialogs??
6072         //this.parent().el.setStyle('position', 'relative');
6073         
6074         
6075         if (this.footer) {
6076             this.footer.parentId = this.id;
6077             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6078             
6079             if(this.lazyLoad){
6080                 this.el.select('tfoot tr td').first().addClass('hide');
6081             }
6082         } 
6083         
6084         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6085         
6086         this.store.on('load', this.onLoad, this);
6087         this.store.on('beforeload', this.onBeforeLoad, this);
6088         this.store.on('update', this.onUpdate, this);
6089         this.store.on('add', this.onAdd, this);
6090         this.store.on("clear", this.clear, this);
6091         
6092         this.el.on("contextmenu", this.onContextMenu, this);
6093         
6094         this.mainBody.on('scroll', this.onBodyScroll, this);
6095         
6096         
6097     },
6098     
6099     onContextMenu : function(e, t)
6100     {
6101         this.processEvent("contextmenu", e);
6102     },
6103     
6104     processEvent : function(name, e)
6105     {
6106         if (name != 'touchstart' ) {
6107             this.fireEvent(name, e);    
6108         }
6109         
6110         var t = e.getTarget();
6111         
6112         var cell = Roo.get(t);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(cell.findParent('tfoot', false, true)){
6119             return;
6120         }
6121         
6122         if(cell.findParent('thead', false, true)){
6123             
6124             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6125                 cell = Roo.get(t).findParent('th', false, true);
6126                 if (!cell) {
6127                     Roo.log("failed to find th in thead?");
6128                     Roo.log(e.getTarget());
6129                     return;
6130                 }
6131             }
6132             
6133             var cellIndex = cell.dom.cellIndex;
6134             
6135             var ename = name == 'touchstart' ? 'click' : name;
6136             this.fireEvent("header" + ename, this, cellIndex, e);
6137             
6138             return;
6139         }
6140         
6141         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6142             cell = Roo.get(t).findParent('td', false, true);
6143             if (!cell) {
6144                 Roo.log("failed to find th in tbody?");
6145                 Roo.log(e.getTarget());
6146                 return;
6147             }
6148         }
6149         
6150         var row = cell.findParent('tr', false, true);
6151         var cellIndex = cell.dom.cellIndex;
6152         var rowIndex = row.dom.rowIndex - 1;
6153         
6154         if(row !== false){
6155             
6156             this.fireEvent("row" + name, this, rowIndex, e);
6157             
6158             if(cell !== false){
6159             
6160                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6161             }
6162         }
6163         
6164     },
6165     
6166     onMouseover : function(e, el)
6167     {
6168         var cell = Roo.get(el);
6169         
6170         if(!cell){
6171             return;
6172         }
6173         
6174         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6175             cell = cell.findParent('td', false, true);
6176         }
6177         
6178         var row = cell.findParent('tr', false, true);
6179         var cellIndex = cell.dom.cellIndex;
6180         var rowIndex = row.dom.rowIndex - 1; // start from 0
6181         
6182         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6183         
6184     },
6185     
6186     onMouseout : function(e, el)
6187     {
6188         var cell = Roo.get(el);
6189         
6190         if(!cell){
6191             return;
6192         }
6193         
6194         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6195             cell = cell.findParent('td', false, true);
6196         }
6197         
6198         var row = cell.findParent('tr', false, true);
6199         var cellIndex = cell.dom.cellIndex;
6200         var rowIndex = row.dom.rowIndex - 1; // start from 0
6201         
6202         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6203         
6204     },
6205     
6206     onClick : function(e, el)
6207     {
6208         var cell = Roo.get(el);
6209         
6210         if(!cell || (!this.cellSelection && !this.rowSelection)){
6211             return;
6212         }
6213         
6214         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6215             cell = cell.findParent('td', false, true);
6216         }
6217         
6218         if(!cell || typeof(cell) == 'undefined'){
6219             return;
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         
6224         if(!row || typeof(row) == 'undefined'){
6225             return;
6226         }
6227         
6228         var cellIndex = cell.dom.cellIndex;
6229         var rowIndex = this.getRowIndex(row);
6230         
6231         // why??? - should these not be based on SelectionModel?
6232         if(this.cellSelection){
6233             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6234         }
6235         
6236         if(this.rowSelection){
6237             this.fireEvent('rowclick', this, row, rowIndex, e);
6238         }
6239         
6240         
6241     },
6242         
6243     onDblClick : function(e,el)
6244     {
6245         var cell = Roo.get(el);
6246         
6247         if(!cell || (!this.cellSelection && !this.rowSelection)){
6248             return;
6249         }
6250         
6251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6252             cell = cell.findParent('td', false, true);
6253         }
6254         
6255         if(!cell || typeof(cell) == 'undefined'){
6256             return;
6257         }
6258         
6259         var row = cell.findParent('tr', false, true);
6260         
6261         if(!row || typeof(row) == 'undefined'){
6262             return;
6263         }
6264         
6265         var cellIndex = cell.dom.cellIndex;
6266         var rowIndex = this.getRowIndex(row);
6267         
6268         if(this.cellSelection){
6269             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6270         }
6271         
6272         if(this.rowSelection){
6273             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6274         }
6275     },
6276     
6277     sort : function(e,el)
6278     {
6279         var col = Roo.get(el);
6280         
6281         if(!col.hasClass('sortable')){
6282             return;
6283         }
6284         
6285         var sort = col.attr('sort');
6286         var dir = 'ASC';
6287         
6288         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6289             dir = 'DESC';
6290         }
6291         
6292         this.store.sortInfo = {field : sort, direction : dir};
6293         
6294         if (this.footer) {
6295             Roo.log("calling footer first");
6296             this.footer.onClick('first');
6297         } else {
6298         
6299             this.store.load({ params : { start : 0 } });
6300         }
6301     },
6302     
6303     renderHeader : function()
6304     {
6305         var header = {
6306             tag: 'thead',
6307             cn : []
6308         };
6309         
6310         var cm = this.cm;
6311         this.totalWidth = 0;
6312         
6313         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6314             
6315             var config = cm.config[i];
6316             
6317             var c = {
6318                 tag: 'th',
6319                 style : '',
6320                 html: cm.getColumnHeader(i)
6321             };
6322             
6323             var hh = '';
6324             
6325             if(typeof(config.sortable) != 'undefined' && config.sortable){
6326                 c.cls = 'sortable';
6327                 c.html = '<i class="glyphicon"></i>' + c.html;
6328             }
6329             
6330             if(typeof(config.lgHeader) != 'undefined'){
6331                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6332             }
6333             
6334             if(typeof(config.mdHeader) != 'undefined'){
6335                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6336             }
6337             
6338             if(typeof(config.smHeader) != 'undefined'){
6339                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6340             }
6341             
6342             if(typeof(config.xsHeader) != 'undefined'){
6343                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6344             }
6345             
6346             if(hh.length){
6347                 c.html = hh;
6348             }
6349             
6350             if(typeof(config.tooltip) != 'undefined'){
6351                 c.tooltip = config.tooltip;
6352             }
6353             
6354             if(typeof(config.colspan) != 'undefined'){
6355                 c.colspan = config.colspan;
6356             }
6357             
6358             if(typeof(config.hidden) != 'undefined' && config.hidden){
6359                 c.style += ' display:none;';
6360             }
6361             
6362             if(typeof(config.dataIndex) != 'undefined'){
6363                 c.sort = config.dataIndex;
6364             }
6365             
6366            
6367             
6368             if(typeof(config.align) != 'undefined' && config.align.length){
6369                 c.style += ' text-align:' + config.align + ';';
6370             }
6371             
6372             if(typeof(config.width) != 'undefined'){
6373                 c.style += ' width:' + config.width + 'px;';
6374                 this.totalWidth += config.width;
6375             } else {
6376                 this.totalWidth += 100; // assume minimum of 100 per column?
6377             }
6378             
6379             if(typeof(config.cls) != 'undefined'){
6380                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6381             }
6382             
6383             ['xs','sm','md','lg'].map(function(size){
6384                 
6385                 if(typeof(config[size]) == 'undefined'){
6386                     return;
6387                 }
6388                 
6389                 if (!config[size]) { // 0 = hidden
6390                     c.cls += ' hidden-' + size;
6391                     return;
6392                 }
6393                 
6394                 c.cls += ' col-' + size + '-' + config[size];
6395
6396             });
6397             
6398             header.cn.push(c)
6399         }
6400         
6401         return header;
6402     },
6403     
6404     renderBody : function()
6405     {
6406         var body = {
6407             tag: 'tbody',
6408             cn : [
6409                 {
6410                     tag: 'tr',
6411                     cn : [
6412                         {
6413                             tag : 'td',
6414                             colspan :  this.cm.getColumnCount()
6415                         }
6416                     ]
6417                 }
6418             ]
6419         };
6420         
6421         return body;
6422     },
6423     
6424     renderFooter : function()
6425     {
6426         var footer = {
6427             tag: 'tfoot',
6428             cn : [
6429                 {
6430                     tag: 'tr',
6431                     cn : [
6432                         {
6433                             tag : 'td',
6434                             colspan :  this.cm.getColumnCount()
6435                         }
6436                     ]
6437                 }
6438             ]
6439         };
6440         
6441         return footer;
6442     },
6443     
6444     
6445     
6446     onLoad : function()
6447     {
6448 //        Roo.log('ds onload');
6449         this.clear();
6450         
6451         var _this = this;
6452         var cm = this.cm;
6453         var ds = this.store;
6454         
6455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6456             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6457             if (_this.store.sortInfo) {
6458                     
6459                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6460                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6461                 }
6462                 
6463                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6464                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6465                 }
6466             }
6467         });
6468         
6469         var tbody =  this.mainBody;
6470               
6471         if(ds.getCount() > 0){
6472             ds.data.each(function(d,rowIndex){
6473                 var row =  this.renderRow(cm, ds, rowIndex);
6474                 
6475                 tbody.createChild(row);
6476                 
6477                 var _this = this;
6478                 
6479                 if(row.cellObjects.length){
6480                     Roo.each(row.cellObjects, function(r){
6481                         _this.renderCellObject(r);
6482                     })
6483                 }
6484                 
6485             }, this);
6486         }
6487         
6488         Roo.each(this.el.select('tbody td', true).elements, function(e){
6489             e.on('mouseover', _this.onMouseover, _this);
6490         });
6491         
6492         Roo.each(this.el.select('tbody td', true).elements, function(e){
6493             e.on('mouseout', _this.onMouseout, _this);
6494         });
6495         this.fireEvent('rowsrendered', this);
6496         //if(this.loadMask){
6497         //    this.maskEl.hide();
6498         //}
6499         
6500         this.autoSize();
6501     },
6502     
6503     
6504     onUpdate : function(ds,record)
6505     {
6506         this.refreshRow(record);
6507         this.autoSize();
6508     },
6509     
6510     onRemove : function(ds, record, index, isUpdate){
6511         if(isUpdate !== true){
6512             this.fireEvent("beforerowremoved", this, index, record);
6513         }
6514         var bt = this.mainBody.dom;
6515         
6516         var rows = this.el.select('tbody > tr', true).elements;
6517         
6518         if(typeof(rows[index]) != 'undefined'){
6519             bt.removeChild(rows[index].dom);
6520         }
6521         
6522 //        if(bt.rows[index]){
6523 //            bt.removeChild(bt.rows[index]);
6524 //        }
6525         
6526         if(isUpdate !== true){
6527             //this.stripeRows(index);
6528             //this.syncRowHeights(index, index);
6529             //this.layout();
6530             this.fireEvent("rowremoved", this, index, record);
6531         }
6532     },
6533     
6534     onAdd : function(ds, records, rowIndex)
6535     {
6536         //Roo.log('on Add called');
6537         // - note this does not handle multiple adding very well..
6538         var bt = this.mainBody.dom;
6539         for (var i =0 ; i < records.length;i++) {
6540             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6541             //Roo.log(records[i]);
6542             //Roo.log(this.store.getAt(rowIndex+i));
6543             this.insertRow(this.store, rowIndex + i, false);
6544             return;
6545         }
6546         
6547     },
6548     
6549     
6550     refreshRow : function(record){
6551         var ds = this.store, index;
6552         if(typeof record == 'number'){
6553             index = record;
6554             record = ds.getAt(index);
6555         }else{
6556             index = ds.indexOf(record);
6557         }
6558         this.insertRow(ds, index, true);
6559         this.autoSize();
6560         this.onRemove(ds, record, index+1, true);
6561         this.autoSize();
6562         //this.syncRowHeights(index, index);
6563         //this.layout();
6564         this.fireEvent("rowupdated", this, index, record);
6565     },
6566     
6567     insertRow : function(dm, rowIndex, isUpdate){
6568         
6569         if(!isUpdate){
6570             this.fireEvent("beforerowsinserted", this, rowIndex);
6571         }
6572             //var s = this.getScrollState();
6573         var row = this.renderRow(this.cm, this.store, rowIndex);
6574         // insert before rowIndex..
6575         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6576         
6577         var _this = this;
6578                 
6579         if(row.cellObjects.length){
6580             Roo.each(row.cellObjects, function(r){
6581                 _this.renderCellObject(r);
6582             })
6583         }
6584             
6585         if(!isUpdate){
6586             this.fireEvent("rowsinserted", this, rowIndex);
6587             //this.syncRowHeights(firstRow, lastRow);
6588             //this.stripeRows(firstRow);
6589             //this.layout();
6590         }
6591         
6592     },
6593     
6594     
6595     getRowDom : function(rowIndex)
6596     {
6597         var rows = this.el.select('tbody > tr', true).elements;
6598         
6599         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6600         
6601     },
6602     // returns the object tree for a tr..
6603   
6604     
6605     renderRow : function(cm, ds, rowIndex) 
6606     {
6607         
6608         var d = ds.getAt(rowIndex);
6609         
6610         var row = {
6611             tag : 'tr',
6612             cn : []
6613         };
6614             
6615         var cellObjects = [];
6616         
6617         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6618             var config = cm.config[i];
6619             
6620             var renderer = cm.getRenderer(i);
6621             var value = '';
6622             var id = false;
6623             
6624             if(typeof(renderer) !== 'undefined'){
6625                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6626             }
6627             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6628             // and are rendered into the cells after the row is rendered - using the id for the element.
6629             
6630             if(typeof(value) === 'object'){
6631                 id = Roo.id();
6632                 cellObjects.push({
6633                     container : id,
6634                     cfg : value 
6635                 })
6636             }
6637             
6638             var rowcfg = {
6639                 record: d,
6640                 rowIndex : rowIndex,
6641                 colIndex : i,
6642                 rowClass : ''
6643             };
6644
6645             this.fireEvent('rowclass', this, rowcfg);
6646             
6647             var td = {
6648                 tag: 'td',
6649                 cls : rowcfg.rowClass,
6650                 style: '',
6651                 html: (typeof(value) === 'object') ? '' : value
6652             };
6653             
6654             if (id) {
6655                 td.id = id;
6656             }
6657             
6658             if(typeof(config.colspan) != 'undefined'){
6659                 td.colspan = config.colspan;
6660             }
6661             
6662             if(typeof(config.hidden) != 'undefined' && config.hidden){
6663                 td.style += ' display:none;';
6664             }
6665             
6666             if(typeof(config.align) != 'undefined' && config.align.length){
6667                 td.style += ' text-align:' + config.align + ';';
6668             }
6669             
6670             if(typeof(config.width) != 'undefined'){
6671                 td.style += ' width:' +  config.width + 'px;';
6672             }
6673             
6674             if(typeof(config.cursor) != 'undefined'){
6675                 td.style += ' cursor:' +  config.cursor + ';';
6676             }
6677             
6678             if(typeof(config.cls) != 'undefined'){
6679                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6680             }
6681             
6682             ['xs','sm','md','lg'].map(function(size){
6683                 
6684                 if(typeof(config[size]) == 'undefined'){
6685                     return;
6686                 }
6687                 
6688                 if (!config[size]) { // 0 = hidden
6689                     td.cls += ' hidden-' + size;
6690                     return;
6691                 }
6692                 
6693                 td.cls += ' col-' + size + '-' + config[size];
6694
6695             });
6696              
6697             row.cn.push(td);
6698            
6699         }
6700         
6701         row.cellObjects = cellObjects;
6702         
6703         return row;
6704           
6705     },
6706     
6707     
6708     
6709     onBeforeLoad : function()
6710     {
6711         //Roo.log('ds onBeforeLoad');
6712         
6713         //this.clear();
6714         
6715         //if(this.loadMask){
6716         //    this.maskEl.show();
6717         //}
6718     },
6719      /**
6720      * Remove all rows
6721      */
6722     clear : function()
6723     {
6724         this.el.select('tbody', true).first().dom.innerHTML = '';
6725     },
6726     /**
6727      * Show or hide a row.
6728      * @param {Number} rowIndex to show or hide
6729      * @param {Boolean} state hide
6730      */
6731     setRowVisibility : function(rowIndex, state)
6732     {
6733         var bt = this.mainBody.dom;
6734         
6735         var rows = this.el.select('tbody > tr', true).elements;
6736         
6737         if(typeof(rows[rowIndex]) == 'undefined'){
6738             return;
6739         }
6740         rows[rowIndex].dom.style.display = state ? '' : 'none';
6741     },
6742     
6743     
6744     getSelectionModel : function(){
6745         if(!this.selModel){
6746             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6747         }
6748         return this.selModel;
6749     },
6750     /*
6751      * Render the Roo.bootstrap object from renderder
6752      */
6753     renderCellObject : function(r)
6754     {
6755         var _this = this;
6756         
6757         var t = r.cfg.render(r.container);
6758         
6759         if(r.cfg.cn){
6760             Roo.each(r.cfg.cn, function(c){
6761                 var child = {
6762                     container: t.getChildContainer(),
6763                     cfg: c
6764                 };
6765                 _this.renderCellObject(child);
6766             })
6767         }
6768     },
6769     
6770     getRowIndex : function(row)
6771     {
6772         var rowIndex = -1;
6773         
6774         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6775             if(el != row){
6776                 return;
6777             }
6778             
6779             rowIndex = index;
6780         });
6781         
6782         return rowIndex;
6783     },
6784      /**
6785      * Returns the grid's underlying element = used by panel.Grid
6786      * @return {Element} The element
6787      */
6788     getGridEl : function(){
6789         return this.el;
6790     },
6791      /**
6792      * Forces a resize - used by panel.Grid
6793      * @return {Element} The element
6794      */
6795     autoSize : function()
6796     {
6797         //var ctr = Roo.get(this.container.dom.parentElement);
6798         var ctr = Roo.get(this.el.dom);
6799         
6800         var thd = this.getGridEl().select('thead',true).first();
6801         var tbd = this.getGridEl().select('tbody', true).first();
6802         var tfd = this.getGridEl().select('tfoot', true).first();
6803         
6804         var cw = ctr.getWidth();
6805         
6806         if (tbd) {
6807             
6808             tbd.setSize(ctr.getWidth(),
6809                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6810             );
6811             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6812             cw -= barsize;
6813         }
6814         cw = Math.max(cw, this.totalWidth);
6815         this.getGridEl().select('tr',true).setWidth(cw);
6816         // resize 'expandable coloumn?
6817         
6818         return; // we doe not have a view in this design..
6819         
6820     },
6821     onBodyScroll: function()
6822     {
6823         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6824         this.mainHead.setStyle({
6825             'position' : 'relative',
6826             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6827         });
6828         
6829         if(this.lazyLoad){
6830             
6831             var scrollHeight = this.mainBody.dom.scrollHeight;
6832             
6833             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6834             
6835             var height = this.mainBody.getHeight();
6836             
6837             if(scrollHeight - height == scrollTop) {
6838                 
6839                 var total = this.ds.getTotalCount();
6840                 
6841                 if(this.footer.cursor + this.footer.pageSize < total){
6842                     
6843                     this.footer.ds.load({
6844                         params : {
6845                             start : this.footer.cursor + this.footer.pageSize,
6846                             limit : this.footer.pageSize
6847                         },
6848                         add : true
6849                     });
6850                 }
6851             }
6852             
6853         }
6854     }
6855 });
6856
6857  
6858
6859  /*
6860  * - LGPL
6861  *
6862  * table cell
6863  * 
6864  */
6865
6866 /**
6867  * @class Roo.bootstrap.TableCell
6868  * @extends Roo.bootstrap.Component
6869  * Bootstrap TableCell class
6870  * @cfg {String} html cell contain text
6871  * @cfg {String} cls cell class
6872  * @cfg {String} tag cell tag (td|th) default td
6873  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6874  * @cfg {String} align Aligns the content in a cell
6875  * @cfg {String} axis Categorizes cells
6876  * @cfg {String} bgcolor Specifies the background color of a cell
6877  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6878  * @cfg {Number} colspan Specifies the number of columns a cell should span
6879  * @cfg {String} headers Specifies one or more header cells a cell is related to
6880  * @cfg {Number} height Sets the height of a cell
6881  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6882  * @cfg {Number} rowspan Sets the number of rows a cell should span
6883  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6884  * @cfg {String} valign Vertical aligns the content in a cell
6885  * @cfg {Number} width Specifies the width of a cell
6886  * 
6887  * @constructor
6888  * Create a new TableCell
6889  * @param {Object} config The config object
6890  */
6891
6892 Roo.bootstrap.TableCell = function(config){
6893     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6894 };
6895
6896 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6897     
6898     html: false,
6899     cls: false,
6900     tag: false,
6901     abbr: false,
6902     align: false,
6903     axis: false,
6904     bgcolor: false,
6905     charoff: false,
6906     colspan: false,
6907     headers: false,
6908     height: false,
6909     nowrap: false,
6910     rowspan: false,
6911     scope: false,
6912     valign: false,
6913     width: false,
6914     
6915     
6916     getAutoCreate : function(){
6917         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6918         
6919         cfg = {
6920             tag: 'td'
6921         };
6922         
6923         if(this.tag){
6924             cfg.tag = this.tag;
6925         }
6926         
6927         if (this.html) {
6928             cfg.html=this.html
6929         }
6930         if (this.cls) {
6931             cfg.cls=this.cls
6932         }
6933         if (this.abbr) {
6934             cfg.abbr=this.abbr
6935         }
6936         if (this.align) {
6937             cfg.align=this.align
6938         }
6939         if (this.axis) {
6940             cfg.axis=this.axis
6941         }
6942         if (this.bgcolor) {
6943             cfg.bgcolor=this.bgcolor
6944         }
6945         if (this.charoff) {
6946             cfg.charoff=this.charoff
6947         }
6948         if (this.colspan) {
6949             cfg.colspan=this.colspan
6950         }
6951         if (this.headers) {
6952             cfg.headers=this.headers
6953         }
6954         if (this.height) {
6955             cfg.height=this.height
6956         }
6957         if (this.nowrap) {
6958             cfg.nowrap=this.nowrap
6959         }
6960         if (this.rowspan) {
6961             cfg.rowspan=this.rowspan
6962         }
6963         if (this.scope) {
6964             cfg.scope=this.scope
6965         }
6966         if (this.valign) {
6967             cfg.valign=this.valign
6968         }
6969         if (this.width) {
6970             cfg.width=this.width
6971         }
6972         
6973         
6974         return cfg;
6975     }
6976    
6977 });
6978
6979  
6980
6981  /*
6982  * - LGPL
6983  *
6984  * table row
6985  * 
6986  */
6987
6988 /**
6989  * @class Roo.bootstrap.TableRow
6990  * @extends Roo.bootstrap.Component
6991  * Bootstrap TableRow class
6992  * @cfg {String} cls row class
6993  * @cfg {String} align Aligns the content in a table row
6994  * @cfg {String} bgcolor Specifies a background color for a table row
6995  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6996  * @cfg {String} valign Vertical aligns the content in a table row
6997  * 
6998  * @constructor
6999  * Create a new TableRow
7000  * @param {Object} config The config object
7001  */
7002
7003 Roo.bootstrap.TableRow = function(config){
7004     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7005 };
7006
7007 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7008     
7009     cls: false,
7010     align: false,
7011     bgcolor: false,
7012     charoff: false,
7013     valign: false,
7014     
7015     getAutoCreate : function(){
7016         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7017         
7018         cfg = {
7019             tag: 'tr'
7020         };
7021             
7022         if(this.cls){
7023             cfg.cls = this.cls;
7024         }
7025         if(this.align){
7026             cfg.align = this.align;
7027         }
7028         if(this.bgcolor){
7029             cfg.bgcolor = this.bgcolor;
7030         }
7031         if(this.charoff){
7032             cfg.charoff = this.charoff;
7033         }
7034         if(this.valign){
7035             cfg.valign = this.valign;
7036         }
7037         
7038         return cfg;
7039     }
7040    
7041 });
7042
7043  
7044
7045  /*
7046  * - LGPL
7047  *
7048  * table body
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.TableBody
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap TableBody class
7056  * @cfg {String} cls element class
7057  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7058  * @cfg {String} align Aligns the content inside the element
7059  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7060  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7061  * 
7062  * @constructor
7063  * Create a new TableBody
7064  * @param {Object} config The config object
7065  */
7066
7067 Roo.bootstrap.TableBody = function(config){
7068     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7069 };
7070
7071 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7072     
7073     cls: false,
7074     tag: false,
7075     align: false,
7076     charoff: false,
7077     valign: false,
7078     
7079     getAutoCreate : function(){
7080         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7081         
7082         cfg = {
7083             tag: 'tbody'
7084         };
7085             
7086         if (this.cls) {
7087             cfg.cls=this.cls
7088         }
7089         if(this.tag){
7090             cfg.tag = this.tag;
7091         }
7092         
7093         if(this.align){
7094             cfg.align = this.align;
7095         }
7096         if(this.charoff){
7097             cfg.charoff = this.charoff;
7098         }
7099         if(this.valign){
7100             cfg.valign = this.valign;
7101         }
7102         
7103         return cfg;
7104     }
7105     
7106     
7107 //    initEvents : function()
7108 //    {
7109 //        
7110 //        if(!this.store){
7111 //            return;
7112 //        }
7113 //        
7114 //        this.store = Roo.factory(this.store, Roo.data);
7115 //        this.store.on('load', this.onLoad, this);
7116 //        
7117 //        this.store.load();
7118 //        
7119 //    },
7120 //    
7121 //    onLoad: function () 
7122 //    {   
7123 //        this.fireEvent('load', this);
7124 //    }
7125 //    
7126 //   
7127 });
7128
7129  
7130
7131  /*
7132  * Based on:
7133  * Ext JS Library 1.1.1
7134  * Copyright(c) 2006-2007, Ext JS, LLC.
7135  *
7136  * Originally Released Under LGPL - original licence link has changed is not relivant.
7137  *
7138  * Fork - LGPL
7139  * <script type="text/javascript">
7140  */
7141
7142 // as we use this in bootstrap.
7143 Roo.namespace('Roo.form');
7144  /**
7145  * @class Roo.form.Action
7146  * Internal Class used to handle form actions
7147  * @constructor
7148  * @param {Roo.form.BasicForm} el The form element or its id
7149  * @param {Object} config Configuration options
7150  */
7151
7152  
7153  
7154 // define the action interface
7155 Roo.form.Action = function(form, options){
7156     this.form = form;
7157     this.options = options || {};
7158 };
7159 /**
7160  * Client Validation Failed
7161  * @const 
7162  */
7163 Roo.form.Action.CLIENT_INVALID = 'client';
7164 /**
7165  * Server Validation Failed
7166  * @const 
7167  */
7168 Roo.form.Action.SERVER_INVALID = 'server';
7169  /**
7170  * Connect to Server Failed
7171  * @const 
7172  */
7173 Roo.form.Action.CONNECT_FAILURE = 'connect';
7174 /**
7175  * Reading Data from Server Failed
7176  * @const 
7177  */
7178 Roo.form.Action.LOAD_FAILURE = 'load';
7179
7180 Roo.form.Action.prototype = {
7181     type : 'default',
7182     failureType : undefined,
7183     response : undefined,
7184     result : undefined,
7185
7186     // interface method
7187     run : function(options){
7188
7189     },
7190
7191     // interface method
7192     success : function(response){
7193
7194     },
7195
7196     // interface method
7197     handleResponse : function(response){
7198
7199     },
7200
7201     // default connection failure
7202     failure : function(response){
7203         
7204         this.response = response;
7205         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7206         this.form.afterAction(this, false);
7207     },
7208
7209     processResponse : function(response){
7210         this.response = response;
7211         if(!response.responseText){
7212             return true;
7213         }
7214         this.result = this.handleResponse(response);
7215         return this.result;
7216     },
7217
7218     // utility functions used internally
7219     getUrl : function(appendParams){
7220         var url = this.options.url || this.form.url || this.form.el.dom.action;
7221         if(appendParams){
7222             var p = this.getParams();
7223             if(p){
7224                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7225             }
7226         }
7227         return url;
7228     },
7229
7230     getMethod : function(){
7231         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7232     },
7233
7234     getParams : function(){
7235         var bp = this.form.baseParams;
7236         var p = this.options.params;
7237         if(p){
7238             if(typeof p == "object"){
7239                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7240             }else if(typeof p == 'string' && bp){
7241                 p += '&' + Roo.urlEncode(bp);
7242             }
7243         }else if(bp){
7244             p = Roo.urlEncode(bp);
7245         }
7246         return p;
7247     },
7248
7249     createCallback : function(){
7250         return {
7251             success: this.success,
7252             failure: this.failure,
7253             scope: this,
7254             timeout: (this.form.timeout*1000),
7255             upload: this.form.fileUpload ? this.success : undefined
7256         };
7257     }
7258 };
7259
7260 Roo.form.Action.Submit = function(form, options){
7261     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7262 };
7263
7264 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7265     type : 'submit',
7266
7267     haveProgress : false,
7268     uploadComplete : false,
7269     
7270     // uploadProgress indicator.
7271     uploadProgress : function()
7272     {
7273         if (!this.form.progressUrl) {
7274             return;
7275         }
7276         
7277         if (!this.haveProgress) {
7278             Roo.MessageBox.progress("Uploading", "Uploading");
7279         }
7280         if (this.uploadComplete) {
7281            Roo.MessageBox.hide();
7282            return;
7283         }
7284         
7285         this.haveProgress = true;
7286    
7287         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7288         
7289         var c = new Roo.data.Connection();
7290         c.request({
7291             url : this.form.progressUrl,
7292             params: {
7293                 id : uid
7294             },
7295             method: 'GET',
7296             success : function(req){
7297                //console.log(data);
7298                 var rdata = false;
7299                 var edata;
7300                 try  {
7301                    rdata = Roo.decode(req.responseText)
7302                 } catch (e) {
7303                     Roo.log("Invalid data from server..");
7304                     Roo.log(edata);
7305                     return;
7306                 }
7307                 if (!rdata || !rdata.success) {
7308                     Roo.log(rdata);
7309                     Roo.MessageBox.alert(Roo.encode(rdata));
7310                     return;
7311                 }
7312                 var data = rdata.data;
7313                 
7314                 if (this.uploadComplete) {
7315                    Roo.MessageBox.hide();
7316                    return;
7317                 }
7318                    
7319                 if (data){
7320                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7321                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7322                     );
7323                 }
7324                 this.uploadProgress.defer(2000,this);
7325             },
7326        
7327             failure: function(data) {
7328                 Roo.log('progress url failed ');
7329                 Roo.log(data);
7330             },
7331             scope : this
7332         });
7333            
7334     },
7335     
7336     
7337     run : function()
7338     {
7339         // run get Values on the form, so it syncs any secondary forms.
7340         this.form.getValues();
7341         
7342         var o = this.options;
7343         var method = this.getMethod();
7344         var isPost = method == 'POST';
7345         if(o.clientValidation === false || this.form.isValid()){
7346             
7347             if (this.form.progressUrl) {
7348                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7349                     (new Date() * 1) + '' + Math.random());
7350                     
7351             } 
7352             
7353             
7354             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7355                 form:this.form.el.dom,
7356                 url:this.getUrl(!isPost),
7357                 method: method,
7358                 params:isPost ? this.getParams() : null,
7359                 isUpload: this.form.fileUpload
7360             }));
7361             
7362             this.uploadProgress();
7363
7364         }else if (o.clientValidation !== false){ // client validation failed
7365             this.failureType = Roo.form.Action.CLIENT_INVALID;
7366             this.form.afterAction(this, false);
7367         }
7368     },
7369
7370     success : function(response)
7371     {
7372         this.uploadComplete= true;
7373         if (this.haveProgress) {
7374             Roo.MessageBox.hide();
7375         }
7376         
7377         
7378         var result = this.processResponse(response);
7379         if(result === true || result.success){
7380             this.form.afterAction(this, true);
7381             return;
7382         }
7383         if(result.errors){
7384             this.form.markInvalid(result.errors);
7385             this.failureType = Roo.form.Action.SERVER_INVALID;
7386         }
7387         this.form.afterAction(this, false);
7388     },
7389     failure : function(response)
7390     {
7391         this.uploadComplete= true;
7392         if (this.haveProgress) {
7393             Roo.MessageBox.hide();
7394         }
7395         
7396         this.response = response;
7397         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398         this.form.afterAction(this, false);
7399     },
7400     
7401     handleResponse : function(response){
7402         if(this.form.errorReader){
7403             var rs = this.form.errorReader.read(response);
7404             var errors = [];
7405             if(rs.records){
7406                 for(var i = 0, len = rs.records.length; i < len; i++) {
7407                     var r = rs.records[i];
7408                     errors[i] = r.data;
7409                 }
7410             }
7411             if(errors.length < 1){
7412                 errors = null;
7413             }
7414             return {
7415                 success : rs.success,
7416                 errors : errors
7417             };
7418         }
7419         var ret = false;
7420         try {
7421             ret = Roo.decode(response.responseText);
7422         } catch (e) {
7423             ret = {
7424                 success: false,
7425                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7426                 errors : []
7427             };
7428         }
7429         return ret;
7430         
7431     }
7432 });
7433
7434
7435 Roo.form.Action.Load = function(form, options){
7436     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7437     this.reader = this.form.reader;
7438 };
7439
7440 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7441     type : 'load',
7442
7443     run : function(){
7444         
7445         Roo.Ajax.request(Roo.apply(
7446                 this.createCallback(), {
7447                     method:this.getMethod(),
7448                     url:this.getUrl(false),
7449                     params:this.getParams()
7450         }));
7451     },
7452
7453     success : function(response){
7454         
7455         var result = this.processResponse(response);
7456         if(result === true || !result.success || !result.data){
7457             this.failureType = Roo.form.Action.LOAD_FAILURE;
7458             this.form.afterAction(this, false);
7459             return;
7460         }
7461         this.form.clearInvalid();
7462         this.form.setValues(result.data);
7463         this.form.afterAction(this, true);
7464     },
7465
7466     handleResponse : function(response){
7467         if(this.form.reader){
7468             var rs = this.form.reader.read(response);
7469             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7470             return {
7471                 success : rs.success,
7472                 data : data
7473             };
7474         }
7475         return Roo.decode(response.responseText);
7476     }
7477 });
7478
7479 Roo.form.Action.ACTION_TYPES = {
7480     'load' : Roo.form.Action.Load,
7481     'submit' : Roo.form.Action.Submit
7482 };/*
7483  * - LGPL
7484  *
7485  * form
7486  *
7487  */
7488
7489 /**
7490  * @class Roo.bootstrap.Form
7491  * @extends Roo.bootstrap.Component
7492  * Bootstrap Form class
7493  * @cfg {String} method  GET | POST (default POST)
7494  * @cfg {String} labelAlign top | left (default top)
7495  * @cfg {String} align left  | right - for navbars
7496  * @cfg {Boolean} loadMask load mask when submit (default true)
7497
7498  *
7499  * @constructor
7500  * Create a new Form
7501  * @param {Object} config The config object
7502  */
7503
7504
7505 Roo.bootstrap.Form = function(config){
7506     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7507     
7508     Roo.bootstrap.Form.popover.apply();
7509     
7510     this.addEvents({
7511         /**
7512          * @event clientvalidation
7513          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7514          * @param {Form} this
7515          * @param {Boolean} valid true if the form has passed client-side validation
7516          */
7517         clientvalidation: true,
7518         /**
7519          * @event beforeaction
7520          * Fires before any action is performed. Return false to cancel the action.
7521          * @param {Form} this
7522          * @param {Action} action The action to be performed
7523          */
7524         beforeaction: true,
7525         /**
7526          * @event actionfailed
7527          * Fires when an action fails.
7528          * @param {Form} this
7529          * @param {Action} action The action that failed
7530          */
7531         actionfailed : true,
7532         /**
7533          * @event actioncomplete
7534          * Fires when an action is completed.
7535          * @param {Form} this
7536          * @param {Action} action The action that completed
7537          */
7538         actioncomplete : true
7539     });
7540
7541 };
7542
7543 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7544
7545      /**
7546      * @cfg {String} method
7547      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7548      */
7549     method : 'POST',
7550     /**
7551      * @cfg {String} url
7552      * The URL to use for form actions if one isn't supplied in the action options.
7553      */
7554     /**
7555      * @cfg {Boolean} fileUpload
7556      * Set to true if this form is a file upload.
7557      */
7558
7559     /**
7560      * @cfg {Object} baseParams
7561      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7562      */
7563
7564     /**
7565      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7566      */
7567     timeout: 30,
7568     /**
7569      * @cfg {Sting} align (left|right) for navbar forms
7570      */
7571     align : 'left',
7572
7573     // private
7574     activeAction : null,
7575
7576     /**
7577      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7578      * element by passing it or its id or mask the form itself by passing in true.
7579      * @type Mixed
7580      */
7581     waitMsgTarget : false,
7582
7583     loadMask : true,
7584     
7585     /**
7586      * @cfg {Boolean} errorMask (true|false) default false
7587      */
7588     errorMask : false,
7589
7590     getAutoCreate : function(){
7591
7592         var cfg = {
7593             tag: 'form',
7594             method : this.method || 'POST',
7595             id : this.id || Roo.id(),
7596             cls : ''
7597         };
7598         if (this.parent().xtype.match(/^Nav/)) {
7599             cfg.cls = 'navbar-form navbar-' + this.align;
7600
7601         }
7602
7603         if (this.labelAlign == 'left' ) {
7604             cfg.cls += ' form-horizontal';
7605         }
7606
7607
7608         return cfg;
7609     },
7610     initEvents : function()
7611     {
7612         this.el.on('submit', this.onSubmit, this);
7613         // this was added as random key presses on the form where triggering form submit.
7614         this.el.on('keypress', function(e) {
7615             if (e.getCharCode() != 13) {
7616                 return true;
7617             }
7618             // we might need to allow it for textareas.. and some other items.
7619             // check e.getTarget().
7620
7621             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7622                 return true;
7623             }
7624
7625             Roo.log("keypress blocked");
7626
7627             e.preventDefault();
7628             return false;
7629         });
7630         
7631     },
7632     // private
7633     onSubmit : function(e){
7634         e.stopEvent();
7635     },
7636
7637      /**
7638      * Returns true if client-side validation on the form is successful.
7639      * @return Boolean
7640      */
7641     isValid : function(){
7642         var items = this.getItems();
7643         var valid = true;
7644         var target = false;
7645         
7646         items.each(function(f){
7647             
7648             if(f.validate()){
7649                 return;
7650             }
7651             valid = false;
7652
7653             if(!target && f.el.isVisible(true)){
7654                 target = f;
7655             }
7656            
7657         });
7658         
7659         if(this.errorMask && !valid){
7660             Roo.bootstrap.Form.popover.mask(this, target);
7661         }
7662         
7663         return valid;
7664     },
7665     
7666     /**
7667      * Returns true if any fields in this form have changed since their original load.
7668      * @return Boolean
7669      */
7670     isDirty : function(){
7671         var dirty = false;
7672         var items = this.getItems();
7673         items.each(function(f){
7674            if(f.isDirty()){
7675                dirty = true;
7676                return false;
7677            }
7678            return true;
7679         });
7680         return dirty;
7681     },
7682      /**
7683      * Performs a predefined action (submit or load) or custom actions you define on this form.
7684      * @param {String} actionName The name of the action type
7685      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7686      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7687      * accept other config options):
7688      * <pre>
7689 Property          Type             Description
7690 ----------------  ---------------  ----------------------------------------------------------------------------------
7691 url               String           The url for the action (defaults to the form's url)
7692 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7693 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7694 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7695                                    validate the form on the client (defaults to false)
7696      * </pre>
7697      * @return {BasicForm} this
7698      */
7699     doAction : function(action, options){
7700         if(typeof action == 'string'){
7701             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7702         }
7703         if(this.fireEvent('beforeaction', this, action) !== false){
7704             this.beforeAction(action);
7705             action.run.defer(100, action);
7706         }
7707         return this;
7708     },
7709
7710     // private
7711     beforeAction : function(action){
7712         var o = action.options;
7713
7714         if(this.loadMask){
7715             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7716         }
7717         // not really supported yet.. ??
7718
7719         //if(this.waitMsgTarget === true){
7720         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7721         //}else if(this.waitMsgTarget){
7722         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7723         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7724         //}else {
7725         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7726        // }
7727
7728     },
7729
7730     // private
7731     afterAction : function(action, success){
7732         this.activeAction = null;
7733         var o = action.options;
7734
7735         //if(this.waitMsgTarget === true){
7736             this.el.unmask();
7737         //}else if(this.waitMsgTarget){
7738         //    this.waitMsgTarget.unmask();
7739         //}else{
7740         //    Roo.MessageBox.updateProgress(1);
7741         //    Roo.MessageBox.hide();
7742        // }
7743         //
7744         if(success){
7745             if(o.reset){
7746                 this.reset();
7747             }
7748             Roo.callback(o.success, o.scope, [this, action]);
7749             this.fireEvent('actioncomplete', this, action);
7750
7751         }else{
7752
7753             // failure condition..
7754             // we have a scenario where updates need confirming.
7755             // eg. if a locking scenario exists..
7756             // we look for { errors : { needs_confirm : true }} in the response.
7757             if (
7758                 (typeof(action.result) != 'undefined')  &&
7759                 (typeof(action.result.errors) != 'undefined')  &&
7760                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7761            ){
7762                 var _t = this;
7763                 Roo.log("not supported yet");
7764                  /*
7765
7766                 Roo.MessageBox.confirm(
7767                     "Change requires confirmation",
7768                     action.result.errorMsg,
7769                     function(r) {
7770                         if (r != 'yes') {
7771                             return;
7772                         }
7773                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7774                     }
7775
7776                 );
7777                 */
7778
7779
7780                 return;
7781             }
7782
7783             Roo.callback(o.failure, o.scope, [this, action]);
7784             // show an error message if no failed handler is set..
7785             if (!this.hasListener('actionfailed')) {
7786                 Roo.log("need to add dialog support");
7787                 /*
7788                 Roo.MessageBox.alert("Error",
7789                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7790                         action.result.errorMsg :
7791                         "Saving Failed, please check your entries or try again"
7792                 );
7793                 */
7794             }
7795
7796             this.fireEvent('actionfailed', this, action);
7797         }
7798
7799     },
7800     /**
7801      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7802      * @param {String} id The value to search for
7803      * @return Field
7804      */
7805     findField : function(id){
7806         var items = this.getItems();
7807         var field = items.get(id);
7808         if(!field){
7809              items.each(function(f){
7810                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7811                     field = f;
7812                     return false;
7813                 }
7814                 return true;
7815             });
7816         }
7817         return field || null;
7818     },
7819      /**
7820      * Mark fields in this form invalid in bulk.
7821      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7822      * @return {BasicForm} this
7823      */
7824     markInvalid : function(errors){
7825         if(errors instanceof Array){
7826             for(var i = 0, len = errors.length; i < len; i++){
7827                 var fieldError = errors[i];
7828                 var f = this.findField(fieldError.id);
7829                 if(f){
7830                     f.markInvalid(fieldError.msg);
7831                 }
7832             }
7833         }else{
7834             var field, id;
7835             for(id in errors){
7836                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7837                     field.markInvalid(errors[id]);
7838                 }
7839             }
7840         }
7841         //Roo.each(this.childForms || [], function (f) {
7842         //    f.markInvalid(errors);
7843         //});
7844
7845         return this;
7846     },
7847
7848     /**
7849      * Set values for fields in this form in bulk.
7850      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7851      * @return {BasicForm} this
7852      */
7853     setValues : function(values){
7854         if(values instanceof Array){ // array of objects
7855             for(var i = 0, len = values.length; i < len; i++){
7856                 var v = values[i];
7857                 var f = this.findField(v.id);
7858                 if(f){
7859                     f.setValue(v.value);
7860                     if(this.trackResetOnLoad){
7861                         f.originalValue = f.getValue();
7862                     }
7863                 }
7864             }
7865         }else{ // object hash
7866             var field, id;
7867             for(id in values){
7868                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7869
7870                     if (field.setFromData &&
7871                         field.valueField &&
7872                         field.displayField &&
7873                         // combos' with local stores can
7874                         // be queried via setValue()
7875                         // to set their value..
7876                         (field.store && !field.store.isLocal)
7877                         ) {
7878                         // it's a combo
7879                         var sd = { };
7880                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7881                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7882                         field.setFromData(sd);
7883
7884                     } else {
7885                         field.setValue(values[id]);
7886                     }
7887
7888
7889                     if(this.trackResetOnLoad){
7890                         field.originalValue = field.getValue();
7891                     }
7892                 }
7893             }
7894         }
7895
7896         //Roo.each(this.childForms || [], function (f) {
7897         //    f.setValues(values);
7898         //});
7899
7900         return this;
7901     },
7902
7903     /**
7904      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7905      * they are returned as an array.
7906      * @param {Boolean} asString
7907      * @return {Object}
7908      */
7909     getValues : function(asString){
7910         //if (this.childForms) {
7911             // copy values from the child forms
7912         //    Roo.each(this.childForms, function (f) {
7913         //        this.setValues(f.getValues());
7914         //    }, this);
7915         //}
7916
7917
7918
7919         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7920         if(asString === true){
7921             return fs;
7922         }
7923         return Roo.urlDecode(fs);
7924     },
7925
7926     /**
7927      * Returns the fields in this form as an object with key/value pairs.
7928      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7929      * @return {Object}
7930      */
7931     getFieldValues : function(with_hidden)
7932     {
7933         var items = this.getItems();
7934         var ret = {};
7935         items.each(function(f){
7936             if (!f.getName()) {
7937                 return;
7938             }
7939             var v = f.getValue();
7940             if (f.inputType =='radio') {
7941                 if (typeof(ret[f.getName()]) == 'undefined') {
7942                     ret[f.getName()] = ''; // empty..
7943                 }
7944
7945                 if (!f.el.dom.checked) {
7946                     return;
7947
7948                 }
7949                 v = f.el.dom.value;
7950
7951             }
7952
7953             // not sure if this supported any more..
7954             if ((typeof(v) == 'object') && f.getRawValue) {
7955                 v = f.getRawValue() ; // dates..
7956             }
7957             // combo boxes where name != hiddenName...
7958             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7959                 ret[f.name] = f.getRawValue();
7960             }
7961             ret[f.getName()] = v;
7962         });
7963
7964         return ret;
7965     },
7966
7967     /**
7968      * Clears all invalid messages in this form.
7969      * @return {BasicForm} this
7970      */
7971     clearInvalid : function(){
7972         var items = this.getItems();
7973
7974         items.each(function(f){
7975            f.clearInvalid();
7976         });
7977
7978
7979
7980         return this;
7981     },
7982
7983     /**
7984      * Resets this form.
7985      * @return {BasicForm} this
7986      */
7987     reset : function(){
7988         var items = this.getItems();
7989         items.each(function(f){
7990             f.reset();
7991         });
7992
7993         Roo.each(this.childForms || [], function (f) {
7994             f.reset();
7995         });
7996
7997
7998         return this;
7999     },
8000     getItems : function()
8001     {
8002         var r=new Roo.util.MixedCollection(false, function(o){
8003             return o.id || (o.id = Roo.id());
8004         });
8005         var iter = function(el) {
8006             if (el.inputEl) {
8007                 r.add(el);
8008             }
8009             if (!el.items) {
8010                 return;
8011             }
8012             Roo.each(el.items,function(e) {
8013                 iter(e);
8014             });
8015
8016
8017         };
8018
8019         iter(this);
8020         return r;
8021
8022
8023
8024
8025     }
8026
8027 });
8028
8029 Roo.apply(Roo.bootstrap.Form, {
8030     
8031     popover : {
8032         
8033         padding : 5,
8034         
8035         isApplied : false,
8036         
8037         isMasked : false,
8038         
8039         form : false,
8040         
8041         target : false,
8042         
8043         toolTip : false,
8044         
8045         intervalID : false,
8046         
8047         maskEl : false,
8048         
8049         apply : function()
8050         {
8051             if(this.isApplied){
8052                 return;
8053             }
8054             
8055             this.maskEl = {
8056                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8057                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8058                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8059                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8060             };
8061             
8062             this.maskEl.top.enableDisplayMode("block");
8063             this.maskEl.left.enableDisplayMode("block");
8064             this.maskEl.bottom.enableDisplayMode("block");
8065             this.maskEl.right.enableDisplayMode("block");
8066             
8067             this.toolTip = new Roo.bootstrap.Tooltip({
8068                 cls : 'roo-form-error-popover',
8069                 alignment : {
8070                     'left' : ['r-l', [-2,0], 'right'],
8071                     'right' : ['l-r', [2,0], 'left'],
8072                     'bottom' : ['tl-bl', [0,2], 'top'],
8073                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8074                 }
8075             });
8076             
8077             this.toolTip.render(Roo.get(document.body));
8078
8079             this.toolTip.el.enableDisplayMode("block");
8080             
8081             Roo.get(document.body).on('click', function(){
8082                 this.unmask();
8083             }, this);
8084             
8085             this.isApplied = true
8086         },
8087         
8088         mask : function(form, target)
8089         {
8090             this.form = form;
8091             
8092             this.target = target;
8093             
8094             if(!this.form.errorMask || !target.el){
8095                 return;
8096             }
8097             
8098             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8099             
8100             var ot = this.target.el.calcOffsetsTo(scrollable);
8101             
8102             var scrollTo = ot[1] - 100;
8103             
8104             var maxScroll = Roo.lib.Dom.getDocumentHeight() - Roo.lib.Dom.getViewportHeight();
8105             
8106             scrollTo = Math.min(scrollTo, maxScroll);
8107             
8108             scrollable.scrollTo('top', scrollTo);
8109             
8110             var box = this.target.el.getBox();
8111
8112             var zIndex = Roo.bootstrap.Modal.zIndex++;
8113
8114             this.maskEl.top.setStyle('position', 'fixed');
8115             this.maskEl.top.setStyle('z-index', zIndex);
8116             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8117             this.maskEl.top.setXY([0, 0]);
8118             this.maskEl.top.show();
8119
8120             this.maskEl.left.setStyle('position', 'fixed');
8121             this.maskEl.left.setStyle('z-index', zIndex);
8122             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8123             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8124             this.maskEl.left.show();
8125
8126             this.maskEl.bottom.setStyle('position', 'fixed');
8127             this.maskEl.bottom.setStyle('z-index', zIndex);
8128             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8129             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8130             this.maskEl.bottom.show();
8131
8132             this.maskEl.right.setStyle('position', 'fixed');
8133             this.maskEl.right.setStyle('z-index', zIndex);
8134             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8135             this.maskEl.right.setXY([0, box.y - this.padding]);
8136             this.maskEl.right.show();
8137
8138
8139             this.toolTip.bindEl = this.target.el;
8140
8141             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8142
8143             var tip = this.target.blankText;
8144
8145             if(this.target.getValue() !== '' && this.target.regexText.length){
8146                 tip = this.target.regexText;
8147             }
8148
8149             this.toolTip.show(tip);
8150
8151             this.intervalID = window.setInterval(function() {
8152                 Roo.bootstrap.Form.popover.unmask();
8153             }, 10000);
8154
8155             window.onwheel = function(){ return false;};
8156             
8157             (function(){ this.isMasked = true; }).defer(500, this);
8158                 
8159             
8160             
8161         },
8162         
8163         unmask : function()
8164         {
8165             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8166                 return;
8167             }
8168             
8169             this.maskEl.top.setStyle('position', 'absolute');
8170             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8171             this.maskEl.top.hide();
8172
8173             this.maskEl.left.setStyle('position', 'absolute');
8174             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8175             this.maskEl.left.hide();
8176
8177             this.maskEl.bottom.setStyle('position', 'absolute');
8178             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8179             this.maskEl.bottom.hide();
8180
8181             this.maskEl.right.setStyle('position', 'absolute');
8182             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8183             this.maskEl.right.hide();
8184             
8185             this.toolTip.hide();
8186             
8187             this.toolTip.el.hide();
8188             
8189             window.onwheel = function(){ return true;};
8190             
8191             if(this.intervalID){
8192                 window.clearInterval(this.intervalID);
8193                 this.intervalID = false;
8194             }
8195             
8196             this.isMasked = false;
8197             
8198         }
8199         
8200     }
8201     
8202 });
8203
8204 /*
8205  * Based on:
8206  * Ext JS Library 1.1.1
8207  * Copyright(c) 2006-2007, Ext JS, LLC.
8208  *
8209  * Originally Released Under LGPL - original licence link has changed is not relivant.
8210  *
8211  * Fork - LGPL
8212  * <script type="text/javascript">
8213  */
8214 /**
8215  * @class Roo.form.VTypes
8216  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8217  * @singleton
8218  */
8219 Roo.form.VTypes = function(){
8220     // closure these in so they are only created once.
8221     var alpha = /^[a-zA-Z_]+$/;
8222     var alphanum = /^[a-zA-Z0-9_]+$/;
8223     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8224     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8225
8226     // All these messages and functions are configurable
8227     return {
8228         /**
8229          * The function used to validate email addresses
8230          * @param {String} value The email address
8231          */
8232         'email' : function(v){
8233             return email.test(v);
8234         },
8235         /**
8236          * The error text to display when the email validation function returns false
8237          * @type String
8238          */
8239         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8240         /**
8241          * The keystroke filter mask to be applied on email input
8242          * @type RegExp
8243          */
8244         'emailMask' : /[a-z0-9_\.\-@]/i,
8245
8246         /**
8247          * The function used to validate URLs
8248          * @param {String} value The URL
8249          */
8250         'url' : function(v){
8251             return url.test(v);
8252         },
8253         /**
8254          * The error text to display when the url validation function returns false
8255          * @type String
8256          */
8257         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8258         
8259         /**
8260          * The function used to validate alpha values
8261          * @param {String} value The value
8262          */
8263         'alpha' : function(v){
8264             return alpha.test(v);
8265         },
8266         /**
8267          * The error text to display when the alpha validation function returns false
8268          * @type String
8269          */
8270         'alphaText' : 'This field should only contain letters and _',
8271         /**
8272          * The keystroke filter mask to be applied on alpha input
8273          * @type RegExp
8274          */
8275         'alphaMask' : /[a-z_]/i,
8276
8277         /**
8278          * The function used to validate alphanumeric values
8279          * @param {String} value The value
8280          */
8281         'alphanum' : function(v){
8282             return alphanum.test(v);
8283         },
8284         /**
8285          * The error text to display when the alphanumeric validation function returns false
8286          * @type String
8287          */
8288         'alphanumText' : 'This field should only contain letters, numbers and _',
8289         /**
8290          * The keystroke filter mask to be applied on alphanumeric input
8291          * @type RegExp
8292          */
8293         'alphanumMask' : /[a-z0-9_]/i
8294     };
8295 }();/*
8296  * - LGPL
8297  *
8298  * Input
8299  * 
8300  */
8301
8302 /**
8303  * @class Roo.bootstrap.Input
8304  * @extends Roo.bootstrap.Component
8305  * Bootstrap Input class
8306  * @cfg {Boolean} disabled is it disabled
8307  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8308  * @cfg {String} name name of the input
8309  * @cfg {string} fieldLabel - the label associated
8310  * @cfg {string} placeholder - placeholder to put in text.
8311  * @cfg {string}  before - input group add on before
8312  * @cfg {string} after - input group add on after
8313  * @cfg {string} size - (lg|sm) or leave empty..
8314  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8315  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8316  * @cfg {Number} md colspan out of 12 for computer-sized screens
8317  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8318  * @cfg {string} value default value of the input
8319  * @cfg {Number} labelWidth set the width of label 
8320  * @cfg {Number} labellg set the width of label (1-12)
8321  * @cfg {Number} labelmd set the width of label (1-12)
8322  * @cfg {Number} labelsm set the width of label (1-12)
8323  * @cfg {Number} labelxs set the width of label (1-12)
8324  * @cfg {String} labelAlign (top|left)
8325  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8326  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8327  * @cfg {String} indicatorpos (left|right) default left
8328
8329  * @cfg {String} align (left|center|right) Default left
8330  * @cfg {Boolean} forceFeedback (true|false) Default false
8331  * 
8332  * 
8333  * 
8334  * 
8335  * @constructor
8336  * Create a new Input
8337  * @param {Object} config The config object
8338  */
8339
8340 Roo.bootstrap.Input = function(config){
8341     
8342     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8343     
8344     this.addEvents({
8345         /**
8346          * @event focus
8347          * Fires when this field receives input focus.
8348          * @param {Roo.form.Field} this
8349          */
8350         focus : true,
8351         /**
8352          * @event blur
8353          * Fires when this field loses input focus.
8354          * @param {Roo.form.Field} this
8355          */
8356         blur : true,
8357         /**
8358          * @event specialkey
8359          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8360          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8361          * @param {Roo.form.Field} this
8362          * @param {Roo.EventObject} e The event object
8363          */
8364         specialkey : true,
8365         /**
8366          * @event change
8367          * Fires just before the field blurs if the field value has changed.
8368          * @param {Roo.form.Field} this
8369          * @param {Mixed} newValue The new value
8370          * @param {Mixed} oldValue The original value
8371          */
8372         change : true,
8373         /**
8374          * @event invalid
8375          * Fires after the field has been marked as invalid.
8376          * @param {Roo.form.Field} this
8377          * @param {String} msg The validation message
8378          */
8379         invalid : true,
8380         /**
8381          * @event valid
8382          * Fires after the field has been validated with no errors.
8383          * @param {Roo.form.Field} this
8384          */
8385         valid : true,
8386          /**
8387          * @event keyup
8388          * Fires after the key up
8389          * @param {Roo.form.Field} this
8390          * @param {Roo.EventObject}  e The event Object
8391          */
8392         keyup : true
8393     });
8394 };
8395
8396 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8397      /**
8398      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8399       automatic validation (defaults to "keyup").
8400      */
8401     validationEvent : "keyup",
8402      /**
8403      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8404      */
8405     validateOnBlur : true,
8406     /**
8407      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8408      */
8409     validationDelay : 250,
8410      /**
8411      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8412      */
8413     focusClass : "x-form-focus",  // not needed???
8414     
8415        
8416     /**
8417      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8418      */
8419     invalidClass : "has-warning",
8420     
8421     /**
8422      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8423      */
8424     validClass : "has-success",
8425     
8426     /**
8427      * @cfg {Boolean} hasFeedback (true|false) default true
8428      */
8429     hasFeedback : true,
8430     
8431     /**
8432      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8433      */
8434     invalidFeedbackClass : "glyphicon-warning-sign",
8435     
8436     /**
8437      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8438      */
8439     validFeedbackClass : "glyphicon-ok",
8440     
8441     /**
8442      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8443      */
8444     selectOnFocus : false,
8445     
8446      /**
8447      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8448      */
8449     maskRe : null,
8450        /**
8451      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8452      */
8453     vtype : null,
8454     
8455       /**
8456      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8457      */
8458     disableKeyFilter : false,
8459     
8460        /**
8461      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8462      */
8463     disabled : false,
8464      /**
8465      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8466      */
8467     allowBlank : true,
8468     /**
8469      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8470      */
8471     blankText : "Please complete this mandatory field",
8472     
8473      /**
8474      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8475      */
8476     minLength : 0,
8477     /**
8478      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8479      */
8480     maxLength : Number.MAX_VALUE,
8481     /**
8482      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8483      */
8484     minLengthText : "The minimum length for this field is {0}",
8485     /**
8486      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8487      */
8488     maxLengthText : "The maximum length for this field is {0}",
8489   
8490     
8491     /**
8492      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8493      * If available, this function will be called only after the basic validators all return true, and will be passed the
8494      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8495      */
8496     validator : null,
8497     /**
8498      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8499      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8500      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8501      */
8502     regex : null,
8503     /**
8504      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8505      */
8506     regexText : "",
8507     
8508     autocomplete: false,
8509     
8510     
8511     fieldLabel : '',
8512     inputType : 'text',
8513     
8514     name : false,
8515     placeholder: false,
8516     before : false,
8517     after : false,
8518     size : false,
8519     hasFocus : false,
8520     preventMark: false,
8521     isFormField : true,
8522     value : '',
8523     labelWidth : 2,
8524     labelAlign : false,
8525     readOnly : false,
8526     align : false,
8527     formatedValue : false,
8528     forceFeedback : false,
8529     
8530     indicatorpos : 'left',
8531     
8532     labellg : 0,
8533     labelmd : 0,
8534     labelsm : 0,
8535     labelxs : 0,
8536     
8537     parentLabelAlign : function()
8538     {
8539         var parent = this;
8540         while (parent.parent()) {
8541             parent = parent.parent();
8542             if (typeof(parent.labelAlign) !='undefined') {
8543                 return parent.labelAlign;
8544             }
8545         }
8546         return 'left';
8547         
8548     },
8549     
8550     getAutoCreate : function()
8551     {
8552         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8553         
8554         var id = Roo.id();
8555         
8556         var cfg = {};
8557         
8558         if(this.inputType != 'hidden'){
8559             cfg.cls = 'form-group' //input-group
8560         }
8561         
8562         var input =  {
8563             tag: 'input',
8564             id : id,
8565             type : this.inputType,
8566             value : this.value,
8567             cls : 'form-control',
8568             placeholder : this.placeholder || '',
8569             autocomplete : this.autocomplete || 'new-password'
8570         };
8571         
8572         if(this.align){
8573             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8574         }
8575         
8576         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8577             input.maxLength = this.maxLength;
8578         }
8579         
8580         if (this.disabled) {
8581             input.disabled=true;
8582         }
8583         
8584         if (this.readOnly) {
8585             input.readonly=true;
8586         }
8587         
8588         if (this.name) {
8589             input.name = this.name;
8590         }
8591         
8592         if (this.size) {
8593             input.cls += ' input-' + this.size;
8594         }
8595         
8596         var settings=this;
8597         ['xs','sm','md','lg'].map(function(size){
8598             if (settings[size]) {
8599                 cfg.cls += ' col-' + size + '-' + settings[size];
8600             }
8601         });
8602         
8603         var inputblock = input;
8604         
8605         var feedback = {
8606             tag: 'span',
8607             cls: 'glyphicon form-control-feedback'
8608         };
8609             
8610         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8611             
8612             inputblock = {
8613                 cls : 'has-feedback',
8614                 cn :  [
8615                     input,
8616                     feedback
8617                 ] 
8618             };  
8619         }
8620         
8621         if (this.before || this.after) {
8622             
8623             inputblock = {
8624                 cls : 'input-group',
8625                 cn :  [] 
8626             };
8627             
8628             if (this.before && typeof(this.before) == 'string') {
8629                 
8630                 inputblock.cn.push({
8631                     tag :'span',
8632                     cls : 'roo-input-before input-group-addon',
8633                     html : this.before
8634                 });
8635             }
8636             if (this.before && typeof(this.before) == 'object') {
8637                 this.before = Roo.factory(this.before);
8638                 
8639                 inputblock.cn.push({
8640                     tag :'span',
8641                     cls : 'roo-input-before input-group-' +
8642                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8643                 });
8644             }
8645             
8646             inputblock.cn.push(input);
8647             
8648             if (this.after && typeof(this.after) == 'string') {
8649                 inputblock.cn.push({
8650                     tag :'span',
8651                     cls : 'roo-input-after input-group-addon',
8652                     html : this.after
8653                 });
8654             }
8655             if (this.after && typeof(this.after) == 'object') {
8656                 this.after = Roo.factory(this.after);
8657                 
8658                 inputblock.cn.push({
8659                     tag :'span',
8660                     cls : 'roo-input-after input-group-' +
8661                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8662                 });
8663             }
8664             
8665             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8666                 inputblock.cls += ' has-feedback';
8667                 inputblock.cn.push(feedback);
8668             }
8669         };
8670         
8671         if (align ==='left' && this.fieldLabel.length) {
8672             
8673             cfg.cls += ' roo-form-group-label-left';
8674             
8675             cfg.cn = [
8676                 {
8677                     tag : 'i',
8678                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8679                     tooltip : 'This field is required'
8680                 },
8681                 {
8682                     tag: 'label',
8683                     'for' :  id,
8684                     cls : 'control-label',
8685                     html : this.fieldLabel
8686
8687                 },
8688                 {
8689                     cls : "", 
8690                     cn: [
8691                         inputblock
8692                     ]
8693                 }
8694             ];
8695             
8696             var labelCfg = cfg.cn[1];
8697             var contentCfg = cfg.cn[2];
8698             
8699             if(this.indicatorpos == 'right'){
8700                 cfg.cn = [
8701                     {
8702                         tag: 'label',
8703                         'for' :  id,
8704                         cls : 'control-label',
8705                         html : this.fieldLabel
8706
8707                     },
8708                     {
8709                         tag : 'i',
8710                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8711                         tooltip : 'This field is required'
8712                     },
8713                     {
8714                         cls : "",
8715                         cn: [
8716                             inputblock
8717                         ]
8718                     }
8719
8720                 ];
8721                 
8722                 labelCfg = cfg.cn[0];
8723                 contentCfg = cfg.cn[2];
8724             
8725             }
8726             
8727             if(this.labelWidth > 12){
8728                 labelCfg.style = "width: " + this.labelWidth + 'px';
8729             }
8730             
8731             if(this.labelWidth < 13 && this.labelmd == 0){
8732                 this.labelmd = this.labelWidth;
8733             }
8734             
8735             if(this.labellg > 0){
8736                 labelCfg.cls += ' col-lg-' + this.labellg;
8737                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8738             }
8739             
8740             if(this.labelmd > 0){
8741                 labelCfg.cls += ' col-md-' + this.labelmd;
8742                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8743             }
8744             
8745             if(this.labelsm > 0){
8746                 labelCfg.cls += ' col-sm-' + this.labelsm;
8747                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8748             }
8749             
8750             if(this.labelxs > 0){
8751                 labelCfg.cls += ' col-xs-' + this.labelxs;
8752                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8753             }
8754             
8755             
8756         } else if ( this.fieldLabel.length) {
8757                 
8758             cfg.cn = [
8759                 {
8760                     tag : 'i',
8761                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8762                     tooltip : 'This field is required'
8763                 },
8764                 {
8765                     tag: 'label',
8766                    //cls : 'input-group-addon',
8767                     html : this.fieldLabel
8768
8769                 },
8770
8771                inputblock
8772
8773            ];
8774            
8775            if(this.indicatorpos == 'right'){
8776                 
8777                 cfg.cn = [
8778                     {
8779                         tag: 'label',
8780                        //cls : 'input-group-addon',
8781                         html : this.fieldLabel
8782
8783                     },
8784                     {
8785                         tag : 'i',
8786                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8787                         tooltip : 'This field is required'
8788                     },
8789
8790                    inputblock
8791
8792                ];
8793
8794             }
8795
8796         } else {
8797             
8798             cfg.cn = [
8799
8800                     inputblock
8801
8802             ];
8803                 
8804                 
8805         };
8806         
8807         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8808            cfg.cls += ' navbar-form';
8809         }
8810         
8811         if (this.parentType === 'NavGroup') {
8812            cfg.cls += ' navbar-form';
8813            cfg.tag = 'li';
8814         }
8815         
8816         return cfg;
8817         
8818     },
8819     /**
8820      * return the real input element.
8821      */
8822     inputEl: function ()
8823     {
8824         return this.el.select('input.form-control',true).first();
8825     },
8826     
8827     tooltipEl : function()
8828     {
8829         return this.inputEl();
8830     },
8831     
8832     indicatorEl : function()
8833     {
8834         var indicator = this.el.select('i.roo-required-indicator',true).first();
8835         
8836         if(!indicator){
8837             return false;
8838         }
8839         
8840         return indicator;
8841         
8842     },
8843     
8844     setDisabled : function(v)
8845     {
8846         var i  = this.inputEl().dom;
8847         if (!v) {
8848             i.removeAttribute('disabled');
8849             return;
8850             
8851         }
8852         i.setAttribute('disabled','true');
8853     },
8854     initEvents : function()
8855     {
8856           
8857         this.inputEl().on("keydown" , this.fireKey,  this);
8858         this.inputEl().on("focus", this.onFocus,  this);
8859         this.inputEl().on("blur", this.onBlur,  this);
8860         
8861         this.inputEl().relayEvent('keyup', this);
8862         
8863         this.indicator = this.indicatorEl();
8864         
8865         if(this.indicator){
8866             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8867             this.indicator.hide();
8868         }
8869  
8870         // reference to original value for reset
8871         this.originalValue = this.getValue();
8872         //Roo.form.TextField.superclass.initEvents.call(this);
8873         if(this.validationEvent == 'keyup'){
8874             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8875             this.inputEl().on('keyup', this.filterValidation, this);
8876         }
8877         else if(this.validationEvent !== false){
8878             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8879         }
8880         
8881         if(this.selectOnFocus){
8882             this.on("focus", this.preFocus, this);
8883             
8884         }
8885         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8886             this.inputEl().on("keypress", this.filterKeys, this);
8887         } else {
8888             this.inputEl().relayEvent('keypress', this);
8889         }
8890        /* if(this.grow){
8891             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8892             this.el.on("click", this.autoSize,  this);
8893         }
8894         */
8895         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8896             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8897         }
8898         
8899         if (typeof(this.before) == 'object') {
8900             this.before.render(this.el.select('.roo-input-before',true).first());
8901         }
8902         if (typeof(this.after) == 'object') {
8903             this.after.render(this.el.select('.roo-input-after',true).first());
8904         }
8905         
8906         
8907     },
8908     filterValidation : function(e){
8909         if(!e.isNavKeyPress()){
8910             this.validationTask.delay(this.validationDelay);
8911         }
8912     },
8913      /**
8914      * Validates the field value
8915      * @return {Boolean} True if the value is valid, else false
8916      */
8917     validate : function(){
8918         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8919         if(this.disabled || this.validateValue(this.getRawValue())){
8920             this.markValid();
8921             return true;
8922         }
8923         
8924         this.markInvalid();
8925         return false;
8926     },
8927     
8928     
8929     /**
8930      * Validates a value according to the field's validation rules and marks the field as invalid
8931      * if the validation fails
8932      * @param {Mixed} value The value to validate
8933      * @return {Boolean} True if the value is valid, else false
8934      */
8935     validateValue : function(value){
8936         if(value.length < 1)  { // if it's blank
8937             if(this.allowBlank){
8938                 return true;
8939             }
8940             return false;
8941         }
8942         
8943         if(value.length < this.minLength){
8944             return false;
8945         }
8946         if(value.length > this.maxLength){
8947             return false;
8948         }
8949         if(this.vtype){
8950             var vt = Roo.form.VTypes;
8951             if(!vt[this.vtype](value, this)){
8952                 return false;
8953             }
8954         }
8955         if(typeof this.validator == "function"){
8956             var msg = this.validator(value);
8957             if(msg !== true){
8958                 return false;
8959             }
8960         }
8961         
8962         if(this.regex && !this.regex.test(value)){
8963             return false;
8964         }
8965         
8966         return true;
8967     },
8968
8969     
8970     
8971      // private
8972     fireKey : function(e){
8973         //Roo.log('field ' + e.getKey());
8974         if(e.isNavKeyPress()){
8975             this.fireEvent("specialkey", this, e);
8976         }
8977     },
8978     focus : function (selectText){
8979         if(this.rendered){
8980             this.inputEl().focus();
8981             if(selectText === true){
8982                 this.inputEl().dom.select();
8983             }
8984         }
8985         return this;
8986     } ,
8987     
8988     onFocus : function(){
8989         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8990            // this.el.addClass(this.focusClass);
8991         }
8992         if(!this.hasFocus){
8993             this.hasFocus = true;
8994             this.startValue = this.getValue();
8995             this.fireEvent("focus", this);
8996         }
8997     },
8998     
8999     beforeBlur : Roo.emptyFn,
9000
9001     
9002     // private
9003     onBlur : function(){
9004         this.beforeBlur();
9005         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9006             //this.el.removeClass(this.focusClass);
9007         }
9008         this.hasFocus = false;
9009         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9010             this.validate();
9011         }
9012         var v = this.getValue();
9013         if(String(v) !== String(this.startValue)){
9014             this.fireEvent('change', this, v, this.startValue);
9015         }
9016         this.fireEvent("blur", this);
9017     },
9018     
9019     /**
9020      * Resets the current field value to the originally loaded value and clears any validation messages
9021      */
9022     reset : function(){
9023         this.setValue(this.originalValue);
9024         this.validate();
9025     },
9026      /**
9027      * Returns the name of the field
9028      * @return {Mixed} name The name field
9029      */
9030     getName: function(){
9031         return this.name;
9032     },
9033      /**
9034      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9035      * @return {Mixed} value The field value
9036      */
9037     getValue : function(){
9038         
9039         var v = this.inputEl().getValue();
9040         
9041         return v;
9042     },
9043     /**
9044      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9045      * @return {Mixed} value The field value
9046      */
9047     getRawValue : function(){
9048         var v = this.inputEl().getValue();
9049         
9050         return v;
9051     },
9052     
9053     /**
9054      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9055      * @param {Mixed} value The value to set
9056      */
9057     setRawValue : function(v){
9058         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9059     },
9060     
9061     selectText : function(start, end){
9062         var v = this.getRawValue();
9063         if(v.length > 0){
9064             start = start === undefined ? 0 : start;
9065             end = end === undefined ? v.length : end;
9066             var d = this.inputEl().dom;
9067             if(d.setSelectionRange){
9068                 d.setSelectionRange(start, end);
9069             }else if(d.createTextRange){
9070                 var range = d.createTextRange();
9071                 range.moveStart("character", start);
9072                 range.moveEnd("character", v.length-end);
9073                 range.select();
9074             }
9075         }
9076     },
9077     
9078     /**
9079      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9080      * @param {Mixed} value The value to set
9081      */
9082     setValue : function(v){
9083         this.value = v;
9084         if(this.rendered){
9085             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9086             this.validate();
9087         }
9088     },
9089     
9090     /*
9091     processValue : function(value){
9092         if(this.stripCharsRe){
9093             var newValue = value.replace(this.stripCharsRe, '');
9094             if(newValue !== value){
9095                 this.setRawValue(newValue);
9096                 return newValue;
9097             }
9098         }
9099         return value;
9100     },
9101   */
9102     preFocus : function(){
9103         
9104         if(this.selectOnFocus){
9105             this.inputEl().dom.select();
9106         }
9107     },
9108     filterKeys : function(e){
9109         var k = e.getKey();
9110         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9111             return;
9112         }
9113         var c = e.getCharCode(), cc = String.fromCharCode(c);
9114         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9115             return;
9116         }
9117         if(!this.maskRe.test(cc)){
9118             e.stopEvent();
9119         }
9120     },
9121      /**
9122      * Clear any invalid styles/messages for this field
9123      */
9124     clearInvalid : function(){
9125         
9126         if(!this.el || this.preventMark){ // not rendered
9127             return;
9128         }
9129         
9130      
9131         this.el.removeClass(this.invalidClass);
9132         
9133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9134             
9135             var feedback = this.el.select('.form-control-feedback', true).first();
9136             
9137             if(feedback){
9138                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9139             }
9140             
9141         }
9142         
9143         this.fireEvent('valid', this);
9144     },
9145     
9146      /**
9147      * Mark this field as valid
9148      */
9149     markValid : function()
9150     {
9151         if(!this.el  || this.preventMark){ // not rendered...
9152             return;
9153         }
9154         
9155         this.el.removeClass([this.invalidClass, this.validClass]);
9156         
9157         var feedback = this.el.select('.form-control-feedback', true).first();
9158             
9159         if(feedback){
9160             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9161         }
9162
9163         if(this.disabled){
9164             return;
9165         }
9166         
9167         if(this.allowBlank && !this.getRawValue().length){
9168             return;
9169         }
9170         
9171         if(this.indicator){
9172             this.indicator.hide();
9173         }
9174         
9175         this.el.addClass(this.validClass);
9176         
9177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9178             
9179             var feedback = this.el.select('.form-control-feedback', true).first();
9180             
9181             if(feedback){
9182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9183                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9184             }
9185             
9186         }
9187         
9188         this.fireEvent('valid', this);
9189     },
9190     
9191      /**
9192      * Mark this field as invalid
9193      * @param {String} msg The validation message
9194      */
9195     markInvalid : function(msg)
9196     {
9197         if(!this.el  || this.preventMark){ // not rendered
9198             return;
9199         }
9200         
9201         this.el.removeClass([this.invalidClass, this.validClass]);
9202         
9203         var feedback = this.el.select('.form-control-feedback', true).first();
9204             
9205         if(feedback){
9206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9207         }
9208
9209         if(this.disabled){
9210             return;
9211         }
9212         
9213         if(this.allowBlank && !this.getRawValue().length){
9214             return;
9215         }
9216         
9217         if(this.indicator){
9218             this.indicator.show();
9219         }
9220         
9221         this.el.addClass(this.invalidClass);
9222         
9223         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9224             
9225             var feedback = this.el.select('.form-control-feedback', true).first();
9226             
9227             if(feedback){
9228                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9229                 
9230                 if(this.getValue().length || this.forceFeedback){
9231                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9232                 }
9233                 
9234             }
9235             
9236         }
9237         
9238         this.fireEvent('invalid', this, msg);
9239     },
9240     // private
9241     SafariOnKeyDown : function(event)
9242     {
9243         // this is a workaround for a password hang bug on chrome/ webkit.
9244         if (this.inputEl().dom.type != 'password') {
9245             return;
9246         }
9247         
9248         var isSelectAll = false;
9249         
9250         if(this.inputEl().dom.selectionEnd > 0){
9251             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9252         }
9253         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9254             event.preventDefault();
9255             this.setValue('');
9256             return;
9257         }
9258         
9259         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9260             
9261             event.preventDefault();
9262             // this is very hacky as keydown always get's upper case.
9263             //
9264             var cc = String.fromCharCode(event.getCharCode());
9265             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9266             
9267         }
9268     },
9269     adjustWidth : function(tag, w){
9270         tag = tag.toLowerCase();
9271         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9272             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9273                 if(tag == 'input'){
9274                     return w + 2;
9275                 }
9276                 if(tag == 'textarea'){
9277                     return w-2;
9278                 }
9279             }else if(Roo.isOpera){
9280                 if(tag == 'input'){
9281                     return w + 2;
9282                 }
9283                 if(tag == 'textarea'){
9284                     return w-2;
9285                 }
9286             }
9287         }
9288         return w;
9289     }
9290     
9291 });
9292
9293  
9294 /*
9295  * - LGPL
9296  *
9297  * Input
9298  * 
9299  */
9300
9301 /**
9302  * @class Roo.bootstrap.TextArea
9303  * @extends Roo.bootstrap.Input
9304  * Bootstrap TextArea class
9305  * @cfg {Number} cols Specifies the visible width of a text area
9306  * @cfg {Number} rows Specifies the visible number of lines in a text area
9307  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9308  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9309  * @cfg {string} html text
9310  * 
9311  * @constructor
9312  * Create a new TextArea
9313  * @param {Object} config The config object
9314  */
9315
9316 Roo.bootstrap.TextArea = function(config){
9317     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9318    
9319 };
9320
9321 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9322      
9323     cols : false,
9324     rows : 5,
9325     readOnly : false,
9326     warp : 'soft',
9327     resize : false,
9328     value: false,
9329     html: false,
9330     
9331     getAutoCreate : function(){
9332         
9333         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9334         
9335         var id = Roo.id();
9336         
9337         var cfg = {};
9338         
9339         var input =  {
9340             tag: 'textarea',
9341             id : id,
9342             warp : this.warp,
9343             rows : this.rows,
9344             value : this.value || '',
9345             html: this.html || '',
9346             cls : 'form-control',
9347             placeholder : this.placeholder || '' 
9348             
9349         };
9350         
9351         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9352             input.maxLength = this.maxLength;
9353         }
9354         
9355         if(this.resize){
9356             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9357         }
9358         
9359         if(this.cols){
9360             input.cols = this.cols;
9361         }
9362         
9363         if (this.readOnly) {
9364             input.readonly = true;
9365         }
9366         
9367         if (this.name) {
9368             input.name = this.name;
9369         }
9370         
9371         if (this.size) {
9372             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9373         }
9374         
9375         var settings=this;
9376         ['xs','sm','md','lg'].map(function(size){
9377             if (settings[size]) {
9378                 cfg.cls += ' col-' + size + '-' + settings[size];
9379             }
9380         });
9381         
9382         var inputblock = input;
9383         
9384         if(this.hasFeedback && !this.allowBlank){
9385             
9386             var feedback = {
9387                 tag: 'span',
9388                 cls: 'glyphicon form-control-feedback'
9389             };
9390
9391             inputblock = {
9392                 cls : 'has-feedback',
9393                 cn :  [
9394                     input,
9395                     feedback
9396                 ] 
9397             };  
9398         }
9399         
9400         
9401         if (this.before || this.after) {
9402             
9403             inputblock = {
9404                 cls : 'input-group',
9405                 cn :  [] 
9406             };
9407             if (this.before) {
9408                 inputblock.cn.push({
9409                     tag :'span',
9410                     cls : 'input-group-addon',
9411                     html : this.before
9412                 });
9413             }
9414             
9415             inputblock.cn.push(input);
9416             
9417             if(this.hasFeedback && !this.allowBlank){
9418                 inputblock.cls += ' has-feedback';
9419                 inputblock.cn.push(feedback);
9420             }
9421             
9422             if (this.after) {
9423                 inputblock.cn.push({
9424                     tag :'span',
9425                     cls : 'input-group-addon',
9426                     html : this.after
9427                 });
9428             }
9429             
9430         }
9431         
9432         if (align ==='left' && this.fieldLabel.length) {
9433             cfg.cn = [
9434                 {
9435                     tag: 'label',
9436                     'for' :  id,
9437                     cls : 'control-label',
9438                     html : this.fieldLabel
9439                 },
9440                 {
9441                     cls : "",
9442                     cn: [
9443                         inputblock
9444                     ]
9445                 }
9446
9447             ];
9448             
9449             if(this.labelWidth > 12){
9450                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9451             }
9452
9453             if(this.labelWidth < 13 && this.labelmd == 0){
9454                 this.labelmd = this.labelWidth;
9455             }
9456
9457             if(this.labellg > 0){
9458                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9459                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9460             }
9461
9462             if(this.labelmd > 0){
9463                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9464                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9465             }
9466
9467             if(this.labelsm > 0){
9468                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9469                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9470             }
9471
9472             if(this.labelxs > 0){
9473                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9474                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9475             }
9476             
9477         } else if ( this.fieldLabel.length) {
9478             cfg.cn = [
9479
9480                {
9481                    tag: 'label',
9482                    //cls : 'input-group-addon',
9483                    html : this.fieldLabel
9484
9485                },
9486
9487                inputblock
9488
9489            ];
9490
9491         } else {
9492
9493             cfg.cn = [
9494
9495                 inputblock
9496
9497             ];
9498                 
9499         }
9500         
9501         if (this.disabled) {
9502             input.disabled=true;
9503         }
9504         
9505         return cfg;
9506         
9507     },
9508     /**
9509      * return the real textarea element.
9510      */
9511     inputEl: function ()
9512     {
9513         return this.el.select('textarea.form-control',true).first();
9514     },
9515     
9516     /**
9517      * Clear any invalid styles/messages for this field
9518      */
9519     clearInvalid : function()
9520     {
9521         
9522         if(!this.el || this.preventMark){ // not rendered
9523             return;
9524         }
9525         
9526         var label = this.el.select('label', true).first();
9527         var icon = this.el.select('i.fa-star', true).first();
9528         
9529         if(label && icon){
9530             icon.remove();
9531         }
9532         
9533         this.el.removeClass(this.invalidClass);
9534         
9535         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9536             
9537             var feedback = this.el.select('.form-control-feedback', true).first();
9538             
9539             if(feedback){
9540                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9541             }
9542             
9543         }
9544         
9545         this.fireEvent('valid', this);
9546     },
9547     
9548      /**
9549      * Mark this field as valid
9550      */
9551     markValid : function()
9552     {
9553         if(!this.el  || this.preventMark){ // not rendered
9554             return;
9555         }
9556         
9557         this.el.removeClass([this.invalidClass, this.validClass]);
9558         
9559         var feedback = this.el.select('.form-control-feedback', true).first();
9560             
9561         if(feedback){
9562             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9563         }
9564
9565         if(this.disabled || this.allowBlank){
9566             return;
9567         }
9568         
9569         var label = this.el.select('label', true).first();
9570         var icon = this.el.select('i.fa-star', true).first();
9571         
9572         if(label && icon){
9573             icon.remove();
9574         }
9575         
9576         this.el.addClass(this.validClass);
9577         
9578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9579             
9580             var feedback = this.el.select('.form-control-feedback', true).first();
9581             
9582             if(feedback){
9583                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9584                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9585             }
9586             
9587         }
9588         
9589         this.fireEvent('valid', this);
9590     },
9591     
9592      /**
9593      * Mark this field as invalid
9594      * @param {String} msg The validation message
9595      */
9596     markInvalid : function(msg)
9597     {
9598         if(!this.el  || this.preventMark){ // not rendered
9599             return;
9600         }
9601         
9602         this.el.removeClass([this.invalidClass, this.validClass]);
9603         
9604         var feedback = this.el.select('.form-control-feedback', true).first();
9605             
9606         if(feedback){
9607             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9608         }
9609
9610         if(this.disabled || this.allowBlank){
9611             return;
9612         }
9613         
9614         var label = this.el.select('label', true).first();
9615         var icon = this.el.select('i.fa-star', true).first();
9616         
9617         if(!this.getValue().length && label && !icon){
9618             this.el.createChild({
9619                 tag : 'i',
9620                 cls : 'text-danger fa fa-lg fa-star',
9621                 tooltip : 'This field is required',
9622                 style : 'margin-right:5px;'
9623             }, label, true);
9624         }
9625
9626         this.el.addClass(this.invalidClass);
9627         
9628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9629             
9630             var feedback = this.el.select('.form-control-feedback', true).first();
9631             
9632             if(feedback){
9633                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9634                 
9635                 if(this.getValue().length || this.forceFeedback){
9636                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9637                 }
9638                 
9639             }
9640             
9641         }
9642         
9643         this.fireEvent('invalid', this, msg);
9644     }
9645 });
9646
9647  
9648 /*
9649  * - LGPL
9650  *
9651  * trigger field - base class for combo..
9652  * 
9653  */
9654  
9655 /**
9656  * @class Roo.bootstrap.TriggerField
9657  * @extends Roo.bootstrap.Input
9658  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9659  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9660  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9661  * for which you can provide a custom implementation.  For example:
9662  * <pre><code>
9663 var trigger = new Roo.bootstrap.TriggerField();
9664 trigger.onTriggerClick = myTriggerFn;
9665 trigger.applyTo('my-field');
9666 </code></pre>
9667  *
9668  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9669  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9670  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9671  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9672  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9673
9674  * @constructor
9675  * Create a new TriggerField.
9676  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9677  * to the base TextField)
9678  */
9679 Roo.bootstrap.TriggerField = function(config){
9680     this.mimicing = false;
9681     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9682 };
9683
9684 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9685     /**
9686      * @cfg {String} triggerClass A CSS class to apply to the trigger
9687      */
9688      /**
9689      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9690      */
9691     hideTrigger:false,
9692
9693     /**
9694      * @cfg {Boolean} removable (true|false) special filter default false
9695      */
9696     removable : false,
9697     
9698     /** @cfg {Boolean} grow @hide */
9699     /** @cfg {Number} growMin @hide */
9700     /** @cfg {Number} growMax @hide */
9701
9702     /**
9703      * @hide 
9704      * @method
9705      */
9706     autoSize: Roo.emptyFn,
9707     // private
9708     monitorTab : true,
9709     // private
9710     deferHeight : true,
9711
9712     
9713     actionMode : 'wrap',
9714     
9715     caret : false,
9716     
9717     
9718     getAutoCreate : function(){
9719        
9720         var align = this.labelAlign || this.parentLabelAlign();
9721         
9722         var id = Roo.id();
9723         
9724         var cfg = {
9725             cls: 'form-group' //input-group
9726         };
9727         
9728         
9729         var input =  {
9730             tag: 'input',
9731             id : id,
9732             type : this.inputType,
9733             cls : 'form-control',
9734             autocomplete: 'new-password',
9735             placeholder : this.placeholder || '' 
9736             
9737         };
9738         if (this.name) {
9739             input.name = this.name;
9740         }
9741         if (this.size) {
9742             input.cls += ' input-' + this.size;
9743         }
9744         
9745         if (this.disabled) {
9746             input.disabled=true;
9747         }
9748         
9749         var inputblock = input;
9750         
9751         if(this.hasFeedback && !this.allowBlank){
9752             
9753             var feedback = {
9754                 tag: 'span',
9755                 cls: 'glyphicon form-control-feedback'
9756             };
9757             
9758             if(this.removable && !this.editable && !this.tickable){
9759                 inputblock = {
9760                     cls : 'has-feedback',
9761                     cn :  [
9762                         inputblock,
9763                         {
9764                             tag: 'button',
9765                             html : 'x',
9766                             cls : 'roo-combo-removable-btn close'
9767                         },
9768                         feedback
9769                     ] 
9770                 };
9771             } else {
9772                 inputblock = {
9773                     cls : 'has-feedback',
9774                     cn :  [
9775                         inputblock,
9776                         feedback
9777                     ] 
9778                 };
9779             }
9780
9781         } else {
9782             if(this.removable && !this.editable && !this.tickable){
9783                 inputblock = {
9784                     cls : 'roo-removable',
9785                     cn :  [
9786                         inputblock,
9787                         {
9788                             tag: 'button',
9789                             html : 'x',
9790                             cls : 'roo-combo-removable-btn close'
9791                         }
9792                     ] 
9793                 };
9794             }
9795         }
9796         
9797         if (this.before || this.after) {
9798             
9799             inputblock = {
9800                 cls : 'input-group',
9801                 cn :  [] 
9802             };
9803             if (this.before) {
9804                 inputblock.cn.push({
9805                     tag :'span',
9806                     cls : 'input-group-addon',
9807                     html : this.before
9808                 });
9809             }
9810             
9811             inputblock.cn.push(input);
9812             
9813             if(this.hasFeedback && !this.allowBlank){
9814                 inputblock.cls += ' has-feedback';
9815                 inputblock.cn.push(feedback);
9816             }
9817             
9818             if (this.after) {
9819                 inputblock.cn.push({
9820                     tag :'span',
9821                     cls : 'input-group-addon',
9822                     html : this.after
9823                 });
9824             }
9825             
9826         };
9827         
9828         var box = {
9829             tag: 'div',
9830             cn: [
9831                 {
9832                     tag: 'input',
9833                     type : 'hidden',
9834                     cls: 'form-hidden-field'
9835                 },
9836                 inputblock
9837             ]
9838             
9839         };
9840         
9841         if(this.multiple){
9842             box = {
9843                 tag: 'div',
9844                 cn: [
9845                     {
9846                         tag: 'input',
9847                         type : 'hidden',
9848                         cls: 'form-hidden-field'
9849                     },
9850                     {
9851                         tag: 'ul',
9852                         cls: 'roo-select2-choices',
9853                         cn:[
9854                             {
9855                                 tag: 'li',
9856                                 cls: 'roo-select2-search-field',
9857                                 cn: [
9858
9859                                     inputblock
9860                                 ]
9861                             }
9862                         ]
9863                     }
9864                 ]
9865             }
9866         };
9867         
9868         var combobox = {
9869             cls: 'roo-select2-container input-group',
9870             cn: [
9871                 box
9872 //                {
9873 //                    tag: 'ul',
9874 //                    cls: 'typeahead typeahead-long dropdown-menu',
9875 //                    style: 'display:none'
9876 //                }
9877             ]
9878         };
9879         
9880         if(!this.multiple && this.showToggleBtn){
9881             
9882             var caret = {
9883                         tag: 'span',
9884                         cls: 'caret'
9885              };
9886             if (this.caret != false) {
9887                 caret = {
9888                      tag: 'i',
9889                      cls: 'fa fa-' + this.caret
9890                 };
9891                 
9892             }
9893             
9894             combobox.cn.push({
9895                 tag :'span',
9896                 cls : 'input-group-addon btn dropdown-toggle',
9897                 cn : [
9898                     caret,
9899                     {
9900                         tag: 'span',
9901                         cls: 'combobox-clear',
9902                         cn  : [
9903                             {
9904                                 tag : 'i',
9905                                 cls: 'icon-remove'
9906                             }
9907                         ]
9908                     }
9909                 ]
9910
9911             })
9912         }
9913         
9914         if(this.multiple){
9915             combobox.cls += ' roo-select2-container-multi';
9916         }
9917         
9918         if (align ==='left' && this.fieldLabel.length) {
9919             
9920             cfg.cls += ' roo-form-group-label-left';
9921
9922             cfg.cn = [
9923                 {
9924                     tag : 'i',
9925                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9926                     tooltip : 'This field is required'
9927                 },
9928                 {
9929                     tag: 'label',
9930                     'for' :  id,
9931                     cls : 'control-label',
9932                     html : this.fieldLabel
9933
9934                 },
9935                 {
9936                     cls : "", 
9937                     cn: [
9938                         combobox
9939                     ]
9940                 }
9941
9942             ];
9943             
9944             var labelCfg = cfg.cn[1];
9945             var contentCfg = cfg.cn[2];
9946             
9947             if(this.indicatorpos == 'right'){
9948                 cfg.cn = [
9949                     {
9950                         tag: 'label',
9951                         'for' :  id,
9952                         cls : 'control-label',
9953                         cn : [
9954                             {
9955                                 tag : 'span',
9956                                 html : this.fieldLabel
9957                             },
9958                             {
9959                                 tag : 'i',
9960                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9961                                 tooltip : 'This field is required'
9962                             }
9963                         ]
9964                     },
9965                     {
9966                         cls : "", 
9967                         cn: [
9968                             combobox
9969                         ]
9970                     }
9971
9972                 ];
9973                 
9974                 labelCfg = cfg.cn[0];
9975                 contentCfg = cfg.cn[1];
9976             }
9977             
9978             if(this.labelWidth > 12){
9979                 labelCfg.style = "width: " + this.labelWidth + 'px';
9980             }
9981             
9982             if(this.labelWidth < 13 && this.labelmd == 0){
9983                 this.labelmd = this.labelWidth;
9984             }
9985             
9986             if(this.labellg > 0){
9987                 labelCfg.cls += ' col-lg-' + this.labellg;
9988                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9989             }
9990             
9991             if(this.labelmd > 0){
9992                 labelCfg.cls += ' col-md-' + this.labelmd;
9993                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9994             }
9995             
9996             if(this.labelsm > 0){
9997                 labelCfg.cls += ' col-sm-' + this.labelsm;
9998                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9999             }
10000             
10001             if(this.labelxs > 0){
10002                 labelCfg.cls += ' col-xs-' + this.labelxs;
10003                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10004             }
10005             
10006         } else if ( this.fieldLabel.length) {
10007 //                Roo.log(" label");
10008             cfg.cn = [
10009                 {
10010                    tag : 'i',
10011                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10012                    tooltip : 'This field is required'
10013                },
10014                {
10015                    tag: 'label',
10016                    //cls : 'input-group-addon',
10017                    html : this.fieldLabel
10018
10019                },
10020
10021                combobox
10022
10023             ];
10024             
10025             if(this.indicatorpos == 'right'){
10026                 
10027                 cfg.cn = [
10028                     {
10029                        tag: 'label',
10030                        cn : [
10031                            {
10032                                tag : 'span',
10033                                html : this.fieldLabel
10034                            },
10035                            {
10036                               tag : 'i',
10037                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10038                               tooltip : 'This field is required'
10039                            }
10040                        ]
10041
10042                     },
10043                     combobox
10044
10045                 ];
10046
10047             }
10048
10049         } else {
10050             
10051 //                Roo.log(" no label && no align");
10052                 cfg = combobox
10053                      
10054                 
10055         }
10056         
10057         var settings=this;
10058         ['xs','sm','md','lg'].map(function(size){
10059             if (settings[size]) {
10060                 cfg.cls += ' col-' + size + '-' + settings[size];
10061             }
10062         });
10063         
10064         return cfg;
10065         
10066     },
10067     
10068     
10069     
10070     // private
10071     onResize : function(w, h){
10072 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10073 //        if(typeof w == 'number'){
10074 //            var x = w - this.trigger.getWidth();
10075 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10076 //            this.trigger.setStyle('left', x+'px');
10077 //        }
10078     },
10079
10080     // private
10081     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10082
10083     // private
10084     getResizeEl : function(){
10085         return this.inputEl();
10086     },
10087
10088     // private
10089     getPositionEl : function(){
10090         return this.inputEl();
10091     },
10092
10093     // private
10094     alignErrorIcon : function(){
10095         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10096     },
10097
10098     // private
10099     initEvents : function(){
10100         
10101         this.createList();
10102         
10103         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10104         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10105         if(!this.multiple && this.showToggleBtn){
10106             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10107             if(this.hideTrigger){
10108                 this.trigger.setDisplayed(false);
10109             }
10110             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10111         }
10112         
10113         if(this.multiple){
10114             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10115         }
10116         
10117         if(this.removable && !this.editable && !this.tickable){
10118             var close = this.closeTriggerEl();
10119             
10120             if(close){
10121                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10122                 close.on('click', this.removeBtnClick, this, close);
10123             }
10124         }
10125         
10126         //this.trigger.addClassOnOver('x-form-trigger-over');
10127         //this.trigger.addClassOnClick('x-form-trigger-click');
10128         
10129         //if(!this.width){
10130         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10131         //}
10132     },
10133     
10134     closeTriggerEl : function()
10135     {
10136         var close = this.el.select('.roo-combo-removable-btn', true).first();
10137         return close ? close : false;
10138     },
10139     
10140     removeBtnClick : function(e, h, el)
10141     {
10142         e.preventDefault();
10143         
10144         if(this.fireEvent("remove", this) !== false){
10145             this.reset();
10146             this.fireEvent("afterremove", this)
10147         }
10148     },
10149     
10150     createList : function()
10151     {
10152         this.list = Roo.get(document.body).createChild({
10153             tag: 'ul',
10154             cls: 'typeahead typeahead-long dropdown-menu',
10155             style: 'display:none'
10156         });
10157         
10158         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10159         
10160     },
10161
10162     // private
10163     initTrigger : function(){
10164        
10165     },
10166
10167     // private
10168     onDestroy : function(){
10169         if(this.trigger){
10170             this.trigger.removeAllListeners();
10171           //  this.trigger.remove();
10172         }
10173         //if(this.wrap){
10174         //    this.wrap.remove();
10175         //}
10176         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10177     },
10178
10179     // private
10180     onFocus : function(){
10181         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10182         /*
10183         if(!this.mimicing){
10184             this.wrap.addClass('x-trigger-wrap-focus');
10185             this.mimicing = true;
10186             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10187             if(this.monitorTab){
10188                 this.el.on("keydown", this.checkTab, this);
10189             }
10190         }
10191         */
10192     },
10193
10194     // private
10195     checkTab : function(e){
10196         if(e.getKey() == e.TAB){
10197             this.triggerBlur();
10198         }
10199     },
10200
10201     // private
10202     onBlur : function(){
10203         // do nothing
10204     },
10205
10206     // private
10207     mimicBlur : function(e, t){
10208         /*
10209         if(!this.wrap.contains(t) && this.validateBlur()){
10210             this.triggerBlur();
10211         }
10212         */
10213     },
10214
10215     // private
10216     triggerBlur : function(){
10217         this.mimicing = false;
10218         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10219         if(this.monitorTab){
10220             this.el.un("keydown", this.checkTab, this);
10221         }
10222         //this.wrap.removeClass('x-trigger-wrap-focus');
10223         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10224     },
10225
10226     // private
10227     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10228     validateBlur : function(e, t){
10229         return true;
10230     },
10231
10232     // private
10233     onDisable : function(){
10234         this.inputEl().dom.disabled = true;
10235         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10236         //if(this.wrap){
10237         //    this.wrap.addClass('x-item-disabled');
10238         //}
10239     },
10240
10241     // private
10242     onEnable : function(){
10243         this.inputEl().dom.disabled = false;
10244         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10245         //if(this.wrap){
10246         //    this.el.removeClass('x-item-disabled');
10247         //}
10248     },
10249
10250     // private
10251     onShow : function(){
10252         var ae = this.getActionEl();
10253         
10254         if(ae){
10255             ae.dom.style.display = '';
10256             ae.dom.style.visibility = 'visible';
10257         }
10258     },
10259
10260     // private
10261     
10262     onHide : function(){
10263         var ae = this.getActionEl();
10264         ae.dom.style.display = 'none';
10265     },
10266
10267     /**
10268      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10269      * by an implementing function.
10270      * @method
10271      * @param {EventObject} e
10272      */
10273     onTriggerClick : Roo.emptyFn
10274 });
10275  /*
10276  * Based on:
10277  * Ext JS Library 1.1.1
10278  * Copyright(c) 2006-2007, Ext JS, LLC.
10279  *
10280  * Originally Released Under LGPL - original licence link has changed is not relivant.
10281  *
10282  * Fork - LGPL
10283  * <script type="text/javascript">
10284  */
10285
10286
10287 /**
10288  * @class Roo.data.SortTypes
10289  * @singleton
10290  * Defines the default sorting (casting?) comparison functions used when sorting data.
10291  */
10292 Roo.data.SortTypes = {
10293     /**
10294      * Default sort that does nothing
10295      * @param {Mixed} s The value being converted
10296      * @return {Mixed} The comparison value
10297      */
10298     none : function(s){
10299         return s;
10300     },
10301     
10302     /**
10303      * The regular expression used to strip tags
10304      * @type {RegExp}
10305      * @property
10306      */
10307     stripTagsRE : /<\/?[^>]+>/gi,
10308     
10309     /**
10310      * Strips all HTML tags to sort on text only
10311      * @param {Mixed} s The value being converted
10312      * @return {String} The comparison value
10313      */
10314     asText : function(s){
10315         return String(s).replace(this.stripTagsRE, "");
10316     },
10317     
10318     /**
10319      * Strips all HTML tags to sort on text only - Case insensitive
10320      * @param {Mixed} s The value being converted
10321      * @return {String} The comparison value
10322      */
10323     asUCText : function(s){
10324         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10325     },
10326     
10327     /**
10328      * Case insensitive string
10329      * @param {Mixed} s The value being converted
10330      * @return {String} The comparison value
10331      */
10332     asUCString : function(s) {
10333         return String(s).toUpperCase();
10334     },
10335     
10336     /**
10337      * Date sorting
10338      * @param {Mixed} s The value being converted
10339      * @return {Number} The comparison value
10340      */
10341     asDate : function(s) {
10342         if(!s){
10343             return 0;
10344         }
10345         if(s instanceof Date){
10346             return s.getTime();
10347         }
10348         return Date.parse(String(s));
10349     },
10350     
10351     /**
10352      * Float sorting
10353      * @param {Mixed} s The value being converted
10354      * @return {Float} The comparison value
10355      */
10356     asFloat : function(s) {
10357         var val = parseFloat(String(s).replace(/,/g, ""));
10358         if(isNaN(val)) {
10359             val = 0;
10360         }
10361         return val;
10362     },
10363     
10364     /**
10365      * Integer sorting
10366      * @param {Mixed} s The value being converted
10367      * @return {Number} The comparison value
10368      */
10369     asInt : function(s) {
10370         var val = parseInt(String(s).replace(/,/g, ""));
10371         if(isNaN(val)) {
10372             val = 0;
10373         }
10374         return val;
10375     }
10376 };/*
10377  * Based on:
10378  * Ext JS Library 1.1.1
10379  * Copyright(c) 2006-2007, Ext JS, LLC.
10380  *
10381  * Originally Released Under LGPL - original licence link has changed is not relivant.
10382  *
10383  * Fork - LGPL
10384  * <script type="text/javascript">
10385  */
10386
10387 /**
10388 * @class Roo.data.Record
10389  * Instances of this class encapsulate both record <em>definition</em> information, and record
10390  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10391  * to access Records cached in an {@link Roo.data.Store} object.<br>
10392  * <p>
10393  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10394  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10395  * objects.<br>
10396  * <p>
10397  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10398  * @constructor
10399  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10400  * {@link #create}. The parameters are the same.
10401  * @param {Array} data An associative Array of data values keyed by the field name.
10402  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10403  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10404  * not specified an integer id is generated.
10405  */
10406 Roo.data.Record = function(data, id){
10407     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10408     this.data = data;
10409 };
10410
10411 /**
10412  * Generate a constructor for a specific record layout.
10413  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10414  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10415  * Each field definition object may contain the following properties: <ul>
10416  * <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,
10417  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10418  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10419  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10420  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10421  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10422  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10423  * this may be omitted.</p></li>
10424  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10425  * <ul><li>auto (Default, implies no conversion)</li>
10426  * <li>string</li>
10427  * <li>int</li>
10428  * <li>float</li>
10429  * <li>boolean</li>
10430  * <li>date</li></ul></p></li>
10431  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10432  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10433  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10434  * by the Reader into an object that will be stored in the Record. It is passed the
10435  * following parameters:<ul>
10436  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10437  * </ul></p></li>
10438  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10439  * </ul>
10440  * <br>usage:<br><pre><code>
10441 var TopicRecord = Roo.data.Record.create(
10442     {name: 'title', mapping: 'topic_title'},
10443     {name: 'author', mapping: 'username'},
10444     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10445     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10446     {name: 'lastPoster', mapping: 'user2'},
10447     {name: 'excerpt', mapping: 'post_text'}
10448 );
10449
10450 var myNewRecord = new TopicRecord({
10451     title: 'Do my job please',
10452     author: 'noobie',
10453     totalPosts: 1,
10454     lastPost: new Date(),
10455     lastPoster: 'Animal',
10456     excerpt: 'No way dude!'
10457 });
10458 myStore.add(myNewRecord);
10459 </code></pre>
10460  * @method create
10461  * @static
10462  */
10463 Roo.data.Record.create = function(o){
10464     var f = function(){
10465         f.superclass.constructor.apply(this, arguments);
10466     };
10467     Roo.extend(f, Roo.data.Record);
10468     var p = f.prototype;
10469     p.fields = new Roo.util.MixedCollection(false, function(field){
10470         return field.name;
10471     });
10472     for(var i = 0, len = o.length; i < len; i++){
10473         p.fields.add(new Roo.data.Field(o[i]));
10474     }
10475     f.getField = function(name){
10476         return p.fields.get(name);  
10477     };
10478     return f;
10479 };
10480
10481 Roo.data.Record.AUTO_ID = 1000;
10482 Roo.data.Record.EDIT = 'edit';
10483 Roo.data.Record.REJECT = 'reject';
10484 Roo.data.Record.COMMIT = 'commit';
10485
10486 Roo.data.Record.prototype = {
10487     /**
10488      * Readonly flag - true if this record has been modified.
10489      * @type Boolean
10490      */
10491     dirty : false,
10492     editing : false,
10493     error: null,
10494     modified: null,
10495
10496     // private
10497     join : function(store){
10498         this.store = store;
10499     },
10500
10501     /**
10502      * Set the named field to the specified value.
10503      * @param {String} name The name of the field to set.
10504      * @param {Object} value The value to set the field to.
10505      */
10506     set : function(name, value){
10507         if(this.data[name] == value){
10508             return;
10509         }
10510         this.dirty = true;
10511         if(!this.modified){
10512             this.modified = {};
10513         }
10514         if(typeof this.modified[name] == 'undefined'){
10515             this.modified[name] = this.data[name];
10516         }
10517         this.data[name] = value;
10518         if(!this.editing && this.store){
10519             this.store.afterEdit(this);
10520         }       
10521     },
10522
10523     /**
10524      * Get the value of the named field.
10525      * @param {String} name The name of the field to get the value of.
10526      * @return {Object} The value of the field.
10527      */
10528     get : function(name){
10529         return this.data[name]; 
10530     },
10531
10532     // private
10533     beginEdit : function(){
10534         this.editing = true;
10535         this.modified = {}; 
10536     },
10537
10538     // private
10539     cancelEdit : function(){
10540         this.editing = false;
10541         delete this.modified;
10542     },
10543
10544     // private
10545     endEdit : function(){
10546         this.editing = false;
10547         if(this.dirty && this.store){
10548             this.store.afterEdit(this);
10549         }
10550     },
10551
10552     /**
10553      * Usually called by the {@link Roo.data.Store} which owns the Record.
10554      * Rejects all changes made to the Record since either creation, or the last commit operation.
10555      * Modified fields are reverted to their original values.
10556      * <p>
10557      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10558      * of reject operations.
10559      */
10560     reject : function(){
10561         var m = this.modified;
10562         for(var n in m){
10563             if(typeof m[n] != "function"){
10564                 this.data[n] = m[n];
10565             }
10566         }
10567         this.dirty = false;
10568         delete this.modified;
10569         this.editing = false;
10570         if(this.store){
10571             this.store.afterReject(this);
10572         }
10573     },
10574
10575     /**
10576      * Usually called by the {@link Roo.data.Store} which owns the Record.
10577      * Commits all changes made to the Record since either creation, or the last commit operation.
10578      * <p>
10579      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10580      * of commit operations.
10581      */
10582     commit : function(){
10583         this.dirty = false;
10584         delete this.modified;
10585         this.editing = false;
10586         if(this.store){
10587             this.store.afterCommit(this);
10588         }
10589     },
10590
10591     // private
10592     hasError : function(){
10593         return this.error != null;
10594     },
10595
10596     // private
10597     clearError : function(){
10598         this.error = null;
10599     },
10600
10601     /**
10602      * Creates a copy of this record.
10603      * @param {String} id (optional) A new record id if you don't want to use this record's id
10604      * @return {Record}
10605      */
10606     copy : function(newId) {
10607         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10608     }
10609 };/*
10610  * Based on:
10611  * Ext JS Library 1.1.1
10612  * Copyright(c) 2006-2007, Ext JS, LLC.
10613  *
10614  * Originally Released Under LGPL - original licence link has changed is not relivant.
10615  *
10616  * Fork - LGPL
10617  * <script type="text/javascript">
10618  */
10619
10620
10621
10622 /**
10623  * @class Roo.data.Store
10624  * @extends Roo.util.Observable
10625  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10626  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10627  * <p>
10628  * 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
10629  * has no knowledge of the format of the data returned by the Proxy.<br>
10630  * <p>
10631  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10632  * instances from the data object. These records are cached and made available through accessor functions.
10633  * @constructor
10634  * Creates a new Store.
10635  * @param {Object} config A config object containing the objects needed for the Store to access data,
10636  * and read the data into Records.
10637  */
10638 Roo.data.Store = function(config){
10639     this.data = new Roo.util.MixedCollection(false);
10640     this.data.getKey = function(o){
10641         return o.id;
10642     };
10643     this.baseParams = {};
10644     // private
10645     this.paramNames = {
10646         "start" : "start",
10647         "limit" : "limit",
10648         "sort" : "sort",
10649         "dir" : "dir",
10650         "multisort" : "_multisort"
10651     };
10652
10653     if(config && config.data){
10654         this.inlineData = config.data;
10655         delete config.data;
10656     }
10657
10658     Roo.apply(this, config);
10659     
10660     if(this.reader){ // reader passed
10661         this.reader = Roo.factory(this.reader, Roo.data);
10662         this.reader.xmodule = this.xmodule || false;
10663         if(!this.recordType){
10664             this.recordType = this.reader.recordType;
10665         }
10666         if(this.reader.onMetaChange){
10667             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10668         }
10669     }
10670
10671     if(this.recordType){
10672         this.fields = this.recordType.prototype.fields;
10673     }
10674     this.modified = [];
10675
10676     this.addEvents({
10677         /**
10678          * @event datachanged
10679          * Fires when the data cache has changed, and a widget which is using this Store
10680          * as a Record cache should refresh its view.
10681          * @param {Store} this
10682          */
10683         datachanged : true,
10684         /**
10685          * @event metachange
10686          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10687          * @param {Store} this
10688          * @param {Object} meta The JSON metadata
10689          */
10690         metachange : true,
10691         /**
10692          * @event add
10693          * Fires when Records have been added to the Store
10694          * @param {Store} this
10695          * @param {Roo.data.Record[]} records The array of Records added
10696          * @param {Number} index The index at which the record(s) were added
10697          */
10698         add : true,
10699         /**
10700          * @event remove
10701          * Fires when a Record has been removed from the Store
10702          * @param {Store} this
10703          * @param {Roo.data.Record} record The Record that was removed
10704          * @param {Number} index The index at which the record was removed
10705          */
10706         remove : true,
10707         /**
10708          * @event update
10709          * Fires when a Record has been updated
10710          * @param {Store} this
10711          * @param {Roo.data.Record} record The Record that was updated
10712          * @param {String} operation The update operation being performed.  Value may be one of:
10713          * <pre><code>
10714  Roo.data.Record.EDIT
10715  Roo.data.Record.REJECT
10716  Roo.data.Record.COMMIT
10717          * </code></pre>
10718          */
10719         update : true,
10720         /**
10721          * @event clear
10722          * Fires when the data cache has been cleared.
10723          * @param {Store} this
10724          */
10725         clear : true,
10726         /**
10727          * @event beforeload
10728          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10729          * the load action will be canceled.
10730          * @param {Store} this
10731          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10732          */
10733         beforeload : true,
10734         /**
10735          * @event beforeloadadd
10736          * Fires after a new set of Records has been loaded.
10737          * @param {Store} this
10738          * @param {Roo.data.Record[]} records The Records that were loaded
10739          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10740          */
10741         beforeloadadd : true,
10742         /**
10743          * @event load
10744          * Fires after a new set of Records has been loaded, before they are added to the store.
10745          * @param {Store} this
10746          * @param {Roo.data.Record[]} records The Records that were loaded
10747          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10748          * @params {Object} return from reader
10749          */
10750         load : true,
10751         /**
10752          * @event loadexception
10753          * Fires if an exception occurs in the Proxy during loading.
10754          * Called with the signature of the Proxy's "loadexception" event.
10755          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10756          * 
10757          * @param {Proxy} 
10758          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10759          * @param {Object} load options 
10760          * @param {Object} jsonData from your request (normally this contains the Exception)
10761          */
10762         loadexception : true
10763     });
10764     
10765     if(this.proxy){
10766         this.proxy = Roo.factory(this.proxy, Roo.data);
10767         this.proxy.xmodule = this.xmodule || false;
10768         this.relayEvents(this.proxy,  ["loadexception"]);
10769     }
10770     this.sortToggle = {};
10771     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10772
10773     Roo.data.Store.superclass.constructor.call(this);
10774
10775     if(this.inlineData){
10776         this.loadData(this.inlineData);
10777         delete this.inlineData;
10778     }
10779 };
10780
10781 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10782      /**
10783     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10784     * without a remote query - used by combo/forms at present.
10785     */
10786     
10787     /**
10788     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10789     */
10790     /**
10791     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10792     */
10793     /**
10794     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10795     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10796     */
10797     /**
10798     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10799     * on any HTTP request
10800     */
10801     /**
10802     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10803     */
10804     /**
10805     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10806     */
10807     multiSort: false,
10808     /**
10809     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10810     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10811     */
10812     remoteSort : false,
10813
10814     /**
10815     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10816      * loaded or when a record is removed. (defaults to false).
10817     */
10818     pruneModifiedRecords : false,
10819
10820     // private
10821     lastOptions : null,
10822
10823     /**
10824      * Add Records to the Store and fires the add event.
10825      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10826      */
10827     add : function(records){
10828         records = [].concat(records);
10829         for(var i = 0, len = records.length; i < len; i++){
10830             records[i].join(this);
10831         }
10832         var index = this.data.length;
10833         this.data.addAll(records);
10834         this.fireEvent("add", this, records, index);
10835     },
10836
10837     /**
10838      * Remove a Record from the Store and fires the remove event.
10839      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10840      */
10841     remove : function(record){
10842         var index = this.data.indexOf(record);
10843         this.data.removeAt(index);
10844         if(this.pruneModifiedRecords){
10845             this.modified.remove(record);
10846         }
10847         this.fireEvent("remove", this, record, index);
10848     },
10849
10850     /**
10851      * Remove all Records from the Store and fires the clear event.
10852      */
10853     removeAll : function(){
10854         this.data.clear();
10855         if(this.pruneModifiedRecords){
10856             this.modified = [];
10857         }
10858         this.fireEvent("clear", this);
10859     },
10860
10861     /**
10862      * Inserts Records to the Store at the given index and fires the add event.
10863      * @param {Number} index The start index at which to insert the passed Records.
10864      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10865      */
10866     insert : function(index, records){
10867         records = [].concat(records);
10868         for(var i = 0, len = records.length; i < len; i++){
10869             this.data.insert(index, records[i]);
10870             records[i].join(this);
10871         }
10872         this.fireEvent("add", this, records, index);
10873     },
10874
10875     /**
10876      * Get the index within the cache of the passed Record.
10877      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10878      * @return {Number} The index of the passed Record. Returns -1 if not found.
10879      */
10880     indexOf : function(record){
10881         return this.data.indexOf(record);
10882     },
10883
10884     /**
10885      * Get the index within the cache of the Record with the passed id.
10886      * @param {String} id The id of the Record to find.
10887      * @return {Number} The index of the Record. Returns -1 if not found.
10888      */
10889     indexOfId : function(id){
10890         return this.data.indexOfKey(id);
10891     },
10892
10893     /**
10894      * Get the Record with the specified id.
10895      * @param {String} id The id of the Record to find.
10896      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10897      */
10898     getById : function(id){
10899         return this.data.key(id);
10900     },
10901
10902     /**
10903      * Get the Record at the specified index.
10904      * @param {Number} index The index of the Record to find.
10905      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10906      */
10907     getAt : function(index){
10908         return this.data.itemAt(index);
10909     },
10910
10911     /**
10912      * Returns a range of Records between specified indices.
10913      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10914      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10915      * @return {Roo.data.Record[]} An array of Records
10916      */
10917     getRange : function(start, end){
10918         return this.data.getRange(start, end);
10919     },
10920
10921     // private
10922     storeOptions : function(o){
10923         o = Roo.apply({}, o);
10924         delete o.callback;
10925         delete o.scope;
10926         this.lastOptions = o;
10927     },
10928
10929     /**
10930      * Loads the Record cache from the configured Proxy using the configured Reader.
10931      * <p>
10932      * If using remote paging, then the first load call must specify the <em>start</em>
10933      * and <em>limit</em> properties in the options.params property to establish the initial
10934      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10935      * <p>
10936      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10937      * and this call will return before the new data has been loaded. Perform any post-processing
10938      * in a callback function, or in a "load" event handler.</strong>
10939      * <p>
10940      * @param {Object} options An object containing properties which control loading options:<ul>
10941      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10942      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10943      * passed the following arguments:<ul>
10944      * <li>r : Roo.data.Record[]</li>
10945      * <li>options: Options object from the load call</li>
10946      * <li>success: Boolean success indicator</li></ul></li>
10947      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10948      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10949      * </ul>
10950      */
10951     load : function(options){
10952         options = options || {};
10953         if(this.fireEvent("beforeload", this, options) !== false){
10954             this.storeOptions(options);
10955             var p = Roo.apply(options.params || {}, this.baseParams);
10956             // if meta was not loaded from remote source.. try requesting it.
10957             if (!this.reader.metaFromRemote) {
10958                 p._requestMeta = 1;
10959             }
10960             if(this.sortInfo && this.remoteSort){
10961                 var pn = this.paramNames;
10962                 p[pn["sort"]] = this.sortInfo.field;
10963                 p[pn["dir"]] = this.sortInfo.direction;
10964             }
10965             if (this.multiSort) {
10966                 var pn = this.paramNames;
10967                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10968             }
10969             
10970             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10971         }
10972     },
10973
10974     /**
10975      * Reloads the Record cache from the configured Proxy using the configured Reader and
10976      * the options from the last load operation performed.
10977      * @param {Object} options (optional) An object containing properties which may override the options
10978      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10979      * the most recently used options are reused).
10980      */
10981     reload : function(options){
10982         this.load(Roo.applyIf(options||{}, this.lastOptions));
10983     },
10984
10985     // private
10986     // Called as a callback by the Reader during a load operation.
10987     loadRecords : function(o, options, success){
10988         if(!o || success === false){
10989             if(success !== false){
10990                 this.fireEvent("load", this, [], options, o);
10991             }
10992             if(options.callback){
10993                 options.callback.call(options.scope || this, [], options, false);
10994             }
10995             return;
10996         }
10997         // if data returned failure - throw an exception.
10998         if (o.success === false) {
10999             // show a message if no listener is registered.
11000             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11001                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11002             }
11003             // loadmask wil be hooked into this..
11004             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11005             return;
11006         }
11007         var r = o.records, t = o.totalRecords || r.length;
11008         
11009         this.fireEvent("beforeloadadd", this, r, options, o);
11010         
11011         if(!options || options.add !== true){
11012             if(this.pruneModifiedRecords){
11013                 this.modified = [];
11014             }
11015             for(var i = 0, len = r.length; i < len; i++){
11016                 r[i].join(this);
11017             }
11018             if(this.snapshot){
11019                 this.data = this.snapshot;
11020                 delete this.snapshot;
11021             }
11022             this.data.clear();
11023             this.data.addAll(r);
11024             this.totalLength = t;
11025             this.applySort();
11026             this.fireEvent("datachanged", this);
11027         }else{
11028             this.totalLength = Math.max(t, this.data.length+r.length);
11029             this.add(r);
11030         }
11031         
11032         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11033                 
11034             var e = new Roo.data.Record({});
11035
11036             e.set(this.parent.displayField, this.parent.emptyTitle);
11037             e.set(this.parent.valueField, '');
11038
11039             this.insert(0, e);
11040         }
11041             
11042         this.fireEvent("load", this, r, options, o);
11043         if(options.callback){
11044             options.callback.call(options.scope || this, r, options, true);
11045         }
11046     },
11047
11048
11049     /**
11050      * Loads data from a passed data block. A Reader which understands the format of the data
11051      * must have been configured in the constructor.
11052      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11053      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11054      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11055      */
11056     loadData : function(o, append){
11057         var r = this.reader.readRecords(o);
11058         this.loadRecords(r, {add: append}, true);
11059     },
11060
11061     /**
11062      * Gets the number of cached records.
11063      * <p>
11064      * <em>If using paging, this may not be the total size of the dataset. If the data object
11065      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11066      * the data set size</em>
11067      */
11068     getCount : function(){
11069         return this.data.length || 0;
11070     },
11071
11072     /**
11073      * Gets the total number of records in the dataset as returned by the server.
11074      * <p>
11075      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11076      * the dataset size</em>
11077      */
11078     getTotalCount : function(){
11079         return this.totalLength || 0;
11080     },
11081
11082     /**
11083      * Returns the sort state of the Store as an object with two properties:
11084      * <pre><code>
11085  field {String} The name of the field by which the Records are sorted
11086  direction {String} The sort order, "ASC" or "DESC"
11087      * </code></pre>
11088      */
11089     getSortState : function(){
11090         return this.sortInfo;
11091     },
11092
11093     // private
11094     applySort : function(){
11095         if(this.sortInfo && !this.remoteSort){
11096             var s = this.sortInfo, f = s.field;
11097             var st = this.fields.get(f).sortType;
11098             var fn = function(r1, r2){
11099                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11100                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11101             };
11102             this.data.sort(s.direction, fn);
11103             if(this.snapshot && this.snapshot != this.data){
11104                 this.snapshot.sort(s.direction, fn);
11105             }
11106         }
11107     },
11108
11109     /**
11110      * Sets the default sort column and order to be used by the next load operation.
11111      * @param {String} fieldName The name of the field to sort by.
11112      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11113      */
11114     setDefaultSort : function(field, dir){
11115         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11116     },
11117
11118     /**
11119      * Sort the Records.
11120      * If remote sorting is used, the sort is performed on the server, and the cache is
11121      * reloaded. If local sorting is used, the cache is sorted internally.
11122      * @param {String} fieldName The name of the field to sort by.
11123      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11124      */
11125     sort : function(fieldName, dir){
11126         var f = this.fields.get(fieldName);
11127         if(!dir){
11128             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11129             
11130             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11131                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11132             }else{
11133                 dir = f.sortDir;
11134             }
11135         }
11136         this.sortToggle[f.name] = dir;
11137         this.sortInfo = {field: f.name, direction: dir};
11138         if(!this.remoteSort){
11139             this.applySort();
11140             this.fireEvent("datachanged", this);
11141         }else{
11142             this.load(this.lastOptions);
11143         }
11144     },
11145
11146     /**
11147      * Calls the specified function for each of the Records in the cache.
11148      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11149      * Returning <em>false</em> aborts and exits the iteration.
11150      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11151      */
11152     each : function(fn, scope){
11153         this.data.each(fn, scope);
11154     },
11155
11156     /**
11157      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11158      * (e.g., during paging).
11159      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11160      */
11161     getModifiedRecords : function(){
11162         return this.modified;
11163     },
11164
11165     // private
11166     createFilterFn : function(property, value, anyMatch){
11167         if(!value.exec){ // not a regex
11168             value = String(value);
11169             if(value.length == 0){
11170                 return false;
11171             }
11172             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11173         }
11174         return function(r){
11175             return value.test(r.data[property]);
11176         };
11177     },
11178
11179     /**
11180      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11181      * @param {String} property A field on your records
11182      * @param {Number} start The record index to start at (defaults to 0)
11183      * @param {Number} end The last record index to include (defaults to length - 1)
11184      * @return {Number} The sum
11185      */
11186     sum : function(property, start, end){
11187         var rs = this.data.items, v = 0;
11188         start = start || 0;
11189         end = (end || end === 0) ? end : rs.length-1;
11190
11191         for(var i = start; i <= end; i++){
11192             v += (rs[i].data[property] || 0);
11193         }
11194         return v;
11195     },
11196
11197     /**
11198      * Filter the records by a specified property.
11199      * @param {String} field A field on your records
11200      * @param {String/RegExp} value Either a string that the field
11201      * should start with or a RegExp to test against the field
11202      * @param {Boolean} anyMatch True to match any part not just the beginning
11203      */
11204     filter : function(property, value, anyMatch){
11205         var fn = this.createFilterFn(property, value, anyMatch);
11206         return fn ? this.filterBy(fn) : this.clearFilter();
11207     },
11208
11209     /**
11210      * Filter by a function. The specified function will be called with each
11211      * record in this data source. If the function returns true the record is included,
11212      * otherwise it is filtered.
11213      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11214      * @param {Object} scope (optional) The scope of the function (defaults to this)
11215      */
11216     filterBy : function(fn, scope){
11217         this.snapshot = this.snapshot || this.data;
11218         this.data = this.queryBy(fn, scope||this);
11219         this.fireEvent("datachanged", this);
11220     },
11221
11222     /**
11223      * Query the records by a specified property.
11224      * @param {String} field A field on your records
11225      * @param {String/RegExp} value Either a string that the field
11226      * should start with or a RegExp to test against the field
11227      * @param {Boolean} anyMatch True to match any part not just the beginning
11228      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11229      */
11230     query : function(property, value, anyMatch){
11231         var fn = this.createFilterFn(property, value, anyMatch);
11232         return fn ? this.queryBy(fn) : this.data.clone();
11233     },
11234
11235     /**
11236      * Query by a function. The specified function will be called with each
11237      * record in this data source. If the function returns true the record is included
11238      * in the results.
11239      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11240      * @param {Object} scope (optional) The scope of the function (defaults to this)
11241       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11242      **/
11243     queryBy : function(fn, scope){
11244         var data = this.snapshot || this.data;
11245         return data.filterBy(fn, scope||this);
11246     },
11247
11248     /**
11249      * Collects unique values for a particular dataIndex from this store.
11250      * @param {String} dataIndex The property to collect
11251      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11252      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11253      * @return {Array} An array of the unique values
11254      **/
11255     collect : function(dataIndex, allowNull, bypassFilter){
11256         var d = (bypassFilter === true && this.snapshot) ?
11257                 this.snapshot.items : this.data.items;
11258         var v, sv, r = [], l = {};
11259         for(var i = 0, len = d.length; i < len; i++){
11260             v = d[i].data[dataIndex];
11261             sv = String(v);
11262             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11263                 l[sv] = true;
11264                 r[r.length] = v;
11265             }
11266         }
11267         return r;
11268     },
11269
11270     /**
11271      * Revert to a view of the Record cache with no filtering applied.
11272      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11273      */
11274     clearFilter : function(suppressEvent){
11275         if(this.snapshot && this.snapshot != this.data){
11276             this.data = this.snapshot;
11277             delete this.snapshot;
11278             if(suppressEvent !== true){
11279                 this.fireEvent("datachanged", this);
11280             }
11281         }
11282     },
11283
11284     // private
11285     afterEdit : function(record){
11286         if(this.modified.indexOf(record) == -1){
11287             this.modified.push(record);
11288         }
11289         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11290     },
11291     
11292     // private
11293     afterReject : function(record){
11294         this.modified.remove(record);
11295         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11296     },
11297
11298     // private
11299     afterCommit : function(record){
11300         this.modified.remove(record);
11301         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11302     },
11303
11304     /**
11305      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11306      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11307      */
11308     commitChanges : function(){
11309         var m = this.modified.slice(0);
11310         this.modified = [];
11311         for(var i = 0, len = m.length; i < len; i++){
11312             m[i].commit();
11313         }
11314     },
11315
11316     /**
11317      * Cancel outstanding changes on all changed records.
11318      */
11319     rejectChanges : function(){
11320         var m = this.modified.slice(0);
11321         this.modified = [];
11322         for(var i = 0, len = m.length; i < len; i++){
11323             m[i].reject();
11324         }
11325     },
11326
11327     onMetaChange : function(meta, rtype, o){
11328         this.recordType = rtype;
11329         this.fields = rtype.prototype.fields;
11330         delete this.snapshot;
11331         this.sortInfo = meta.sortInfo || this.sortInfo;
11332         this.modified = [];
11333         this.fireEvent('metachange', this, this.reader.meta);
11334     },
11335     
11336     moveIndex : function(data, type)
11337     {
11338         var index = this.indexOf(data);
11339         
11340         var newIndex = index + type;
11341         
11342         this.remove(data);
11343         
11344         this.insert(newIndex, data);
11345         
11346     }
11347 });/*
11348  * Based on:
11349  * Ext JS Library 1.1.1
11350  * Copyright(c) 2006-2007, Ext JS, LLC.
11351  *
11352  * Originally Released Under LGPL - original licence link has changed is not relivant.
11353  *
11354  * Fork - LGPL
11355  * <script type="text/javascript">
11356  */
11357
11358 /**
11359  * @class Roo.data.SimpleStore
11360  * @extends Roo.data.Store
11361  * Small helper class to make creating Stores from Array data easier.
11362  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11363  * @cfg {Array} fields An array of field definition objects, or field name strings.
11364  * @cfg {Array} data The multi-dimensional array of data
11365  * @constructor
11366  * @param {Object} config
11367  */
11368 Roo.data.SimpleStore = function(config){
11369     Roo.data.SimpleStore.superclass.constructor.call(this, {
11370         isLocal : true,
11371         reader: new Roo.data.ArrayReader({
11372                 id: config.id
11373             },
11374             Roo.data.Record.create(config.fields)
11375         ),
11376         proxy : new Roo.data.MemoryProxy(config.data)
11377     });
11378     this.load();
11379 };
11380 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11381  * Based on:
11382  * Ext JS Library 1.1.1
11383  * Copyright(c) 2006-2007, Ext JS, LLC.
11384  *
11385  * Originally Released Under LGPL - original licence link has changed is not relivant.
11386  *
11387  * Fork - LGPL
11388  * <script type="text/javascript">
11389  */
11390
11391 /**
11392 /**
11393  * @extends Roo.data.Store
11394  * @class Roo.data.JsonStore
11395  * Small helper class to make creating Stores for JSON data easier. <br/>
11396 <pre><code>
11397 var store = new Roo.data.JsonStore({
11398     url: 'get-images.php',
11399     root: 'images',
11400     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11401 });
11402 </code></pre>
11403  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11404  * JsonReader and HttpProxy (unless inline data is provided).</b>
11405  * @cfg {Array} fields An array of field definition objects, or field name strings.
11406  * @constructor
11407  * @param {Object} config
11408  */
11409 Roo.data.JsonStore = function(c){
11410     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11411         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11412         reader: new Roo.data.JsonReader(c, c.fields)
11413     }));
11414 };
11415 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11416  * Based on:
11417  * Ext JS Library 1.1.1
11418  * Copyright(c) 2006-2007, Ext JS, LLC.
11419  *
11420  * Originally Released Under LGPL - original licence link has changed is not relivant.
11421  *
11422  * Fork - LGPL
11423  * <script type="text/javascript">
11424  */
11425
11426  
11427 Roo.data.Field = function(config){
11428     if(typeof config == "string"){
11429         config = {name: config};
11430     }
11431     Roo.apply(this, config);
11432     
11433     if(!this.type){
11434         this.type = "auto";
11435     }
11436     
11437     var st = Roo.data.SortTypes;
11438     // named sortTypes are supported, here we look them up
11439     if(typeof this.sortType == "string"){
11440         this.sortType = st[this.sortType];
11441     }
11442     
11443     // set default sortType for strings and dates
11444     if(!this.sortType){
11445         switch(this.type){
11446             case "string":
11447                 this.sortType = st.asUCString;
11448                 break;
11449             case "date":
11450                 this.sortType = st.asDate;
11451                 break;
11452             default:
11453                 this.sortType = st.none;
11454         }
11455     }
11456
11457     // define once
11458     var stripRe = /[\$,%]/g;
11459
11460     // prebuilt conversion function for this field, instead of
11461     // switching every time we're reading a value
11462     if(!this.convert){
11463         var cv, dateFormat = this.dateFormat;
11464         switch(this.type){
11465             case "":
11466             case "auto":
11467             case undefined:
11468                 cv = function(v){ return v; };
11469                 break;
11470             case "string":
11471                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11472                 break;
11473             case "int":
11474                 cv = function(v){
11475                     return v !== undefined && v !== null && v !== '' ?
11476                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11477                     };
11478                 break;
11479             case "float":
11480                 cv = function(v){
11481                     return v !== undefined && v !== null && v !== '' ?
11482                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11483                     };
11484                 break;
11485             case "bool":
11486             case "boolean":
11487                 cv = function(v){ return v === true || v === "true" || v == 1; };
11488                 break;
11489             case "date":
11490                 cv = function(v){
11491                     if(!v){
11492                         return '';
11493                     }
11494                     if(v instanceof Date){
11495                         return v;
11496                     }
11497                     if(dateFormat){
11498                         if(dateFormat == "timestamp"){
11499                             return new Date(v*1000);
11500                         }
11501                         return Date.parseDate(v, dateFormat);
11502                     }
11503                     var parsed = Date.parse(v);
11504                     return parsed ? new Date(parsed) : null;
11505                 };
11506              break;
11507             
11508         }
11509         this.convert = cv;
11510     }
11511 };
11512
11513 Roo.data.Field.prototype = {
11514     dateFormat: null,
11515     defaultValue: "",
11516     mapping: null,
11517     sortType : null,
11518     sortDir : "ASC"
11519 };/*
11520  * Based on:
11521  * Ext JS Library 1.1.1
11522  * Copyright(c) 2006-2007, Ext JS, LLC.
11523  *
11524  * Originally Released Under LGPL - original licence link has changed is not relivant.
11525  *
11526  * Fork - LGPL
11527  * <script type="text/javascript">
11528  */
11529  
11530 // Base class for reading structured data from a data source.  This class is intended to be
11531 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11532
11533 /**
11534  * @class Roo.data.DataReader
11535  * Base class for reading structured data from a data source.  This class is intended to be
11536  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11537  */
11538
11539 Roo.data.DataReader = function(meta, recordType){
11540     
11541     this.meta = meta;
11542     
11543     this.recordType = recordType instanceof Array ? 
11544         Roo.data.Record.create(recordType) : recordType;
11545 };
11546
11547 Roo.data.DataReader.prototype = {
11548      /**
11549      * Create an empty record
11550      * @param {Object} data (optional) - overlay some values
11551      * @return {Roo.data.Record} record created.
11552      */
11553     newRow :  function(d) {
11554         var da =  {};
11555         this.recordType.prototype.fields.each(function(c) {
11556             switch( c.type) {
11557                 case 'int' : da[c.name] = 0; break;
11558                 case 'date' : da[c.name] = new Date(); break;
11559                 case 'float' : da[c.name] = 0.0; break;
11560                 case 'boolean' : da[c.name] = false; break;
11561                 default : da[c.name] = ""; break;
11562             }
11563             
11564         });
11565         return new this.recordType(Roo.apply(da, d));
11566     }
11567     
11568 };/*
11569  * Based on:
11570  * Ext JS Library 1.1.1
11571  * Copyright(c) 2006-2007, Ext JS, LLC.
11572  *
11573  * Originally Released Under LGPL - original licence link has changed is not relivant.
11574  *
11575  * Fork - LGPL
11576  * <script type="text/javascript">
11577  */
11578
11579 /**
11580  * @class Roo.data.DataProxy
11581  * @extends Roo.data.Observable
11582  * This class is an abstract base class for implementations which provide retrieval of
11583  * unformatted data objects.<br>
11584  * <p>
11585  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11586  * (of the appropriate type which knows how to parse the data object) to provide a block of
11587  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11588  * <p>
11589  * Custom implementations must implement the load method as described in
11590  * {@link Roo.data.HttpProxy#load}.
11591  */
11592 Roo.data.DataProxy = function(){
11593     this.addEvents({
11594         /**
11595          * @event beforeload
11596          * Fires before a network request is made to retrieve a data object.
11597          * @param {Object} This DataProxy object.
11598          * @param {Object} params The params parameter to the load function.
11599          */
11600         beforeload : true,
11601         /**
11602          * @event load
11603          * Fires before the load method's callback is called.
11604          * @param {Object} This DataProxy object.
11605          * @param {Object} o The data object.
11606          * @param {Object} arg The callback argument object passed to the load function.
11607          */
11608         load : true,
11609         /**
11610          * @event loadexception
11611          * Fires if an Exception occurs during data retrieval.
11612          * @param {Object} This DataProxy object.
11613          * @param {Object} o The data object.
11614          * @param {Object} arg The callback argument object passed to the load function.
11615          * @param {Object} e The Exception.
11616          */
11617         loadexception : true
11618     });
11619     Roo.data.DataProxy.superclass.constructor.call(this);
11620 };
11621
11622 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11623
11624     /**
11625      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11626      */
11627 /*
11628  * Based on:
11629  * Ext JS Library 1.1.1
11630  * Copyright(c) 2006-2007, Ext JS, LLC.
11631  *
11632  * Originally Released Under LGPL - original licence link has changed is not relivant.
11633  *
11634  * Fork - LGPL
11635  * <script type="text/javascript">
11636  */
11637 /**
11638  * @class Roo.data.MemoryProxy
11639  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11640  * to the Reader when its load method is called.
11641  * @constructor
11642  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11643  */
11644 Roo.data.MemoryProxy = function(data){
11645     if (data.data) {
11646         data = data.data;
11647     }
11648     Roo.data.MemoryProxy.superclass.constructor.call(this);
11649     this.data = data;
11650 };
11651
11652 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11653     
11654     /**
11655      * Load data from the requested source (in this case an in-memory
11656      * data object passed to the constructor), read the data object into
11657      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11658      * process that block using the passed callback.
11659      * @param {Object} params This parameter is not used by the MemoryProxy class.
11660      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11661      * object into a block of Roo.data.Records.
11662      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11663      * The function must be passed <ul>
11664      * <li>The Record block object</li>
11665      * <li>The "arg" argument from the load function</li>
11666      * <li>A boolean success indicator</li>
11667      * </ul>
11668      * @param {Object} scope The scope in which to call the callback
11669      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11670      */
11671     load : function(params, reader, callback, scope, arg){
11672         params = params || {};
11673         var result;
11674         try {
11675             result = reader.readRecords(this.data);
11676         }catch(e){
11677             this.fireEvent("loadexception", this, arg, null, e);
11678             callback.call(scope, null, arg, false);
11679             return;
11680         }
11681         callback.call(scope, result, arg, true);
11682     },
11683     
11684     // private
11685     update : function(params, records){
11686         
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698 /**
11699  * @class Roo.data.HttpProxy
11700  * @extends Roo.data.DataProxy
11701  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11702  * configured to reference a certain URL.<br><br>
11703  * <p>
11704  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11705  * from which the running page was served.<br><br>
11706  * <p>
11707  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11708  * <p>
11709  * Be aware that to enable the browser to parse an XML document, the server must set
11710  * the Content-Type header in the HTTP response to "text/xml".
11711  * @constructor
11712  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11713  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11714  * will be used to make the request.
11715  */
11716 Roo.data.HttpProxy = function(conn){
11717     Roo.data.HttpProxy.superclass.constructor.call(this);
11718     // is conn a conn config or a real conn?
11719     this.conn = conn;
11720     this.useAjax = !conn || !conn.events;
11721   
11722 };
11723
11724 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11725     // thse are take from connection...
11726     
11727     /**
11728      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11729      */
11730     /**
11731      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11732      * extra parameters to each request made by this object. (defaults to undefined)
11733      */
11734     /**
11735      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11736      *  to each request made by this object. (defaults to undefined)
11737      */
11738     /**
11739      * @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)
11740      */
11741     /**
11742      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11743      */
11744      /**
11745      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11746      * @type Boolean
11747      */
11748   
11749
11750     /**
11751      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11752      * @type Boolean
11753      */
11754     /**
11755      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11756      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11757      * a finer-grained basis than the DataProxy events.
11758      */
11759     getConnection : function(){
11760         return this.useAjax ? Roo.Ajax : this.conn;
11761     },
11762
11763     /**
11764      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11765      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11766      * process that block using the passed callback.
11767      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11768      * for the request to the remote server.
11769      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11770      * object into a block of Roo.data.Records.
11771      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11772      * The function must be passed <ul>
11773      * <li>The Record block object</li>
11774      * <li>The "arg" argument from the load function</li>
11775      * <li>A boolean success indicator</li>
11776      * </ul>
11777      * @param {Object} scope The scope in which to call the callback
11778      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11779      */
11780     load : function(params, reader, callback, scope, arg){
11781         if(this.fireEvent("beforeload", this, params) !== false){
11782             var  o = {
11783                 params : params || {},
11784                 request: {
11785                     callback : callback,
11786                     scope : scope,
11787                     arg : arg
11788                 },
11789                 reader: reader,
11790                 callback : this.loadResponse,
11791                 scope: this
11792             };
11793             if(this.useAjax){
11794                 Roo.applyIf(o, this.conn);
11795                 if(this.activeRequest){
11796                     Roo.Ajax.abort(this.activeRequest);
11797                 }
11798                 this.activeRequest = Roo.Ajax.request(o);
11799             }else{
11800                 this.conn.request(o);
11801             }
11802         }else{
11803             callback.call(scope||this, null, arg, false);
11804         }
11805     },
11806
11807     // private
11808     loadResponse : function(o, success, response){
11809         delete this.activeRequest;
11810         if(!success){
11811             this.fireEvent("loadexception", this, o, response);
11812             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11813             return;
11814         }
11815         var result;
11816         try {
11817             result = o.reader.read(response);
11818         }catch(e){
11819             this.fireEvent("loadexception", this, o, response, e);
11820             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11821             return;
11822         }
11823         
11824         this.fireEvent("load", this, o, o.request.arg);
11825         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11826     },
11827
11828     // private
11829     update : function(dataSet){
11830
11831     },
11832
11833     // private
11834     updateResponse : function(dataSet){
11835
11836     }
11837 });/*
11838  * Based on:
11839  * Ext JS Library 1.1.1
11840  * Copyright(c) 2006-2007, Ext JS, LLC.
11841  *
11842  * Originally Released Under LGPL - original licence link has changed is not relivant.
11843  *
11844  * Fork - LGPL
11845  * <script type="text/javascript">
11846  */
11847
11848 /**
11849  * @class Roo.data.ScriptTagProxy
11850  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11851  * other than the originating domain of the running page.<br><br>
11852  * <p>
11853  * <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
11854  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11855  * <p>
11856  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11857  * source code that is used as the source inside a &lt;script> tag.<br><br>
11858  * <p>
11859  * In order for the browser to process the returned data, the server must wrap the data object
11860  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11861  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11862  * depending on whether the callback name was passed:
11863  * <p>
11864  * <pre><code>
11865 boolean scriptTag = false;
11866 String cb = request.getParameter("callback");
11867 if (cb != null) {
11868     scriptTag = true;
11869     response.setContentType("text/javascript");
11870 } else {
11871     response.setContentType("application/x-json");
11872 }
11873 Writer out = response.getWriter();
11874 if (scriptTag) {
11875     out.write(cb + "(");
11876 }
11877 out.print(dataBlock.toJsonString());
11878 if (scriptTag) {
11879     out.write(");");
11880 }
11881 </pre></code>
11882  *
11883  * @constructor
11884  * @param {Object} config A configuration object.
11885  */
11886 Roo.data.ScriptTagProxy = function(config){
11887     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11888     Roo.apply(this, config);
11889     this.head = document.getElementsByTagName("head")[0];
11890 };
11891
11892 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11893
11894 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11895     /**
11896      * @cfg {String} url The URL from which to request the data object.
11897      */
11898     /**
11899      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11900      */
11901     timeout : 30000,
11902     /**
11903      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11904      * the server the name of the callback function set up by the load call to process the returned data object.
11905      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11906      * javascript output which calls this named function passing the data object as its only parameter.
11907      */
11908     callbackParam : "callback",
11909     /**
11910      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11911      * name to the request.
11912      */
11913     nocache : true,
11914
11915     /**
11916      * Load data from the configured URL, read the data object into
11917      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11918      * process that block using the passed callback.
11919      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11920      * for the request to the remote server.
11921      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11922      * object into a block of Roo.data.Records.
11923      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11924      * The function must be passed <ul>
11925      * <li>The Record block object</li>
11926      * <li>The "arg" argument from the load function</li>
11927      * <li>A boolean success indicator</li>
11928      * </ul>
11929      * @param {Object} scope The scope in which to call the callback
11930      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11931      */
11932     load : function(params, reader, callback, scope, arg){
11933         if(this.fireEvent("beforeload", this, params) !== false){
11934
11935             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11936
11937             var url = this.url;
11938             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11939             if(this.nocache){
11940                 url += "&_dc=" + (new Date().getTime());
11941             }
11942             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11943             var trans = {
11944                 id : transId,
11945                 cb : "stcCallback"+transId,
11946                 scriptId : "stcScript"+transId,
11947                 params : params,
11948                 arg : arg,
11949                 url : url,
11950                 callback : callback,
11951                 scope : scope,
11952                 reader : reader
11953             };
11954             var conn = this;
11955
11956             window[trans.cb] = function(o){
11957                 conn.handleResponse(o, trans);
11958             };
11959
11960             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11961
11962             if(this.autoAbort !== false){
11963                 this.abort();
11964             }
11965
11966             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11967
11968             var script = document.createElement("script");
11969             script.setAttribute("src", url);
11970             script.setAttribute("type", "text/javascript");
11971             script.setAttribute("id", trans.scriptId);
11972             this.head.appendChild(script);
11973
11974             this.trans = trans;
11975         }else{
11976             callback.call(scope||this, null, arg, false);
11977         }
11978     },
11979
11980     // private
11981     isLoading : function(){
11982         return this.trans ? true : false;
11983     },
11984
11985     /**
11986      * Abort the current server request.
11987      */
11988     abort : function(){
11989         if(this.isLoading()){
11990             this.destroyTrans(this.trans);
11991         }
11992     },
11993
11994     // private
11995     destroyTrans : function(trans, isLoaded){
11996         this.head.removeChild(document.getElementById(trans.scriptId));
11997         clearTimeout(trans.timeoutId);
11998         if(isLoaded){
11999             window[trans.cb] = undefined;
12000             try{
12001                 delete window[trans.cb];
12002             }catch(e){}
12003         }else{
12004             // if hasn't been loaded, wait for load to remove it to prevent script error
12005             window[trans.cb] = function(){
12006                 window[trans.cb] = undefined;
12007                 try{
12008                     delete window[trans.cb];
12009                 }catch(e){}
12010             };
12011         }
12012     },
12013
12014     // private
12015     handleResponse : function(o, trans){
12016         this.trans = false;
12017         this.destroyTrans(trans, true);
12018         var result;
12019         try {
12020             result = trans.reader.readRecords(o);
12021         }catch(e){
12022             this.fireEvent("loadexception", this, o, trans.arg, e);
12023             trans.callback.call(trans.scope||window, null, trans.arg, false);
12024             return;
12025         }
12026         this.fireEvent("load", this, o, trans.arg);
12027         trans.callback.call(trans.scope||window, result, trans.arg, true);
12028     },
12029
12030     // private
12031     handleFailure : function(trans){
12032         this.trans = false;
12033         this.destroyTrans(trans, false);
12034         this.fireEvent("loadexception", this, null, trans.arg);
12035         trans.callback.call(trans.scope||window, null, trans.arg, false);
12036     }
12037 });/*
12038  * Based on:
12039  * Ext JS Library 1.1.1
12040  * Copyright(c) 2006-2007, Ext JS, LLC.
12041  *
12042  * Originally Released Under LGPL - original licence link has changed is not relivant.
12043  *
12044  * Fork - LGPL
12045  * <script type="text/javascript">
12046  */
12047
12048 /**
12049  * @class Roo.data.JsonReader
12050  * @extends Roo.data.DataReader
12051  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12052  * based on mappings in a provided Roo.data.Record constructor.
12053  * 
12054  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12055  * in the reply previously. 
12056  * 
12057  * <p>
12058  * Example code:
12059  * <pre><code>
12060 var RecordDef = Roo.data.Record.create([
12061     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12062     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12063 ]);
12064 var myReader = new Roo.data.JsonReader({
12065     totalProperty: "results",    // The property which contains the total dataset size (optional)
12066     root: "rows",                // The property which contains an Array of row objects
12067     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12068 }, RecordDef);
12069 </code></pre>
12070  * <p>
12071  * This would consume a JSON file like this:
12072  * <pre><code>
12073 { 'results': 2, 'rows': [
12074     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12075     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12076 }
12077 </code></pre>
12078  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12079  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12080  * paged from the remote server.
12081  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12082  * @cfg {String} root name of the property which contains the Array of row objects.
12083  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12084  * @cfg {Array} fields Array of field definition objects
12085  * @constructor
12086  * Create a new JsonReader
12087  * @param {Object} meta Metadata configuration options
12088  * @param {Object} recordType Either an Array of field definition objects,
12089  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12090  */
12091 Roo.data.JsonReader = function(meta, recordType){
12092     
12093     meta = meta || {};
12094     // set some defaults:
12095     Roo.applyIf(meta, {
12096         totalProperty: 'total',
12097         successProperty : 'success',
12098         root : 'data',
12099         id : 'id'
12100     });
12101     
12102     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12103 };
12104 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12105     
12106     /**
12107      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12108      * Used by Store query builder to append _requestMeta to params.
12109      * 
12110      */
12111     metaFromRemote : false,
12112     /**
12113      * This method is only used by a DataProxy which has retrieved data from a remote server.
12114      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12115      * @return {Object} data A data block which is used by an Roo.data.Store object as
12116      * a cache of Roo.data.Records.
12117      */
12118     read : function(response){
12119         var json = response.responseText;
12120        
12121         var o = /* eval:var:o */ eval("("+json+")");
12122         if(!o) {
12123             throw {message: "JsonReader.read: Json object not found"};
12124         }
12125         
12126         if(o.metaData){
12127             
12128             delete this.ef;
12129             this.metaFromRemote = true;
12130             this.meta = o.metaData;
12131             this.recordType = Roo.data.Record.create(o.metaData.fields);
12132             this.onMetaChange(this.meta, this.recordType, o);
12133         }
12134         return this.readRecords(o);
12135     },
12136
12137     // private function a store will implement
12138     onMetaChange : function(meta, recordType, o){
12139
12140     },
12141
12142     /**
12143          * @ignore
12144          */
12145     simpleAccess: function(obj, subsc) {
12146         return obj[subsc];
12147     },
12148
12149         /**
12150          * @ignore
12151          */
12152     getJsonAccessor: function(){
12153         var re = /[\[\.]/;
12154         return function(expr) {
12155             try {
12156                 return(re.test(expr))
12157                     ? new Function("obj", "return obj." + expr)
12158                     : function(obj){
12159                         return obj[expr];
12160                     };
12161             } catch(e){}
12162             return Roo.emptyFn;
12163         };
12164     }(),
12165
12166     /**
12167      * Create a data block containing Roo.data.Records from an XML document.
12168      * @param {Object} o An object which contains an Array of row objects in the property specified
12169      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12170      * which contains the total size of the dataset.
12171      * @return {Object} data A data block which is used by an Roo.data.Store object as
12172      * a cache of Roo.data.Records.
12173      */
12174     readRecords : function(o){
12175         /**
12176          * After any data loads, the raw JSON data is available for further custom processing.
12177          * @type Object
12178          */
12179         this.o = o;
12180         var s = this.meta, Record = this.recordType,
12181             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12182
12183 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12184         if (!this.ef) {
12185             if(s.totalProperty) {
12186                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12187                 }
12188                 if(s.successProperty) {
12189                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12190                 }
12191                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12192                 if (s.id) {
12193                         var g = this.getJsonAccessor(s.id);
12194                         this.getId = function(rec) {
12195                                 var r = g(rec);  
12196                                 return (r === undefined || r === "") ? null : r;
12197                         };
12198                 } else {
12199                         this.getId = function(){return null;};
12200                 }
12201             this.ef = [];
12202             for(var jj = 0; jj < fl; jj++){
12203                 f = fi[jj];
12204                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12205                 this.ef[jj] = this.getJsonAccessor(map);
12206             }
12207         }
12208
12209         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12210         if(s.totalProperty){
12211             var vt = parseInt(this.getTotal(o), 10);
12212             if(!isNaN(vt)){
12213                 totalRecords = vt;
12214             }
12215         }
12216         if(s.successProperty){
12217             var vs = this.getSuccess(o);
12218             if(vs === false || vs === 'false'){
12219                 success = false;
12220             }
12221         }
12222         var records = [];
12223         for(var i = 0; i < c; i++){
12224                 var n = root[i];
12225             var values = {};
12226             var id = this.getId(n);
12227             for(var j = 0; j < fl; j++){
12228                 f = fi[j];
12229             var v = this.ef[j](n);
12230             if (!f.convert) {
12231                 Roo.log('missing convert for ' + f.name);
12232                 Roo.log(f);
12233                 continue;
12234             }
12235             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12236             }
12237             var record = new Record(values, id);
12238             record.json = n;
12239             records[i] = record;
12240         }
12241         return {
12242             raw : o,
12243             success : success,
12244             records : records,
12245             totalRecords : totalRecords
12246         };
12247     }
12248 });/*
12249  * Based on:
12250  * Ext JS Library 1.1.1
12251  * Copyright(c) 2006-2007, Ext JS, LLC.
12252  *
12253  * Originally Released Under LGPL - original licence link has changed is not relivant.
12254  *
12255  * Fork - LGPL
12256  * <script type="text/javascript">
12257  */
12258
12259 /**
12260  * @class Roo.data.ArrayReader
12261  * @extends Roo.data.DataReader
12262  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12263  * Each element of that Array represents a row of data fields. The
12264  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12265  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12266  * <p>
12267  * Example code:.
12268  * <pre><code>
12269 var RecordDef = Roo.data.Record.create([
12270     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12271     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12272 ]);
12273 var myReader = new Roo.data.ArrayReader({
12274     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12275 }, RecordDef);
12276 </code></pre>
12277  * <p>
12278  * This would consume an Array like this:
12279  * <pre><code>
12280 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12281   </code></pre>
12282  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12283  * @constructor
12284  * Create a new JsonReader
12285  * @param {Object} meta Metadata configuration options.
12286  * @param {Object} recordType Either an Array of field definition objects
12287  * as specified to {@link Roo.data.Record#create},
12288  * or an {@link Roo.data.Record} object
12289  * created using {@link Roo.data.Record#create}.
12290  */
12291 Roo.data.ArrayReader = function(meta, recordType){
12292     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12293 };
12294
12295 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12296     /**
12297      * Create a data block containing Roo.data.Records from an XML document.
12298      * @param {Object} o An Array of row objects which represents the dataset.
12299      * @return {Object} data A data block which is used by an Roo.data.Store object as
12300      * a cache of Roo.data.Records.
12301      */
12302     readRecords : function(o){
12303         var sid = this.meta ? this.meta.id : null;
12304         var recordType = this.recordType, fields = recordType.prototype.fields;
12305         var records = [];
12306         var root = o;
12307             for(var i = 0; i < root.length; i++){
12308                     var n = root[i];
12309                 var values = {};
12310                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12311                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12312                 var f = fields.items[j];
12313                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12314                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12315                 v = f.convert(v);
12316                 values[f.name] = v;
12317             }
12318                 var record = new recordType(values, id);
12319                 record.json = n;
12320                 records[records.length] = record;
12321             }
12322             return {
12323                 records : records,
12324                 totalRecords : records.length
12325             };
12326     }
12327 });/*
12328  * - LGPL
12329  * * 
12330  */
12331
12332 /**
12333  * @class Roo.bootstrap.ComboBox
12334  * @extends Roo.bootstrap.TriggerField
12335  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12336  * @cfg {Boolean} append (true|false) default false
12337  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12338  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12339  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12340  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12341  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12342  * @cfg {Boolean} animate default true
12343  * @cfg {Boolean} emptyResultText only for touch device
12344  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12345  * @cfg {String} emptyTitle default ''
12346  * @constructor
12347  * Create a new ComboBox.
12348  * @param {Object} config Configuration options
12349  */
12350 Roo.bootstrap.ComboBox = function(config){
12351     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12352     this.addEvents({
12353         /**
12354          * @event expand
12355          * Fires when the dropdown list is expanded
12356              * @param {Roo.bootstrap.ComboBox} combo This combo box
12357              */
12358         'expand' : true,
12359         /**
12360          * @event collapse
12361          * Fires when the dropdown list is collapsed
12362              * @param {Roo.bootstrap.ComboBox} combo This combo box
12363              */
12364         'collapse' : true,
12365         /**
12366          * @event beforeselect
12367          * Fires before a list item is selected. Return false to cancel the selection.
12368              * @param {Roo.bootstrap.ComboBox} combo This combo box
12369              * @param {Roo.data.Record} record The data record returned from the underlying store
12370              * @param {Number} index The index of the selected item in the dropdown list
12371              */
12372         'beforeselect' : true,
12373         /**
12374          * @event select
12375          * Fires when a list item is selected
12376              * @param {Roo.bootstrap.ComboBox} combo This combo box
12377              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12378              * @param {Number} index The index of the selected item in the dropdown list
12379              */
12380         'select' : true,
12381         /**
12382          * @event beforequery
12383          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12384          * The event object passed has these properties:
12385              * @param {Roo.bootstrap.ComboBox} combo This combo box
12386              * @param {String} query The query
12387              * @param {Boolean} forceAll true to force "all" query
12388              * @param {Boolean} cancel true to cancel the query
12389              * @param {Object} e The query event object
12390              */
12391         'beforequery': true,
12392          /**
12393          * @event add
12394          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12395              * @param {Roo.bootstrap.ComboBox} combo This combo box
12396              */
12397         'add' : true,
12398         /**
12399          * @event edit
12400          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12401              * @param {Roo.bootstrap.ComboBox} combo This combo box
12402              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12403              */
12404         'edit' : true,
12405         /**
12406          * @event remove
12407          * Fires when the remove value from the combobox array
12408              * @param {Roo.bootstrap.ComboBox} combo This combo box
12409              */
12410         'remove' : true,
12411         /**
12412          * @event afterremove
12413          * Fires when the remove value from the combobox array
12414              * @param {Roo.bootstrap.ComboBox} combo This combo box
12415              */
12416         'afterremove' : true,
12417         /**
12418          * @event specialfilter
12419          * Fires when specialfilter
12420             * @param {Roo.bootstrap.ComboBox} combo This combo box
12421             */
12422         'specialfilter' : true,
12423         /**
12424          * @event tick
12425          * Fires when tick the element
12426             * @param {Roo.bootstrap.ComboBox} combo This combo box
12427             */
12428         'tick' : true,
12429         /**
12430          * @event touchviewdisplay
12431          * Fires when touch view require special display (default is using displayField)
12432             * @param {Roo.bootstrap.ComboBox} combo This combo box
12433             * @param {Object} cfg set html .
12434             */
12435         'touchviewdisplay' : true
12436         
12437     });
12438     
12439     this.item = [];
12440     this.tickItems = [];
12441     
12442     this.selectedIndex = -1;
12443     if(this.mode == 'local'){
12444         if(config.queryDelay === undefined){
12445             this.queryDelay = 10;
12446         }
12447         if(config.minChars === undefined){
12448             this.minChars = 0;
12449         }
12450     }
12451 };
12452
12453 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12454      
12455     /**
12456      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12457      * rendering into an Roo.Editor, defaults to false)
12458      */
12459     /**
12460      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12461      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12462      */
12463     /**
12464      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12465      */
12466     /**
12467      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12468      * the dropdown list (defaults to undefined, with no header element)
12469      */
12470
12471      /**
12472      * @cfg {String/Roo.Template} tpl The template to use to render the output
12473      */
12474      
12475      /**
12476      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12477      */
12478     listWidth: undefined,
12479     /**
12480      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12481      * mode = 'remote' or 'text' if mode = 'local')
12482      */
12483     displayField: undefined,
12484     
12485     /**
12486      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12487      * mode = 'remote' or 'value' if mode = 'local'). 
12488      * Note: use of a valueField requires the user make a selection
12489      * in order for a value to be mapped.
12490      */
12491     valueField: undefined,
12492     /**
12493      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12494      */
12495     modalTitle : '',
12496     
12497     /**
12498      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12499      * field's data value (defaults to the underlying DOM element's name)
12500      */
12501     hiddenName: undefined,
12502     /**
12503      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12504      */
12505     listClass: '',
12506     /**
12507      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12508      */
12509     selectedClass: 'active',
12510     
12511     /**
12512      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12513      */
12514     shadow:'sides',
12515     /**
12516      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12517      * anchor positions (defaults to 'tl-bl')
12518      */
12519     listAlign: 'tl-bl?',
12520     /**
12521      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12522      */
12523     maxHeight: 300,
12524     /**
12525      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12526      * query specified by the allQuery config option (defaults to 'query')
12527      */
12528     triggerAction: 'query',
12529     /**
12530      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12531      * (defaults to 4, does not apply if editable = false)
12532      */
12533     minChars : 4,
12534     /**
12535      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12536      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12537      */
12538     typeAhead: false,
12539     /**
12540      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12541      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12542      */
12543     queryDelay: 500,
12544     /**
12545      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12546      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12547      */
12548     pageSize: 0,
12549     /**
12550      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12551      * when editable = true (defaults to false)
12552      */
12553     selectOnFocus:false,
12554     /**
12555      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12556      */
12557     queryParam: 'query',
12558     /**
12559      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12560      * when mode = 'remote' (defaults to 'Loading...')
12561      */
12562     loadingText: 'Loading...',
12563     /**
12564      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12565      */
12566     resizable: false,
12567     /**
12568      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12569      */
12570     handleHeight : 8,
12571     /**
12572      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12573      * traditional select (defaults to true)
12574      */
12575     editable: true,
12576     /**
12577      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12578      */
12579     allQuery: '',
12580     /**
12581      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12582      */
12583     mode: 'remote',
12584     /**
12585      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12586      * listWidth has a higher value)
12587      */
12588     minListWidth : 70,
12589     /**
12590      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12591      * allow the user to set arbitrary text into the field (defaults to false)
12592      */
12593     forceSelection:false,
12594     /**
12595      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12596      * if typeAhead = true (defaults to 250)
12597      */
12598     typeAheadDelay : 250,
12599     /**
12600      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12601      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12602      */
12603     valueNotFoundText : undefined,
12604     /**
12605      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12606      */
12607     blockFocus : false,
12608     
12609     /**
12610      * @cfg {Boolean} disableClear Disable showing of clear button.
12611      */
12612     disableClear : false,
12613     /**
12614      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12615      */
12616     alwaysQuery : false,
12617     
12618     /**
12619      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12620      */
12621     multiple : false,
12622     
12623     /**
12624      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12625      */
12626     invalidClass : "has-warning",
12627     
12628     /**
12629      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12630      */
12631     validClass : "has-success",
12632     
12633     /**
12634      * @cfg {Boolean} specialFilter (true|false) special filter default false
12635      */
12636     specialFilter : false,
12637     
12638     /**
12639      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12640      */
12641     mobileTouchView : true,
12642     
12643     /**
12644      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12645      */
12646     useNativeIOS : false,
12647     
12648     ios_options : false,
12649     
12650     //private
12651     addicon : false,
12652     editicon: false,
12653     
12654     page: 0,
12655     hasQuery: false,
12656     append: false,
12657     loadNext: false,
12658     autoFocus : true,
12659     tickable : false,
12660     btnPosition : 'right',
12661     triggerList : true,
12662     showToggleBtn : true,
12663     animate : true,
12664     emptyResultText: 'Empty',
12665     triggerText : 'Select',
12666     emptyTitle : '',
12667     
12668     // element that contains real text value.. (when hidden is used..)
12669     
12670     getAutoCreate : function()
12671     {   
12672         var cfg = false;
12673         //render
12674         /*
12675          * Render classic select for iso
12676          */
12677         
12678         if(Roo.isIOS && this.useNativeIOS){
12679             cfg = this.getAutoCreateNativeIOS();
12680             return cfg;
12681         }
12682         
12683         /*
12684          * Touch Devices
12685          */
12686         
12687         if(Roo.isTouch && this.mobileTouchView){
12688             cfg = this.getAutoCreateTouchView();
12689             return cfg;;
12690         }
12691         
12692         /*
12693          *  Normal ComboBox
12694          */
12695         if(!this.tickable){
12696             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12697             if(this.name == 'info_year_invest_id_display_name'){
12698                 Roo.log('cfg.................................................');
12699                 Roo.log(cfg);
12700             }
12701             return cfg;
12702         }
12703         
12704         /*
12705          *  ComboBox with tickable selections
12706          */
12707              
12708         var align = this.labelAlign || this.parentLabelAlign();
12709         
12710         cfg = {
12711             cls : 'form-group roo-combobox-tickable' //input-group
12712         };
12713         
12714         var btn_text_select = '';
12715         var btn_text_done = '';
12716         var btn_text_cancel = '';
12717         
12718         if (this.btn_text_show) {
12719             btn_text_select = 'Select';
12720             btn_text_done = 'Done';
12721             btn_text_cancel = 'Cancel'; 
12722         }
12723         
12724         var buttons = {
12725             tag : 'div',
12726             cls : 'tickable-buttons',
12727             cn : [
12728                 {
12729                     tag : 'button',
12730                     type : 'button',
12731                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12732                     //html : this.triggerText
12733                     html: btn_text_select
12734                 },
12735                 {
12736                     tag : 'button',
12737                     type : 'button',
12738                     name : 'ok',
12739                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12740                     //html : 'Done'
12741                     html: btn_text_done
12742                 },
12743                 {
12744                     tag : 'button',
12745                     type : 'button',
12746                     name : 'cancel',
12747                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12748                     //html : 'Cancel'
12749                     html: btn_text_cancel
12750                 }
12751             ]
12752         };
12753         
12754         if(this.editable){
12755             buttons.cn.unshift({
12756                 tag: 'input',
12757                 cls: 'roo-select2-search-field-input'
12758             });
12759         }
12760         
12761         var _this = this;
12762         
12763         Roo.each(buttons.cn, function(c){
12764             if (_this.size) {
12765                 c.cls += ' btn-' + _this.size;
12766             }
12767
12768             if (_this.disabled) {
12769                 c.disabled = true;
12770             }
12771         });
12772         
12773         var box = {
12774             tag: 'div',
12775             cn: [
12776                 {
12777                     tag: 'input',
12778                     type : 'hidden',
12779                     cls: 'form-hidden-field'
12780                 },
12781                 {
12782                     tag: 'ul',
12783                     cls: 'roo-select2-choices',
12784                     cn:[
12785                         {
12786                             tag: 'li',
12787                             cls: 'roo-select2-search-field',
12788                             cn: [
12789                                 buttons
12790                             ]
12791                         }
12792                     ]
12793                 }
12794             ]
12795         };
12796         
12797         var combobox = {
12798             cls: 'roo-select2-container input-group roo-select2-container-multi',
12799             cn: [
12800                 box
12801 //                {
12802 //                    tag: 'ul',
12803 //                    cls: 'typeahead typeahead-long dropdown-menu',
12804 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12805 //                }
12806             ]
12807         };
12808         
12809         if(this.hasFeedback && !this.allowBlank){
12810             
12811             var feedback = {
12812                 tag: 'span',
12813                 cls: 'glyphicon form-control-feedback'
12814             };
12815
12816             combobox.cn.push(feedback);
12817         }
12818         
12819         
12820         if (align ==='left' && this.fieldLabel.length) {
12821             
12822             cfg.cls += ' roo-form-group-label-left';
12823             
12824             cfg.cn = [
12825                 {
12826                     tag : 'i',
12827                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12828                     tooltip : 'This field is required'
12829                 },
12830                 {
12831                     tag: 'label',
12832                     'for' :  id,
12833                     cls : 'control-label',
12834                     html : this.fieldLabel
12835
12836                 },
12837                 {
12838                     cls : "", 
12839                     cn: [
12840                         combobox
12841                     ]
12842                 }
12843
12844             ];
12845             
12846             var labelCfg = cfg.cn[1];
12847             var contentCfg = cfg.cn[2];
12848             
12849
12850             if(this.indicatorpos == 'right'){
12851                 
12852                 cfg.cn = [
12853                     {
12854                         tag: 'label',
12855                         'for' :  id,
12856                         cls : 'control-label',
12857                         cn : [
12858                             {
12859                                 tag : 'span',
12860                                 html : this.fieldLabel
12861                             },
12862                             {
12863                                 tag : 'i',
12864                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12865                                 tooltip : 'This field is required'
12866                             }
12867                         ]
12868                     },
12869                     {
12870                         cls : "",
12871                         cn: [
12872                             combobox
12873                         ]
12874                     }
12875
12876                 ];
12877                 
12878                 
12879                 
12880                 labelCfg = cfg.cn[0];
12881                 contentCfg = cfg.cn[1];
12882             
12883             }
12884             
12885             if(this.labelWidth > 12){
12886                 labelCfg.style = "width: " + this.labelWidth + 'px';
12887             }
12888             
12889             if(this.labelWidth < 13 && this.labelmd == 0){
12890                 this.labelmd = this.labelWidth;
12891             }
12892             
12893             if(this.labellg > 0){
12894                 labelCfg.cls += ' col-lg-' + this.labellg;
12895                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12896             }
12897             
12898             if(this.labelmd > 0){
12899                 labelCfg.cls += ' col-md-' + this.labelmd;
12900                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12901             }
12902             
12903             if(this.labelsm > 0){
12904                 labelCfg.cls += ' col-sm-' + this.labelsm;
12905                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12906             }
12907             
12908             if(this.labelxs > 0){
12909                 labelCfg.cls += ' col-xs-' + this.labelxs;
12910                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12911             }
12912                 
12913                 
12914         } else if ( this.fieldLabel.length) {
12915 //                Roo.log(" label");
12916                  cfg.cn = [
12917                     {
12918                         tag : 'i',
12919                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12920                         tooltip : 'This field is required'
12921                     },
12922                     {
12923                         tag: 'label',
12924                         //cls : 'input-group-addon',
12925                         html : this.fieldLabel
12926                         
12927                     },
12928                     
12929                     combobox
12930                     
12931                 ];
12932                 
12933                 if(this.indicatorpos == 'right'){
12934                     
12935                     cfg.cn = [
12936                         {
12937                             tag: 'label??',
12938                             //cls : 'input-group-addon',
12939                             cn : [
12940                                 {
12941                                     tag : 'span',
12942                                     html : this.fieldLabel
12943                                 },
12944                                 {
12945                                     tag : 'i',
12946                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12947                                     tooltip : 'This field is required'
12948                                 }
12949                             ]
12950                         },
12951                         
12952                         
12953                         
12954                         combobox
12955
12956                     ];
12957                 
12958                 }
12959
12960         } else {
12961             
12962 //                Roo.log(" no label && no align");
12963                 cfg = combobox
12964                      
12965                 
12966         }
12967          
12968         var settings=this;
12969         ['xs','sm','md','lg'].map(function(size){
12970             if (settings[size]) {
12971                 cfg.cls += ' col-' + size + '-' + settings[size];
12972             }
12973         });
12974         
12975         return cfg;
12976         
12977     },
12978     
12979     _initEventsCalled : false,
12980     
12981     // private
12982     initEvents: function()
12983     {   
12984         if (this._initEventsCalled) { // as we call render... prevent looping...
12985             return;
12986         }
12987         this._initEventsCalled = true;
12988         
12989         if (!this.store) {
12990             throw "can not find store for combo";
12991         }
12992         
12993         this.store = Roo.factory(this.store, Roo.data);
12994         this.store.parent = this;
12995         
12996         // if we are building from html. then this element is so complex, that we can not really
12997         // use the rendered HTML.
12998         // so we have to trash and replace the previous code.
12999         if (Roo.XComponent.build_from_html) {
13000             
13001             // remove this element....
13002             var e = this.el.dom, k=0;
13003             while (e ) { e = e.previousSibling;  ++k;}
13004
13005             this.el.remove();
13006             
13007             this.el=false;
13008             this.rendered = false;
13009             
13010             this.render(this.parent().getChildContainer(true), k);
13011             
13012             
13013             
13014         }
13015         
13016         if(Roo.isIOS && this.useNativeIOS){
13017             this.initIOSView();
13018             return;
13019         }
13020         
13021         /*
13022          * Touch Devices
13023          */
13024         
13025         if(Roo.isTouch && this.mobileTouchView){
13026             this.initTouchView();
13027             return;
13028         }
13029         
13030         if(this.tickable){
13031             this.initTickableEvents();
13032             return;
13033         }
13034         
13035         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13036         
13037         if(this.hiddenName){
13038             
13039             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13040             
13041             this.hiddenField.dom.value =
13042                 this.hiddenValue !== undefined ? this.hiddenValue :
13043                 this.value !== undefined ? this.value : '';
13044
13045             // prevent input submission
13046             this.el.dom.removeAttribute('name');
13047             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13048              
13049              
13050         }
13051         //if(Roo.isGecko){
13052         //    this.el.dom.setAttribute('autocomplete', 'off');
13053         //}
13054         
13055         var cls = 'x-combo-list';
13056         
13057         //this.list = new Roo.Layer({
13058         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13059         //});
13060         
13061         var _this = this;
13062         
13063         (function(){
13064             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13065             _this.list.setWidth(lw);
13066         }).defer(100);
13067         
13068         this.list.on('mouseover', this.onViewOver, this);
13069         this.list.on('mousemove', this.onViewMove, this);
13070         
13071         this.list.on('scroll', this.onViewScroll, this);
13072         
13073         /*
13074         this.list.swallowEvent('mousewheel');
13075         this.assetHeight = 0;
13076
13077         if(this.title){
13078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13079             this.assetHeight += this.header.getHeight();
13080         }
13081
13082         this.innerList = this.list.createChild({cls:cls+'-inner'});
13083         this.innerList.on('mouseover', this.onViewOver, this);
13084         this.innerList.on('mousemove', this.onViewMove, this);
13085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13086         
13087         if(this.allowBlank && !this.pageSize && !this.disableClear){
13088             this.footer = this.list.createChild({cls:cls+'-ft'});
13089             this.pageTb = new Roo.Toolbar(this.footer);
13090            
13091         }
13092         if(this.pageSize){
13093             this.footer = this.list.createChild({cls:cls+'-ft'});
13094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13095                     {pageSize: this.pageSize});
13096             
13097         }
13098         
13099         if (this.pageTb && this.allowBlank && !this.disableClear) {
13100             var _this = this;
13101             this.pageTb.add(new Roo.Toolbar.Fill(), {
13102                 cls: 'x-btn-icon x-btn-clear',
13103                 text: '&#160;',
13104                 handler: function()
13105                 {
13106                     _this.collapse();
13107                     _this.clearValue();
13108                     _this.onSelect(false, -1);
13109                 }
13110             });
13111         }
13112         if (this.footer) {
13113             this.assetHeight += this.footer.getHeight();
13114         }
13115         */
13116             
13117         if(!this.tpl){
13118             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13119         }
13120
13121         this.view = new Roo.View(this.list, this.tpl, {
13122             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13123         });
13124         //this.view.wrapEl.setDisplayed(false);
13125         this.view.on('click', this.onViewClick, this);
13126         
13127         
13128         this.store.on('beforeload', this.onBeforeLoad, this);
13129         this.store.on('load', this.onLoad, this);
13130         this.store.on('loadexception', this.onLoadException, this);
13131         /*
13132         if(this.resizable){
13133             this.resizer = new Roo.Resizable(this.list,  {
13134                pinned:true, handles:'se'
13135             });
13136             this.resizer.on('resize', function(r, w, h){
13137                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13138                 this.listWidth = w;
13139                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13140                 this.restrictHeight();
13141             }, this);
13142             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13143         }
13144         */
13145         if(!this.editable){
13146             this.editable = true;
13147             this.setEditable(false);
13148         }
13149         
13150         /*
13151         
13152         if (typeof(this.events.add.listeners) != 'undefined') {
13153             
13154             this.addicon = this.wrap.createChild(
13155                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13156        
13157             this.addicon.on('click', function(e) {
13158                 this.fireEvent('add', this);
13159             }, this);
13160         }
13161         if (typeof(this.events.edit.listeners) != 'undefined') {
13162             
13163             this.editicon = this.wrap.createChild(
13164                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13165             if (this.addicon) {
13166                 this.editicon.setStyle('margin-left', '40px');
13167             }
13168             this.editicon.on('click', function(e) {
13169                 
13170                 // we fire even  if inothing is selected..
13171                 this.fireEvent('edit', this, this.lastData );
13172                 
13173             }, this);
13174         }
13175         */
13176         
13177         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13178             "up" : function(e){
13179                 this.inKeyMode = true;
13180                 this.selectPrev();
13181             },
13182
13183             "down" : function(e){
13184                 if(!this.isExpanded()){
13185                     this.onTriggerClick();
13186                 }else{
13187                     this.inKeyMode = true;
13188                     this.selectNext();
13189                 }
13190             },
13191
13192             "enter" : function(e){
13193 //                this.onViewClick();
13194                 //return true;
13195                 this.collapse();
13196                 
13197                 if(this.fireEvent("specialkey", this, e)){
13198                     this.onViewClick(false);
13199                 }
13200                 
13201                 return true;
13202             },
13203
13204             "esc" : function(e){
13205                 this.collapse();
13206             },
13207
13208             "tab" : function(e){
13209                 this.collapse();
13210                 
13211                 if(this.fireEvent("specialkey", this, e)){
13212                     this.onViewClick(false);
13213                 }
13214                 
13215                 return true;
13216             },
13217
13218             scope : this,
13219
13220             doRelay : function(foo, bar, hname){
13221                 if(hname == 'down' || this.scope.isExpanded()){
13222                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13223                 }
13224                 return true;
13225             },
13226
13227             forceKeyDown: true
13228         });
13229         
13230         
13231         this.queryDelay = Math.max(this.queryDelay || 10,
13232                 this.mode == 'local' ? 10 : 250);
13233         
13234         
13235         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13236         
13237         if(this.typeAhead){
13238             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13239         }
13240         if(this.editable !== false){
13241             this.inputEl().on("keyup", this.onKeyUp, this);
13242         }
13243         if(this.forceSelection){
13244             this.inputEl().on('blur', this.doForce, this);
13245         }
13246         
13247         if(this.multiple){
13248             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13249             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13250         }
13251     },
13252     
13253     initTickableEvents: function()
13254     {   
13255         this.createList();
13256         
13257         if(this.hiddenName){
13258             
13259             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13260             
13261             this.hiddenField.dom.value =
13262                 this.hiddenValue !== undefined ? this.hiddenValue :
13263                 this.value !== undefined ? this.value : '';
13264
13265             // prevent input submission
13266             this.el.dom.removeAttribute('name');
13267             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13268              
13269              
13270         }
13271         
13272 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13273         
13274         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13275         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13276         if(this.triggerList){
13277             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13278         }
13279          
13280         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13281         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13282         
13283         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13284         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13285         
13286         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13287         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13288         
13289         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13290         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13291         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13292         
13293         this.okBtn.hide();
13294         this.cancelBtn.hide();
13295         
13296         var _this = this;
13297         
13298         (function(){
13299             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13300             _this.list.setWidth(lw);
13301         }).defer(100);
13302         
13303         this.list.on('mouseover', this.onViewOver, this);
13304         this.list.on('mousemove', this.onViewMove, this);
13305         
13306         this.list.on('scroll', this.onViewScroll, this);
13307         
13308         if(!this.tpl){
13309             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>';
13310         }
13311
13312         this.view = new Roo.View(this.list, this.tpl, {
13313             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13314         });
13315         
13316         //this.view.wrapEl.setDisplayed(false);
13317         this.view.on('click', this.onViewClick, this);
13318         
13319         
13320         
13321         this.store.on('beforeload', this.onBeforeLoad, this);
13322         this.store.on('load', this.onLoad, this);
13323         this.store.on('loadexception', this.onLoadException, this);
13324         
13325         if(this.editable){
13326             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13327                 "up" : function(e){
13328                     this.inKeyMode = true;
13329                     this.selectPrev();
13330                 },
13331
13332                 "down" : function(e){
13333                     this.inKeyMode = true;
13334                     this.selectNext();
13335                 },
13336
13337                 "enter" : function(e){
13338                     if(this.fireEvent("specialkey", this, e)){
13339                         this.onViewClick(false);
13340                     }
13341                     
13342                     return true;
13343                 },
13344
13345                 "esc" : function(e){
13346                     this.onTickableFooterButtonClick(e, false, false);
13347                 },
13348
13349                 "tab" : function(e){
13350                     this.fireEvent("specialkey", this, e);
13351                     
13352                     this.onTickableFooterButtonClick(e, false, false);
13353                     
13354                     return true;
13355                 },
13356
13357                 scope : this,
13358
13359                 doRelay : function(e, fn, key){
13360                     if(this.scope.isExpanded()){
13361                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13362                     }
13363                     return true;
13364                 },
13365
13366                 forceKeyDown: true
13367             });
13368         }
13369         
13370         this.queryDelay = Math.max(this.queryDelay || 10,
13371                 this.mode == 'local' ? 10 : 250);
13372         
13373         
13374         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13375         
13376         if(this.typeAhead){
13377             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13378         }
13379         
13380         if(this.editable !== false){
13381             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13382         }
13383         
13384         this.indicator = this.indicatorEl();
13385         
13386         if(this.indicator){
13387             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13388             this.indicator.hide();
13389         }
13390         
13391     },
13392
13393     onDestroy : function(){
13394         if(this.view){
13395             this.view.setStore(null);
13396             this.view.el.removeAllListeners();
13397             this.view.el.remove();
13398             this.view.purgeListeners();
13399         }
13400         if(this.list){
13401             this.list.dom.innerHTML  = '';
13402         }
13403         
13404         if(this.store){
13405             this.store.un('beforeload', this.onBeforeLoad, this);
13406             this.store.un('load', this.onLoad, this);
13407             this.store.un('loadexception', this.onLoadException, this);
13408         }
13409         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13410     },
13411
13412     // private
13413     fireKey : function(e){
13414         if(e.isNavKeyPress() && !this.list.isVisible()){
13415             this.fireEvent("specialkey", this, e);
13416         }
13417     },
13418
13419     // private
13420     onResize: function(w, h){
13421 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13422 //        
13423 //        if(typeof w != 'number'){
13424 //            // we do not handle it!?!?
13425 //            return;
13426 //        }
13427 //        var tw = this.trigger.getWidth();
13428 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13429 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13430 //        var x = w - tw;
13431 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13432 //            
13433 //        //this.trigger.setStyle('left', x+'px');
13434 //        
13435 //        if(this.list && this.listWidth === undefined){
13436 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13437 //            this.list.setWidth(lw);
13438 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13439 //        }
13440         
13441     
13442         
13443     },
13444
13445     /**
13446      * Allow or prevent the user from directly editing the field text.  If false is passed,
13447      * the user will only be able to select from the items defined in the dropdown list.  This method
13448      * is the runtime equivalent of setting the 'editable' config option at config time.
13449      * @param {Boolean} value True to allow the user to directly edit the field text
13450      */
13451     setEditable : function(value){
13452         if(value == this.editable){
13453             return;
13454         }
13455         this.editable = value;
13456         if(!value){
13457             this.inputEl().dom.setAttribute('readOnly', true);
13458             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13459             this.inputEl().addClass('x-combo-noedit');
13460         }else{
13461             this.inputEl().dom.setAttribute('readOnly', false);
13462             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13463             this.inputEl().removeClass('x-combo-noedit');
13464         }
13465     },
13466
13467     // private
13468     
13469     onBeforeLoad : function(combo,opts){
13470         if(!this.hasFocus){
13471             return;
13472         }
13473          if (!opts.add) {
13474             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13475          }
13476         this.restrictHeight();
13477         this.selectedIndex = -1;
13478     },
13479
13480     // private
13481     onLoad : function(){
13482         
13483         this.hasQuery = false;
13484         
13485         if(!this.hasFocus){
13486             return;
13487         }
13488         
13489         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13490             this.loading.hide();
13491         }
13492         
13493         if(this.store.getCount() > 0){
13494             
13495             this.expand();
13496             this.restrictHeight();
13497             if(this.lastQuery == this.allQuery){
13498                 if(this.editable && !this.tickable){
13499                     this.inputEl().dom.select();
13500                 }
13501                 
13502                 if(
13503                     !this.selectByValue(this.value, true) &&
13504                     this.autoFocus && 
13505                     (
13506                         !this.store.lastOptions ||
13507                         typeof(this.store.lastOptions.add) == 'undefined' || 
13508                         this.store.lastOptions.add != true
13509                     )
13510                 ){
13511                     this.select(0, true);
13512                 }
13513             }else{
13514                 if(this.autoFocus){
13515                     this.selectNext();
13516                 }
13517                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13518                     this.taTask.delay(this.typeAheadDelay);
13519                 }
13520             }
13521         }else{
13522             this.onEmptyResults();
13523         }
13524         
13525         //this.el.focus();
13526     },
13527     // private
13528     onLoadException : function()
13529     {
13530         this.hasQuery = false;
13531         
13532         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13533             this.loading.hide();
13534         }
13535         
13536         if(this.tickable && this.editable){
13537             return;
13538         }
13539         
13540         this.collapse();
13541         // only causes errors at present
13542         //Roo.log(this.store.reader.jsonData);
13543         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13544             // fixme
13545             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13546         //}
13547         
13548         
13549     },
13550     // private
13551     onTypeAhead : function(){
13552         if(this.store.getCount() > 0){
13553             var r = this.store.getAt(0);
13554             var newValue = r.data[this.displayField];
13555             var len = newValue.length;
13556             var selStart = this.getRawValue().length;
13557             
13558             if(selStart != len){
13559                 this.setRawValue(newValue);
13560                 this.selectText(selStart, newValue.length);
13561             }
13562         }
13563     },
13564
13565     // private
13566     onSelect : function(record, index){
13567         
13568         if(this.fireEvent('beforeselect', this, record, index) !== false){
13569         
13570             this.setFromData(index > -1 ? record.data : false);
13571             
13572             this.collapse();
13573             this.fireEvent('select', this, record, index);
13574         }
13575     },
13576
13577     /**
13578      * Returns the currently selected field value or empty string if no value is set.
13579      * @return {String} value The selected value
13580      */
13581     getValue : function()
13582     {
13583         if(Roo.isIOS && this.useNativeIOS){
13584             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13585         }
13586         
13587         if(this.multiple){
13588             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13589         }
13590         
13591         if(this.valueField){
13592             return typeof this.value != 'undefined' ? this.value : '';
13593         }else{
13594             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13595         }
13596     },
13597     
13598     getRawValue : function()
13599     {
13600         if(Roo.isIOS && this.useNativeIOS){
13601             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13602         }
13603         
13604         var v = this.inputEl().getValue();
13605         
13606         return v;
13607     },
13608
13609     /**
13610      * Clears any text/value currently set in the field
13611      */
13612     clearValue : function(){
13613         
13614         if(this.hiddenField){
13615             this.hiddenField.dom.value = '';
13616         }
13617         this.value = '';
13618         this.setRawValue('');
13619         this.lastSelectionText = '';
13620         this.lastData = false;
13621         
13622         var close = this.closeTriggerEl();
13623         
13624         if(close){
13625             close.hide();
13626         }
13627         
13628         this.validate();
13629         
13630     },
13631
13632     /**
13633      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13634      * will be displayed in the field.  If the value does not match the data value of an existing item,
13635      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13636      * Otherwise the field will be blank (although the value will still be set).
13637      * @param {String} value The value to match
13638      */
13639     setValue : function(v)
13640     {
13641         if(Roo.isIOS && this.useNativeIOS){
13642             this.setIOSValue(v);
13643             return;
13644         }
13645         
13646         if(this.multiple){
13647             this.syncValue();
13648             return;
13649         }
13650         
13651         var text = v;
13652         if(this.valueField){
13653             var r = this.findRecord(this.valueField, v);
13654             if(r){
13655                 text = r.data[this.displayField];
13656             }else if(this.valueNotFoundText !== undefined){
13657                 text = this.valueNotFoundText;
13658             }
13659         }
13660         this.lastSelectionText = text;
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = v;
13663         }
13664         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13665         this.value = v;
13666         
13667         var close = this.closeTriggerEl();
13668         
13669         if(close){
13670             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13671         }
13672         
13673         this.validate();
13674     },
13675     /**
13676      * @property {Object} the last set data for the element
13677      */
13678     
13679     lastData : false,
13680     /**
13681      * Sets the value of the field based on a object which is related to the record format for the store.
13682      * @param {Object} value the value to set as. or false on reset?
13683      */
13684     setFromData : function(o){
13685         
13686         if(this.multiple){
13687             this.addItem(o);
13688             return;
13689         }
13690             
13691         var dv = ''; // display value
13692         var vv = ''; // value value..
13693         this.lastData = o;
13694         if (this.displayField) {
13695             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13696         } else {
13697             // this is an error condition!!!
13698             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13699         }
13700         
13701         if(this.valueField){
13702             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13703         }
13704         
13705         var close = this.closeTriggerEl();
13706         
13707         if(close){
13708             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13709         }
13710         
13711         if(this.hiddenField){
13712             this.hiddenField.dom.value = vv;
13713             
13714             this.lastSelectionText = dv;
13715             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13716             this.value = vv;
13717             return;
13718         }
13719         // no hidden field.. - we store the value in 'value', but still display
13720         // display field!!!!
13721         this.lastSelectionText = dv;
13722         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13723         this.value = vv;
13724         
13725         
13726         
13727     },
13728     // private
13729     reset : function(){
13730         // overridden so that last data is reset..
13731         
13732         if(this.multiple){
13733             this.clearItem();
13734             return;
13735         }
13736         
13737         this.setValue(this.originalValue);
13738         //this.clearInvalid();
13739         this.lastData = false;
13740         if (this.view) {
13741             this.view.clearSelections();
13742         }
13743         
13744         this.validate();
13745     },
13746     // private
13747     findRecord : function(prop, value){
13748         var record;
13749         if(this.store.getCount() > 0){
13750             this.store.each(function(r){
13751                 if(r.data[prop] == value){
13752                     record = r;
13753                     return false;
13754                 }
13755                 return true;
13756             });
13757         }
13758         return record;
13759     },
13760     
13761     getName: function()
13762     {
13763         // returns hidden if it's set..
13764         if (!this.rendered) {return ''};
13765         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13766         
13767     },
13768     // private
13769     onViewMove : function(e, t){
13770         this.inKeyMode = false;
13771     },
13772
13773     // private
13774     onViewOver : function(e, t){
13775         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13776             return;
13777         }
13778         var item = this.view.findItemFromChild(t);
13779         
13780         if(item){
13781             var index = this.view.indexOf(item);
13782             this.select(index, false);
13783         }
13784     },
13785
13786     // private
13787     onViewClick : function(view, doFocus, el, e)
13788     {
13789         var index = this.view.getSelectedIndexes()[0];
13790         
13791         var r = this.store.getAt(index);
13792         
13793         if(this.tickable){
13794             
13795             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13796                 return;
13797             }
13798             
13799             var rm = false;
13800             var _this = this;
13801             
13802             Roo.each(this.tickItems, function(v,k){
13803                 
13804                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13805                     Roo.log(v);
13806                     _this.tickItems.splice(k, 1);
13807                     
13808                     if(typeof(e) == 'undefined' && view == false){
13809                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13810                     }
13811                     
13812                     rm = true;
13813                     return;
13814                 }
13815             });
13816             
13817             if(rm){
13818                 return;
13819             }
13820             
13821             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13822                 this.tickItems.push(r.data);
13823             }
13824             
13825             if(typeof(e) == 'undefined' && view == false){
13826                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13827             }
13828                     
13829             return;
13830         }
13831         
13832         if(r){
13833             this.onSelect(r, index);
13834         }
13835         if(doFocus !== false && !this.blockFocus){
13836             this.inputEl().focus();
13837         }
13838     },
13839
13840     // private
13841     restrictHeight : function(){
13842         //this.innerList.dom.style.height = '';
13843         //var inner = this.innerList.dom;
13844         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13845         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13846         //this.list.beginUpdate();
13847         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13848         this.list.alignTo(this.inputEl(), this.listAlign);
13849         this.list.alignTo(this.inputEl(), this.listAlign);
13850         //this.list.endUpdate();
13851     },
13852
13853     // private
13854     onEmptyResults : function(){
13855         
13856         if(this.tickable && this.editable){
13857             this.restrictHeight();
13858             return;
13859         }
13860         
13861         this.collapse();
13862     },
13863
13864     /**
13865      * Returns true if the dropdown list is expanded, else false.
13866      */
13867     isExpanded : function(){
13868         return this.list.isVisible();
13869     },
13870
13871     /**
13872      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13873      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13874      * @param {String} value The data value of the item to select
13875      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13876      * selected item if it is not currently in view (defaults to true)
13877      * @return {Boolean} True if the value matched an item in the list, else false
13878      */
13879     selectByValue : function(v, scrollIntoView){
13880         if(v !== undefined && v !== null){
13881             var r = this.findRecord(this.valueField || this.displayField, v);
13882             if(r){
13883                 this.select(this.store.indexOf(r), scrollIntoView);
13884                 return true;
13885             }
13886         }
13887         return false;
13888     },
13889
13890     /**
13891      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13892      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13893      * @param {Number} index The zero-based index of the list item to select
13894      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13895      * selected item if it is not currently in view (defaults to true)
13896      */
13897     select : function(index, scrollIntoView){
13898         this.selectedIndex = index;
13899         this.view.select(index);
13900         if(scrollIntoView !== false){
13901             var el = this.view.getNode(index);
13902             /*
13903              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13904              */
13905             if(el){
13906                 this.list.scrollChildIntoView(el, false);
13907             }
13908         }
13909     },
13910
13911     // private
13912     selectNext : function(){
13913         var ct = this.store.getCount();
13914         if(ct > 0){
13915             if(this.selectedIndex == -1){
13916                 this.select(0);
13917             }else if(this.selectedIndex < ct-1){
13918                 this.select(this.selectedIndex+1);
13919             }
13920         }
13921     },
13922
13923     // private
13924     selectPrev : function(){
13925         var ct = this.store.getCount();
13926         if(ct > 0){
13927             if(this.selectedIndex == -1){
13928                 this.select(0);
13929             }else if(this.selectedIndex != 0){
13930                 this.select(this.selectedIndex-1);
13931             }
13932         }
13933     },
13934
13935     // private
13936     onKeyUp : function(e){
13937         if(this.editable !== false && !e.isSpecialKey()){
13938             this.lastKey = e.getKey();
13939             this.dqTask.delay(this.queryDelay);
13940         }
13941     },
13942
13943     // private
13944     validateBlur : function(){
13945         return !this.list || !this.list.isVisible();   
13946     },
13947
13948     // private
13949     initQuery : function(){
13950         
13951         var v = this.getRawValue();
13952         
13953         if(this.tickable && this.editable){
13954             v = this.tickableInputEl().getValue();
13955         }
13956         
13957         this.doQuery(v);
13958     },
13959
13960     // private
13961     doForce : function(){
13962         if(this.inputEl().dom.value.length > 0){
13963             this.inputEl().dom.value =
13964                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13965              
13966         }
13967     },
13968
13969     /**
13970      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13971      * query allowing the query action to be canceled if needed.
13972      * @param {String} query The SQL query to execute
13973      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13974      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13975      * saved in the current store (defaults to false)
13976      */
13977     doQuery : function(q, forceAll){
13978         
13979         if(q === undefined || q === null){
13980             q = '';
13981         }
13982         var qe = {
13983             query: q,
13984             forceAll: forceAll,
13985             combo: this,
13986             cancel:false
13987         };
13988         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13989             return false;
13990         }
13991         q = qe.query;
13992         
13993         forceAll = qe.forceAll;
13994         if(forceAll === true || (q.length >= this.minChars)){
13995             
13996             this.hasQuery = true;
13997             
13998             if(this.lastQuery != q || this.alwaysQuery){
13999                 this.lastQuery = q;
14000                 if(this.mode == 'local'){
14001                     this.selectedIndex = -1;
14002                     if(forceAll){
14003                         this.store.clearFilter();
14004                     }else{
14005                         
14006                         if(this.specialFilter){
14007                             this.fireEvent('specialfilter', this);
14008                             this.onLoad();
14009                             return;
14010                         }
14011                         
14012                         this.store.filter(this.displayField, q);
14013                     }
14014                     
14015                     this.store.fireEvent("datachanged", this.store);
14016                     
14017                     this.onLoad();
14018                     
14019                     
14020                 }else{
14021                     
14022                     this.store.baseParams[this.queryParam] = q;
14023                     
14024                     var options = {params : this.getParams(q)};
14025                     
14026                     if(this.loadNext){
14027                         options.add = true;
14028                         options.params.start = this.page * this.pageSize;
14029                     }
14030                     
14031                     this.store.load(options);
14032                     
14033                     /*
14034                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14035                      *  we should expand the list on onLoad
14036                      *  so command out it
14037                      */
14038 //                    this.expand();
14039                 }
14040             }else{
14041                 this.selectedIndex = -1;
14042                 this.onLoad();   
14043             }
14044         }
14045         
14046         this.loadNext = false;
14047     },
14048     
14049     // private
14050     getParams : function(q){
14051         var p = {};
14052         //p[this.queryParam] = q;
14053         
14054         if(this.pageSize){
14055             p.start = 0;
14056             p.limit = this.pageSize;
14057         }
14058         return p;
14059     },
14060
14061     /**
14062      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14063      */
14064     collapse : function(){
14065         if(!this.isExpanded()){
14066             return;
14067         }
14068         
14069         this.list.hide();
14070         
14071         this.hasFocus = false;
14072         
14073         if(this.tickable){
14074             this.okBtn.hide();
14075             this.cancelBtn.hide();
14076             this.trigger.show();
14077             
14078             if(this.editable){
14079                 this.tickableInputEl().dom.value = '';
14080                 this.tickableInputEl().blur();
14081             }
14082             
14083         }
14084         
14085         Roo.get(document).un('mousedown', this.collapseIf, this);
14086         Roo.get(document).un('mousewheel', this.collapseIf, this);
14087         if (!this.editable) {
14088             Roo.get(document).un('keydown', this.listKeyPress, this);
14089         }
14090         this.fireEvent('collapse', this);
14091         
14092         this.validate();
14093     },
14094
14095     // private
14096     collapseIf : function(e){
14097         var in_combo  = e.within(this.el);
14098         var in_list =  e.within(this.list);
14099         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14100         
14101         if (in_combo || in_list || is_list) {
14102             //e.stopPropagation();
14103             return;
14104         }
14105         
14106         if(this.tickable){
14107             this.onTickableFooterButtonClick(e, false, false);
14108         }
14109
14110         this.collapse();
14111         
14112     },
14113
14114     /**
14115      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14116      */
14117     expand : function(){
14118        
14119         if(this.isExpanded() || !this.hasFocus){
14120             return;
14121         }
14122         
14123         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14124         this.list.setWidth(lw);
14125         
14126         Roo.log('expand');
14127         
14128         this.list.show();
14129         
14130         this.restrictHeight();
14131         
14132         if(this.tickable){
14133             
14134             this.tickItems = Roo.apply([], this.item);
14135             
14136             this.okBtn.show();
14137             this.cancelBtn.show();
14138             this.trigger.hide();
14139             
14140             if(this.editable){
14141                 this.tickableInputEl().focus();
14142             }
14143             
14144         }
14145         
14146         Roo.get(document).on('mousedown', this.collapseIf, this);
14147         Roo.get(document).on('mousewheel', this.collapseIf, this);
14148         if (!this.editable) {
14149             Roo.get(document).on('keydown', this.listKeyPress, this);
14150         }
14151         
14152         this.fireEvent('expand', this);
14153     },
14154
14155     // private
14156     // Implements the default empty TriggerField.onTriggerClick function
14157     onTriggerClick : function(e)
14158     {
14159         Roo.log('trigger click');
14160         
14161         if(this.disabled || !this.triggerList){
14162             return;
14163         }
14164         
14165         this.page = 0;
14166         this.loadNext = false;
14167         
14168         if(this.isExpanded()){
14169             this.collapse();
14170             if (!this.blockFocus) {
14171                 this.inputEl().focus();
14172             }
14173             
14174         }else {
14175             this.hasFocus = true;
14176             if(this.triggerAction == 'all') {
14177                 this.doQuery(this.allQuery, true);
14178             } else {
14179                 this.doQuery(this.getRawValue());
14180             }
14181             if (!this.blockFocus) {
14182                 this.inputEl().focus();
14183             }
14184         }
14185     },
14186     
14187     onTickableTriggerClick : function(e)
14188     {
14189         if(this.disabled){
14190             return;
14191         }
14192         
14193         this.page = 0;
14194         this.loadNext = false;
14195         this.hasFocus = true;
14196         
14197         if(this.triggerAction == 'all') {
14198             this.doQuery(this.allQuery, true);
14199         } else {
14200             this.doQuery(this.getRawValue());
14201         }
14202     },
14203     
14204     onSearchFieldClick : function(e)
14205     {
14206         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14207             this.onTickableFooterButtonClick(e, false, false);
14208             return;
14209         }
14210         
14211         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14212             return;
14213         }
14214         
14215         this.page = 0;
14216         this.loadNext = false;
14217         this.hasFocus = true;
14218         
14219         if(this.triggerAction == 'all') {
14220             this.doQuery(this.allQuery, true);
14221         } else {
14222             this.doQuery(this.getRawValue());
14223         }
14224     },
14225     
14226     listKeyPress : function(e)
14227     {
14228         //Roo.log('listkeypress');
14229         // scroll to first matching element based on key pres..
14230         if (e.isSpecialKey()) {
14231             return false;
14232         }
14233         var k = String.fromCharCode(e.getKey()).toUpperCase();
14234         //Roo.log(k);
14235         var match  = false;
14236         var csel = this.view.getSelectedNodes();
14237         var cselitem = false;
14238         if (csel.length) {
14239             var ix = this.view.indexOf(csel[0]);
14240             cselitem  = this.store.getAt(ix);
14241             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14242                 cselitem = false;
14243             }
14244             
14245         }
14246         
14247         this.store.each(function(v) { 
14248             if (cselitem) {
14249                 // start at existing selection.
14250                 if (cselitem.id == v.id) {
14251                     cselitem = false;
14252                 }
14253                 return true;
14254             }
14255                 
14256             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14257                 match = this.store.indexOf(v);
14258                 return false;
14259             }
14260             return true;
14261         }, this);
14262         
14263         if (match === false) {
14264             return true; // no more action?
14265         }
14266         // scroll to?
14267         this.view.select(match);
14268         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14269         sn.scrollIntoView(sn.dom.parentNode, false);
14270     },
14271     
14272     onViewScroll : function(e, t){
14273         
14274         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){
14275             return;
14276         }
14277         
14278         this.hasQuery = true;
14279         
14280         this.loading = this.list.select('.loading', true).first();
14281         
14282         if(this.loading === null){
14283             this.list.createChild({
14284                 tag: 'div',
14285                 cls: 'loading roo-select2-more-results roo-select2-active',
14286                 html: 'Loading more results...'
14287             });
14288             
14289             this.loading = this.list.select('.loading', true).first();
14290             
14291             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14292             
14293             this.loading.hide();
14294         }
14295         
14296         this.loading.show();
14297         
14298         var _combo = this;
14299         
14300         this.page++;
14301         this.loadNext = true;
14302         
14303         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14304         
14305         return;
14306     },
14307     
14308     addItem : function(o)
14309     {   
14310         var dv = ''; // display value
14311         
14312         if (this.displayField) {
14313             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14314         } else {
14315             // this is an error condition!!!
14316             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14317         }
14318         
14319         if(!dv.length){
14320             return;
14321         }
14322         
14323         var choice = this.choices.createChild({
14324             tag: 'li',
14325             cls: 'roo-select2-search-choice',
14326             cn: [
14327                 {
14328                     tag: 'div',
14329                     html: dv
14330                 },
14331                 {
14332                     tag: 'a',
14333                     href: '#',
14334                     cls: 'roo-select2-search-choice-close fa fa-times',
14335                     tabindex: '-1'
14336                 }
14337             ]
14338             
14339         }, this.searchField);
14340         
14341         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14342         
14343         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14344         
14345         this.item.push(o);
14346         
14347         this.lastData = o;
14348         
14349         this.syncValue();
14350         
14351         this.inputEl().dom.value = '';
14352         
14353         this.validate();
14354     },
14355     
14356     onRemoveItem : function(e, _self, o)
14357     {
14358         e.preventDefault();
14359         
14360         this.lastItem = Roo.apply([], this.item);
14361         
14362         var index = this.item.indexOf(o.data) * 1;
14363         
14364         if( index < 0){
14365             Roo.log('not this item?!');
14366             return;
14367         }
14368         
14369         this.item.splice(index, 1);
14370         o.item.remove();
14371         
14372         this.syncValue();
14373         
14374         this.fireEvent('remove', this, e);
14375         
14376         this.validate();
14377         
14378     },
14379     
14380     syncValue : function()
14381     {
14382         if(!this.item.length){
14383             this.clearValue();
14384             return;
14385         }
14386             
14387         var value = [];
14388         var _this = this;
14389         Roo.each(this.item, function(i){
14390             if(_this.valueField){
14391                 value.push(i[_this.valueField]);
14392                 return;
14393             }
14394
14395             value.push(i);
14396         });
14397
14398         this.value = value.join(',');
14399
14400         if(this.hiddenField){
14401             this.hiddenField.dom.value = this.value;
14402         }
14403         
14404         this.store.fireEvent("datachanged", this.store);
14405         
14406         this.validate();
14407     },
14408     
14409     clearItem : function()
14410     {
14411         if(!this.multiple){
14412             return;
14413         }
14414         
14415         this.item = [];
14416         
14417         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14418            c.remove();
14419         });
14420         
14421         this.syncValue();
14422         
14423         this.validate();
14424         
14425         if(this.tickable && !Roo.isTouch){
14426             this.view.refresh();
14427         }
14428     },
14429     
14430     inputEl: function ()
14431     {
14432         if(Roo.isIOS && this.useNativeIOS){
14433             return this.el.select('select.roo-ios-select', true).first();
14434         }
14435         
14436         if(Roo.isTouch && this.mobileTouchView){
14437             return this.el.select('input.form-control',true).first();
14438         }
14439         
14440         if(this.tickable){
14441             return this.searchField;
14442         }
14443         
14444         return this.el.select('input.form-control',true).first();
14445     },
14446     
14447     onTickableFooterButtonClick : function(e, btn, el)
14448     {
14449         e.preventDefault();
14450         
14451         this.lastItem = Roo.apply([], this.item);
14452         
14453         if(btn && btn.name == 'cancel'){
14454             this.tickItems = Roo.apply([], this.item);
14455             this.collapse();
14456             return;
14457         }
14458         
14459         this.clearItem();
14460         
14461         var _this = this;
14462         
14463         Roo.each(this.tickItems, function(o){
14464             _this.addItem(o);
14465         });
14466         
14467         this.collapse();
14468         
14469     },
14470     
14471     validate : function()
14472     {
14473         var v = this.getRawValue();
14474         
14475         if(this.multiple){
14476             v = this.getValue();
14477         }
14478         
14479         if(this.disabled || this.allowBlank || v.length){
14480             this.markValid();
14481             return true;
14482         }
14483         
14484         this.markInvalid();
14485         return false;
14486     },
14487     
14488     tickableInputEl : function()
14489     {
14490         if(!this.tickable || !this.editable){
14491             return this.inputEl();
14492         }
14493         
14494         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14495     },
14496     
14497     
14498     getAutoCreateTouchView : function()
14499     {
14500         var id = Roo.id();
14501         
14502         var cfg = {
14503             cls: 'form-group' //input-group
14504         };
14505         
14506         var input =  {
14507             tag: 'input',
14508             id : id,
14509             type : this.inputType,
14510             cls : 'form-control x-combo-noedit',
14511             autocomplete: 'new-password',
14512             placeholder : this.placeholder || '',
14513             readonly : true
14514         };
14515         
14516         if (this.name) {
14517             input.name = this.name;
14518         }
14519         
14520         if (this.size) {
14521             input.cls += ' input-' + this.size;
14522         }
14523         
14524         if (this.disabled) {
14525             input.disabled = true;
14526         }
14527         
14528         var inputblock = {
14529             cls : '',
14530             cn : [
14531                 input
14532             ]
14533         };
14534         
14535         if(this.before){
14536             inputblock.cls += ' input-group';
14537             
14538             inputblock.cn.unshift({
14539                 tag :'span',
14540                 cls : 'input-group-addon',
14541                 html : this.before
14542             });
14543         }
14544         
14545         if(this.removable && !this.multiple){
14546             inputblock.cls += ' roo-removable';
14547             
14548             inputblock.cn.push({
14549                 tag: 'button',
14550                 html : 'x',
14551                 cls : 'roo-combo-removable-btn close'
14552             });
14553         }
14554
14555         if(this.hasFeedback && !this.allowBlank){
14556             
14557             inputblock.cls += ' has-feedback';
14558             
14559             inputblock.cn.push({
14560                 tag: 'span',
14561                 cls: 'glyphicon form-control-feedback'
14562             });
14563             
14564         }
14565         
14566         if (this.after) {
14567             
14568             inputblock.cls += (this.before) ? '' : ' input-group';
14569             
14570             inputblock.cn.push({
14571                 tag :'span',
14572                 cls : 'input-group-addon',
14573                 html : this.after
14574             });
14575         }
14576
14577         var box = {
14578             tag: 'div',
14579             cn: [
14580                 {
14581                     tag: 'input',
14582                     type : 'hidden',
14583                     cls: 'form-hidden-field'
14584                 },
14585                 inputblock
14586             ]
14587             
14588         };
14589         
14590         if(this.multiple){
14591             box = {
14592                 tag: 'div',
14593                 cn: [
14594                     {
14595                         tag: 'input',
14596                         type : 'hidden',
14597                         cls: 'form-hidden-field'
14598                     },
14599                     {
14600                         tag: 'ul',
14601                         cls: 'roo-select2-choices',
14602                         cn:[
14603                             {
14604                                 tag: 'li',
14605                                 cls: 'roo-select2-search-field',
14606                                 cn: [
14607
14608                                     inputblock
14609                                 ]
14610                             }
14611                         ]
14612                     }
14613                 ]
14614             }
14615         };
14616         
14617         var combobox = {
14618             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14619             cn: [
14620                 box
14621             ]
14622         };
14623         
14624         if(!this.multiple && this.showToggleBtn){
14625             
14626             var caret = {
14627                         tag: 'span',
14628                         cls: 'caret'
14629             };
14630             
14631             if (this.caret != false) {
14632                 caret = {
14633                      tag: 'i',
14634                      cls: 'fa fa-' + this.caret
14635                 };
14636                 
14637             }
14638             
14639             combobox.cn.push({
14640                 tag :'span',
14641                 cls : 'input-group-addon btn dropdown-toggle',
14642                 cn : [
14643                     caret,
14644                     {
14645                         tag: 'span',
14646                         cls: 'combobox-clear',
14647                         cn  : [
14648                             {
14649                                 tag : 'i',
14650                                 cls: 'icon-remove'
14651                             }
14652                         ]
14653                     }
14654                 ]
14655
14656             })
14657         }
14658         
14659         if(this.multiple){
14660             combobox.cls += ' roo-select2-container-multi';
14661         }
14662         
14663         var align = this.labelAlign || this.parentLabelAlign();
14664         
14665         if (align ==='left' && this.fieldLabel.length) {
14666
14667             cfg.cn = [
14668                 {
14669                    tag : 'i',
14670                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14671                    tooltip : 'This field is required'
14672                 },
14673                 {
14674                     tag: 'label',
14675                     cls : 'control-label',
14676                     html : this.fieldLabel
14677
14678                 },
14679                 {
14680                     cls : '', 
14681                     cn: [
14682                         combobox
14683                     ]
14684                 }
14685             ];
14686             
14687             var labelCfg = cfg.cn[1];
14688             var contentCfg = cfg.cn[2];
14689             
14690
14691             if(this.indicatorpos == 'right'){
14692                 cfg.cn = [
14693                     {
14694                         tag: 'label',
14695                         cls : 'control-label',
14696                         html : this.fieldLabel,
14697                         cn : [
14698                             {
14699                                tag : 'i',
14700                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14701                                tooltip : 'This field is required'
14702                             }
14703                         ]
14704                     },
14705                     {
14706                         cls : '', 
14707                         cn: [
14708                             combobox
14709                         ]
14710                     }
14711                 ];
14712             }
14713             
14714             labelCfg = cfg.cn[0];
14715             contentCfg = cfg.cn[2];
14716             
14717             if(this.labelWidth > 12){
14718                 labelCfg.style = "width: " + this.labelWidth + 'px';
14719             }
14720             
14721             if(this.labelWidth < 13 && this.labelmd == 0){
14722                 this.labelmd = this.labelWidth;
14723             }
14724             
14725             if(this.labellg > 0){
14726                 labelCfg.cls += ' col-lg-' + this.labellg;
14727                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14728             }
14729             
14730             if(this.labelmd > 0){
14731                 labelCfg.cls += ' col-md-' + this.labelmd;
14732                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14733             }
14734             
14735             if(this.labelsm > 0){
14736                 labelCfg.cls += ' col-sm-' + this.labelsm;
14737                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14738             }
14739             
14740             if(this.labelxs > 0){
14741                 labelCfg.cls += ' col-xs-' + this.labelxs;
14742                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14743             }
14744                 
14745                 
14746         } else if ( this.fieldLabel.length) {
14747             cfg.cn = [
14748                 {
14749                    tag : 'i',
14750                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14751                    tooltip : 'This field is required'
14752                 },
14753                 {
14754                     tag: 'label',
14755                     cls : 'control-label',
14756                     html : this.fieldLabel
14757
14758                 },
14759                 {
14760                     cls : '', 
14761                     cn: [
14762                         combobox
14763                     ]
14764                 }
14765             ];
14766             
14767             if(this.indicatorpos == 'right'){
14768                 cfg.cn = [
14769                     {
14770                         tag: 'label',
14771                         cls : 'control-label',
14772                         html : this.fieldLabel,
14773                         cn : [
14774                             {
14775                                tag : 'i',
14776                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14777                                tooltip : 'This field is required'
14778                             }
14779                         ]
14780                     },
14781                     {
14782                         cls : '', 
14783                         cn: [
14784                             combobox
14785                         ]
14786                     }
14787                 ];
14788             }
14789         } else {
14790             cfg.cn = combobox;    
14791         }
14792         
14793         
14794         var settings = this;
14795         
14796         ['xs','sm','md','lg'].map(function(size){
14797             if (settings[size]) {
14798                 cfg.cls += ' col-' + size + '-' + settings[size];
14799             }
14800         });
14801         
14802         return cfg;
14803     },
14804     
14805     initTouchView : function()
14806     {
14807         this.renderTouchView();
14808         
14809         this.touchViewEl.on('scroll', function(){
14810             this.el.dom.scrollTop = 0;
14811         }, this);
14812         
14813         this.originalValue = this.getValue();
14814         
14815         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14816         
14817         this.inputEl().on("click", this.showTouchView, this);
14818         if (this.triggerEl) {
14819             this.triggerEl.on("click", this.showTouchView, this);
14820         }
14821         
14822         
14823         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14824         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14825         
14826         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14827         
14828         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14829         this.store.on('load', this.onTouchViewLoad, this);
14830         this.store.on('loadexception', this.onTouchViewLoadException, this);
14831         
14832         if(this.hiddenName){
14833             
14834             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14835             
14836             this.hiddenField.dom.value =
14837                 this.hiddenValue !== undefined ? this.hiddenValue :
14838                 this.value !== undefined ? this.value : '';
14839         
14840             this.el.dom.removeAttribute('name');
14841             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14842         }
14843         
14844         if(this.multiple){
14845             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14846             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14847         }
14848         
14849         if(this.removable && !this.multiple){
14850             var close = this.closeTriggerEl();
14851             if(close){
14852                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14853                 close.on('click', this.removeBtnClick, this, close);
14854             }
14855         }
14856         /*
14857          * fix the bug in Safari iOS8
14858          */
14859         this.inputEl().on("focus", function(e){
14860             document.activeElement.blur();
14861         }, this);
14862         
14863         return;
14864         
14865         
14866     },
14867     
14868     renderTouchView : function()
14869     {
14870         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14871         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14872         
14873         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14874         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14875         
14876         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14877         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14878         this.touchViewBodyEl.setStyle('overflow', 'auto');
14879         
14880         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14881         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14882         
14883         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14884         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14885         
14886     },
14887     
14888     showTouchView : function()
14889     {
14890         if(this.disabled){
14891             return;
14892         }
14893         
14894         this.touchViewHeaderEl.hide();
14895
14896         if(this.modalTitle.length){
14897             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14898             this.touchViewHeaderEl.show();
14899         }
14900
14901         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14902         this.touchViewEl.show();
14903
14904         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14905         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14906                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14907
14908         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14909
14910         if(this.modalTitle.length){
14911             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14912         }
14913         
14914         this.touchViewBodyEl.setHeight(bodyHeight);
14915
14916         if(this.animate){
14917             var _this = this;
14918             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14919         }else{
14920             this.touchViewEl.addClass('in');
14921         }
14922
14923         this.doTouchViewQuery();
14924         
14925     },
14926     
14927     hideTouchView : function()
14928     {
14929         this.touchViewEl.removeClass('in');
14930
14931         if(this.animate){
14932             var _this = this;
14933             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14934         }else{
14935             this.touchViewEl.setStyle('display', 'none');
14936         }
14937         
14938     },
14939     
14940     setTouchViewValue : function()
14941     {
14942         if(this.multiple){
14943             this.clearItem();
14944         
14945             var _this = this;
14946
14947             Roo.each(this.tickItems, function(o){
14948                 this.addItem(o);
14949             }, this);
14950         }
14951         
14952         this.hideTouchView();
14953     },
14954     
14955     doTouchViewQuery : function()
14956     {
14957         var qe = {
14958             query: '',
14959             forceAll: true,
14960             combo: this,
14961             cancel:false
14962         };
14963         
14964         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14965             return false;
14966         }
14967         
14968         if(!this.alwaysQuery || this.mode == 'local'){
14969             this.onTouchViewLoad();
14970             return;
14971         }
14972         
14973         this.store.load();
14974     },
14975     
14976     onTouchViewBeforeLoad : function(combo,opts)
14977     {
14978         return;
14979     },
14980
14981     // private
14982     onTouchViewLoad : function()
14983     {
14984         if(this.store.getCount() < 1){
14985             this.onTouchViewEmptyResults();
14986             return;
14987         }
14988         
14989         this.clearTouchView();
14990         
14991         var rawValue = this.getRawValue();
14992         
14993         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14994         
14995         this.tickItems = [];
14996         
14997         this.store.data.each(function(d, rowIndex){
14998             var row = this.touchViewListGroup.createChild(template);
14999             
15000             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15001                 row.addClass(d.data.cls);
15002             }
15003             
15004             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15005                 var cfg = {
15006                     data : d.data,
15007                     html : d.data[this.displayField]
15008                 };
15009                 
15010                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15011                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15012                 }
15013             }
15014             row.removeClass('selected');
15015             if(!this.multiple && this.valueField &&
15016                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15017             {
15018                 // radio buttons..
15019                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15020                 row.addClass('selected');
15021             }
15022             
15023             if(this.multiple && this.valueField &&
15024                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15025             {
15026                 
15027                 // checkboxes...
15028                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15029                 this.tickItems.push(d.data);
15030             }
15031             
15032             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15033             
15034         }, this);
15035         
15036         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15037         
15038         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15039
15040         if(this.modalTitle.length){
15041             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15042         }
15043
15044         var listHeight = this.touchViewListGroup.getHeight();
15045         
15046         var _this = this;
15047         
15048         if(firstChecked && listHeight > bodyHeight){
15049             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15050         }
15051         
15052     },
15053     
15054     onTouchViewLoadException : function()
15055     {
15056         this.hideTouchView();
15057     },
15058     
15059     onTouchViewEmptyResults : function()
15060     {
15061         this.clearTouchView();
15062         
15063         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15064         
15065         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15066         
15067     },
15068     
15069     clearTouchView : function()
15070     {
15071         this.touchViewListGroup.dom.innerHTML = '';
15072     },
15073     
15074     onTouchViewClick : function(e, el, o)
15075     {
15076         e.preventDefault();
15077         
15078         var row = o.row;
15079         var rowIndex = o.rowIndex;
15080         
15081         var r = this.store.getAt(rowIndex);
15082         
15083         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15084             
15085             if(!this.multiple){
15086                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15087                     c.dom.removeAttribute('checked');
15088                 }, this);
15089
15090                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15091
15092                 this.setFromData(r.data);
15093
15094                 var close = this.closeTriggerEl();
15095
15096                 if(close){
15097                     close.show();
15098                 }
15099
15100                 this.hideTouchView();
15101
15102                 this.fireEvent('select', this, r, rowIndex);
15103
15104                 return;
15105             }
15106
15107             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15108                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15109                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15110                 return;
15111             }
15112
15113             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15114             this.addItem(r.data);
15115             this.tickItems.push(r.data);
15116         }
15117     },
15118     
15119     getAutoCreateNativeIOS : function()
15120     {
15121         var cfg = {
15122             cls: 'form-group' //input-group,
15123         };
15124         
15125         var combobox =  {
15126             tag: 'select',
15127             cls : 'roo-ios-select'
15128         };
15129         
15130         if (this.name) {
15131             combobox.name = this.name;
15132         }
15133         
15134         if (this.disabled) {
15135             combobox.disabled = true;
15136         }
15137         
15138         var settings = this;
15139         
15140         ['xs','sm','md','lg'].map(function(size){
15141             if (settings[size]) {
15142                 cfg.cls += ' col-' + size + '-' + settings[size];
15143             }
15144         });
15145         
15146         cfg.cn = combobox;
15147         
15148         return cfg;
15149         
15150     },
15151     
15152     initIOSView : function()
15153     {
15154         this.store.on('load', this.onIOSViewLoad, this);
15155         
15156         return;
15157     },
15158     
15159     onIOSViewLoad : function()
15160     {
15161         if(this.store.getCount() < 1){
15162             return;
15163         }
15164         
15165         this.clearIOSView();
15166         
15167         if(this.allowBlank) {
15168             
15169             var default_text = '-- SELECT --';
15170             
15171             var opt = this.inputEl().createChild({
15172                 tag: 'option',
15173                 value : 0,
15174                 html : default_text
15175             });
15176             
15177             var o = {};
15178             o[this.valueField] = 0;
15179             o[this.displayField] = default_text;
15180             
15181             this.ios_options.push({
15182                 data : o,
15183                 el : opt
15184             });
15185             
15186         }
15187         
15188         this.store.data.each(function(d, rowIndex){
15189             
15190             var html = '';
15191             
15192             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15193                 html = d.data[this.displayField];
15194             }
15195             
15196             var value = '';
15197             
15198             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15199                 value = d.data[this.valueField];
15200             }
15201             
15202             var option = {
15203                 tag: 'option',
15204                 value : value,
15205                 html : html
15206             };
15207             
15208             if(this.value == d.data[this.valueField]){
15209                 option['selected'] = true;
15210             }
15211             
15212             var opt = this.inputEl().createChild(option);
15213             
15214             this.ios_options.push({
15215                 data : d.data,
15216                 el : opt
15217             });
15218             
15219         }, this);
15220         
15221         this.inputEl().on('change', function(){
15222            this.fireEvent('select', this);
15223         }, this);
15224         
15225     },
15226     
15227     clearIOSView: function()
15228     {
15229         this.inputEl().dom.innerHTML = '';
15230         
15231         this.ios_options = [];
15232     },
15233     
15234     setIOSValue: function(v)
15235     {
15236         this.value = v;
15237         
15238         if(!this.ios_options){
15239             return;
15240         }
15241         
15242         Roo.each(this.ios_options, function(opts){
15243            
15244            opts.el.dom.removeAttribute('selected');
15245            
15246            if(opts.data[this.valueField] != v){
15247                return;
15248            }
15249            
15250            opts.el.dom.setAttribute('selected', true);
15251            
15252         }, this);
15253     }
15254
15255     /** 
15256     * @cfg {Boolean} grow 
15257     * @hide 
15258     */
15259     /** 
15260     * @cfg {Number} growMin 
15261     * @hide 
15262     */
15263     /** 
15264     * @cfg {Number} growMax 
15265     * @hide 
15266     */
15267     /**
15268      * @hide
15269      * @method autoSize
15270      */
15271 });
15272
15273 Roo.apply(Roo.bootstrap.ComboBox,  {
15274     
15275     header : {
15276         tag: 'div',
15277         cls: 'modal-header',
15278         cn: [
15279             {
15280                 tag: 'h4',
15281                 cls: 'modal-title'
15282             }
15283         ]
15284     },
15285     
15286     body : {
15287         tag: 'div',
15288         cls: 'modal-body',
15289         cn: [
15290             {
15291                 tag: 'ul',
15292                 cls: 'list-group'
15293             }
15294         ]
15295     },
15296     
15297     listItemRadio : {
15298         tag: 'li',
15299         cls: 'list-group-item',
15300         cn: [
15301             {
15302                 tag: 'span',
15303                 cls: 'roo-combobox-list-group-item-value'
15304             },
15305             {
15306                 tag: 'div',
15307                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15308                 cn: [
15309                     {
15310                         tag: 'input',
15311                         type: 'radio'
15312                     },
15313                     {
15314                         tag: 'label'
15315                     }
15316                 ]
15317             }
15318         ]
15319     },
15320     
15321     listItemCheckbox : {
15322         tag: 'li',
15323         cls: 'list-group-item',
15324         cn: [
15325             {
15326                 tag: 'span',
15327                 cls: 'roo-combobox-list-group-item-value'
15328             },
15329             {
15330                 tag: 'div',
15331                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15332                 cn: [
15333                     {
15334                         tag: 'input',
15335                         type: 'checkbox'
15336                     },
15337                     {
15338                         tag: 'label'
15339                     }
15340                 ]
15341             }
15342         ]
15343     },
15344     
15345     emptyResult : {
15346         tag: 'div',
15347         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15348     },
15349     
15350     footer : {
15351         tag: 'div',
15352         cls: 'modal-footer',
15353         cn: [
15354             {
15355                 tag: 'div',
15356                 cls: 'row',
15357                 cn: [
15358                     {
15359                         tag: 'div',
15360                         cls: 'col-xs-6 text-left',
15361                         cn: {
15362                             tag: 'button',
15363                             cls: 'btn btn-danger roo-touch-view-cancel',
15364                             html: 'Cancel'
15365                         }
15366                     },
15367                     {
15368                         tag: 'div',
15369                         cls: 'col-xs-6 text-right',
15370                         cn: {
15371                             tag: 'button',
15372                             cls: 'btn btn-success roo-touch-view-ok',
15373                             html: 'OK'
15374                         }
15375                     }
15376                 ]
15377             }
15378         ]
15379         
15380     }
15381 });
15382
15383 Roo.apply(Roo.bootstrap.ComboBox,  {
15384     
15385     touchViewTemplate : {
15386         tag: 'div',
15387         cls: 'modal fade roo-combobox-touch-view',
15388         cn: [
15389             {
15390                 tag: 'div',
15391                 cls: 'modal-dialog',
15392                 style : 'position:fixed', // we have to fix position....
15393                 cn: [
15394                     {
15395                         tag: 'div',
15396                         cls: 'modal-content',
15397                         cn: [
15398                             Roo.bootstrap.ComboBox.header,
15399                             Roo.bootstrap.ComboBox.body,
15400                             Roo.bootstrap.ComboBox.footer
15401                         ]
15402                     }
15403                 ]
15404             }
15405         ]
15406     }
15407 });/*
15408  * Based on:
15409  * Ext JS Library 1.1.1
15410  * Copyright(c) 2006-2007, Ext JS, LLC.
15411  *
15412  * Originally Released Under LGPL - original licence link has changed is not relivant.
15413  *
15414  * Fork - LGPL
15415  * <script type="text/javascript">
15416  */
15417
15418 /**
15419  * @class Roo.View
15420  * @extends Roo.util.Observable
15421  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15422  * This class also supports single and multi selection modes. <br>
15423  * Create a data model bound view:
15424  <pre><code>
15425  var store = new Roo.data.Store(...);
15426
15427  var view = new Roo.View({
15428     el : "my-element",
15429     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15430  
15431     singleSelect: true,
15432     selectedClass: "ydataview-selected",
15433     store: store
15434  });
15435
15436  // listen for node click?
15437  view.on("click", function(vw, index, node, e){
15438  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15439  });
15440
15441  // load XML data
15442  dataModel.load("foobar.xml");
15443  </code></pre>
15444  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15445  * <br><br>
15446  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15447  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15448  * 
15449  * Note: old style constructor is still suported (container, template, config)
15450  * 
15451  * @constructor
15452  * Create a new View
15453  * @param {Object} config The config object
15454  * 
15455  */
15456 Roo.View = function(config, depreciated_tpl, depreciated_config){
15457     
15458     this.parent = false;
15459     
15460     if (typeof(depreciated_tpl) == 'undefined') {
15461         // new way.. - universal constructor.
15462         Roo.apply(this, config);
15463         this.el  = Roo.get(this.el);
15464     } else {
15465         // old format..
15466         this.el  = Roo.get(config);
15467         this.tpl = depreciated_tpl;
15468         Roo.apply(this, depreciated_config);
15469     }
15470     this.wrapEl  = this.el.wrap().wrap();
15471     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15472     
15473     
15474     if(typeof(this.tpl) == "string"){
15475         this.tpl = new Roo.Template(this.tpl);
15476     } else {
15477         // support xtype ctors..
15478         this.tpl = new Roo.factory(this.tpl, Roo);
15479     }
15480     
15481     
15482     this.tpl.compile();
15483     
15484     /** @private */
15485     this.addEvents({
15486         /**
15487          * @event beforeclick
15488          * Fires before a click is processed. Returns false to cancel the default action.
15489          * @param {Roo.View} this
15490          * @param {Number} index The index of the target node
15491          * @param {HTMLElement} node The target node
15492          * @param {Roo.EventObject} e The raw event object
15493          */
15494             "beforeclick" : true,
15495         /**
15496          * @event click
15497          * Fires when a template node is clicked.
15498          * @param {Roo.View} this
15499          * @param {Number} index The index of the target node
15500          * @param {HTMLElement} node The target node
15501          * @param {Roo.EventObject} e The raw event object
15502          */
15503             "click" : true,
15504         /**
15505          * @event dblclick
15506          * Fires when a template node is double clicked.
15507          * @param {Roo.View} this
15508          * @param {Number} index The index of the target node
15509          * @param {HTMLElement} node The target node
15510          * @param {Roo.EventObject} e The raw event object
15511          */
15512             "dblclick" : true,
15513         /**
15514          * @event contextmenu
15515          * Fires when a template node is right clicked.
15516          * @param {Roo.View} this
15517          * @param {Number} index The index of the target node
15518          * @param {HTMLElement} node The target node
15519          * @param {Roo.EventObject} e The raw event object
15520          */
15521             "contextmenu" : true,
15522         /**
15523          * @event selectionchange
15524          * Fires when the selected nodes change.
15525          * @param {Roo.View} this
15526          * @param {Array} selections Array of the selected nodes
15527          */
15528             "selectionchange" : true,
15529     
15530         /**
15531          * @event beforeselect
15532          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15533          * @param {Roo.View} this
15534          * @param {HTMLElement} node The node to be selected
15535          * @param {Array} selections Array of currently selected nodes
15536          */
15537             "beforeselect" : true,
15538         /**
15539          * @event preparedata
15540          * Fires on every row to render, to allow you to change the data.
15541          * @param {Roo.View} this
15542          * @param {Object} data to be rendered (change this)
15543          */
15544           "preparedata" : true
15545           
15546           
15547         });
15548
15549
15550
15551     this.el.on({
15552         "click": this.onClick,
15553         "dblclick": this.onDblClick,
15554         "contextmenu": this.onContextMenu,
15555         scope:this
15556     });
15557
15558     this.selections = [];
15559     this.nodes = [];
15560     this.cmp = new Roo.CompositeElementLite([]);
15561     if(this.store){
15562         this.store = Roo.factory(this.store, Roo.data);
15563         this.setStore(this.store, true);
15564     }
15565     
15566     if ( this.footer && this.footer.xtype) {
15567            
15568          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15569         
15570         this.footer.dataSource = this.store;
15571         this.footer.container = fctr;
15572         this.footer = Roo.factory(this.footer, Roo);
15573         fctr.insertFirst(this.el);
15574         
15575         // this is a bit insane - as the paging toolbar seems to detach the el..
15576 //        dom.parentNode.parentNode.parentNode
15577          // they get detached?
15578     }
15579     
15580     
15581     Roo.View.superclass.constructor.call(this);
15582     
15583     
15584 };
15585
15586 Roo.extend(Roo.View, Roo.util.Observable, {
15587     
15588      /**
15589      * @cfg {Roo.data.Store} store Data store to load data from.
15590      */
15591     store : false,
15592     
15593     /**
15594      * @cfg {String|Roo.Element} el The container element.
15595      */
15596     el : '',
15597     
15598     /**
15599      * @cfg {String|Roo.Template} tpl The template used by this View 
15600      */
15601     tpl : false,
15602     /**
15603      * @cfg {String} dataName the named area of the template to use as the data area
15604      *                          Works with domtemplates roo-name="name"
15605      */
15606     dataName: false,
15607     /**
15608      * @cfg {String} selectedClass The css class to add to selected nodes
15609      */
15610     selectedClass : "x-view-selected",
15611      /**
15612      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15613      */
15614     emptyText : "",
15615     
15616     /**
15617      * @cfg {String} text to display on mask (default Loading)
15618      */
15619     mask : false,
15620     /**
15621      * @cfg {Boolean} multiSelect Allow multiple selection
15622      */
15623     multiSelect : false,
15624     /**
15625      * @cfg {Boolean} singleSelect Allow single selection
15626      */
15627     singleSelect:  false,
15628     
15629     /**
15630      * @cfg {Boolean} toggleSelect - selecting 
15631      */
15632     toggleSelect : false,
15633     
15634     /**
15635      * @cfg {Boolean} tickable - selecting 
15636      */
15637     tickable : false,
15638     
15639     /**
15640      * Returns the element this view is bound to.
15641      * @return {Roo.Element}
15642      */
15643     getEl : function(){
15644         return this.wrapEl;
15645     },
15646     
15647     
15648
15649     /**
15650      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15651      */
15652     refresh : function(){
15653         //Roo.log('refresh');
15654         var t = this.tpl;
15655         
15656         // if we are using something like 'domtemplate', then
15657         // the what gets used is:
15658         // t.applySubtemplate(NAME, data, wrapping data..)
15659         // the outer template then get' applied with
15660         //     the store 'extra data'
15661         // and the body get's added to the
15662         //      roo-name="data" node?
15663         //      <span class='roo-tpl-{name}'></span> ?????
15664         
15665         
15666         
15667         this.clearSelections();
15668         this.el.update("");
15669         var html = [];
15670         var records = this.store.getRange();
15671         if(records.length < 1) {
15672             
15673             // is this valid??  = should it render a template??
15674             
15675             this.el.update(this.emptyText);
15676             return;
15677         }
15678         var el = this.el;
15679         if (this.dataName) {
15680             this.el.update(t.apply(this.store.meta)); //????
15681             el = this.el.child('.roo-tpl-' + this.dataName);
15682         }
15683         
15684         for(var i = 0, len = records.length; i < len; i++){
15685             var data = this.prepareData(records[i].data, i, records[i]);
15686             this.fireEvent("preparedata", this, data, i, records[i]);
15687             
15688             var d = Roo.apply({}, data);
15689             
15690             if(this.tickable){
15691                 Roo.apply(d, {'roo-id' : Roo.id()});
15692                 
15693                 var _this = this;
15694             
15695                 Roo.each(this.parent.item, function(item){
15696                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15697                         return;
15698                     }
15699                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15700                 });
15701             }
15702             
15703             html[html.length] = Roo.util.Format.trim(
15704                 this.dataName ?
15705                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15706                     t.apply(d)
15707             );
15708         }
15709         
15710         
15711         
15712         el.update(html.join(""));
15713         this.nodes = el.dom.childNodes;
15714         this.updateIndexes(0);
15715     },
15716     
15717
15718     /**
15719      * Function to override to reformat the data that is sent to
15720      * the template for each node.
15721      * DEPRICATED - use the preparedata event handler.
15722      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15723      * a JSON object for an UpdateManager bound view).
15724      */
15725     prepareData : function(data, index, record)
15726     {
15727         this.fireEvent("preparedata", this, data, index, record);
15728         return data;
15729     },
15730
15731     onUpdate : function(ds, record){
15732         // Roo.log('on update');   
15733         this.clearSelections();
15734         var index = this.store.indexOf(record);
15735         var n = this.nodes[index];
15736         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15737         n.parentNode.removeChild(n);
15738         this.updateIndexes(index, index);
15739     },
15740
15741     
15742     
15743 // --------- FIXME     
15744     onAdd : function(ds, records, index)
15745     {
15746         //Roo.log(['on Add', ds, records, index] );        
15747         this.clearSelections();
15748         if(this.nodes.length == 0){
15749             this.refresh();
15750             return;
15751         }
15752         var n = this.nodes[index];
15753         for(var i = 0, len = records.length; i < len; i++){
15754             var d = this.prepareData(records[i].data, i, records[i]);
15755             if(n){
15756                 this.tpl.insertBefore(n, d);
15757             }else{
15758                 
15759                 this.tpl.append(this.el, d);
15760             }
15761         }
15762         this.updateIndexes(index);
15763     },
15764
15765     onRemove : function(ds, record, index){
15766        // Roo.log('onRemove');
15767         this.clearSelections();
15768         var el = this.dataName  ?
15769             this.el.child('.roo-tpl-' + this.dataName) :
15770             this.el; 
15771         
15772         el.dom.removeChild(this.nodes[index]);
15773         this.updateIndexes(index);
15774     },
15775
15776     /**
15777      * Refresh an individual node.
15778      * @param {Number} index
15779      */
15780     refreshNode : function(index){
15781         this.onUpdate(this.store, this.store.getAt(index));
15782     },
15783
15784     updateIndexes : function(startIndex, endIndex){
15785         var ns = this.nodes;
15786         startIndex = startIndex || 0;
15787         endIndex = endIndex || ns.length - 1;
15788         for(var i = startIndex; i <= endIndex; i++){
15789             ns[i].nodeIndex = i;
15790         }
15791     },
15792
15793     /**
15794      * Changes the data store this view uses and refresh the view.
15795      * @param {Store} store
15796      */
15797     setStore : function(store, initial){
15798         if(!initial && this.store){
15799             this.store.un("datachanged", this.refresh);
15800             this.store.un("add", this.onAdd);
15801             this.store.un("remove", this.onRemove);
15802             this.store.un("update", this.onUpdate);
15803             this.store.un("clear", this.refresh);
15804             this.store.un("beforeload", this.onBeforeLoad);
15805             this.store.un("load", this.onLoad);
15806             this.store.un("loadexception", this.onLoad);
15807         }
15808         if(store){
15809           
15810             store.on("datachanged", this.refresh, this);
15811             store.on("add", this.onAdd, this);
15812             store.on("remove", this.onRemove, this);
15813             store.on("update", this.onUpdate, this);
15814             store.on("clear", this.refresh, this);
15815             store.on("beforeload", this.onBeforeLoad, this);
15816             store.on("load", this.onLoad, this);
15817             store.on("loadexception", this.onLoad, this);
15818         }
15819         
15820         if(store){
15821             this.refresh();
15822         }
15823     },
15824     /**
15825      * onbeforeLoad - masks the loading area.
15826      *
15827      */
15828     onBeforeLoad : function(store,opts)
15829     {
15830          //Roo.log('onBeforeLoad');   
15831         if (!opts.add) {
15832             this.el.update("");
15833         }
15834         this.el.mask(this.mask ? this.mask : "Loading" ); 
15835     },
15836     onLoad : function ()
15837     {
15838         this.el.unmask();
15839     },
15840     
15841
15842     /**
15843      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15844      * @param {HTMLElement} node
15845      * @return {HTMLElement} The template node
15846      */
15847     findItemFromChild : function(node){
15848         var el = this.dataName  ?
15849             this.el.child('.roo-tpl-' + this.dataName,true) :
15850             this.el.dom; 
15851         
15852         if(!node || node.parentNode == el){
15853                     return node;
15854             }
15855             var p = node.parentNode;
15856             while(p && p != el){
15857             if(p.parentNode == el){
15858                 return p;
15859             }
15860             p = p.parentNode;
15861         }
15862             return null;
15863     },
15864
15865     /** @ignore */
15866     onClick : function(e){
15867         var item = this.findItemFromChild(e.getTarget());
15868         if(item){
15869             var index = this.indexOf(item);
15870             if(this.onItemClick(item, index, e) !== false){
15871                 this.fireEvent("click", this, index, item, e);
15872             }
15873         }else{
15874             this.clearSelections();
15875         }
15876     },
15877
15878     /** @ignore */
15879     onContextMenu : function(e){
15880         var item = this.findItemFromChild(e.getTarget());
15881         if(item){
15882             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15883         }
15884     },
15885
15886     /** @ignore */
15887     onDblClick : function(e){
15888         var item = this.findItemFromChild(e.getTarget());
15889         if(item){
15890             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15891         }
15892     },
15893
15894     onItemClick : function(item, index, e)
15895     {
15896         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15897             return false;
15898         }
15899         if (this.toggleSelect) {
15900             var m = this.isSelected(item) ? 'unselect' : 'select';
15901             //Roo.log(m);
15902             var _t = this;
15903             _t[m](item, true, false);
15904             return true;
15905         }
15906         if(this.multiSelect || this.singleSelect){
15907             if(this.multiSelect && e.shiftKey && this.lastSelection){
15908                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15909             }else{
15910                 this.select(item, this.multiSelect && e.ctrlKey);
15911                 this.lastSelection = item;
15912             }
15913             
15914             if(!this.tickable){
15915                 e.preventDefault();
15916             }
15917             
15918         }
15919         return true;
15920     },
15921
15922     /**
15923      * Get the number of selected nodes.
15924      * @return {Number}
15925      */
15926     getSelectionCount : function(){
15927         return this.selections.length;
15928     },
15929
15930     /**
15931      * Get the currently selected nodes.
15932      * @return {Array} An array of HTMLElements
15933      */
15934     getSelectedNodes : function(){
15935         return this.selections;
15936     },
15937
15938     /**
15939      * Get the indexes of the selected nodes.
15940      * @return {Array}
15941      */
15942     getSelectedIndexes : function(){
15943         var indexes = [], s = this.selections;
15944         for(var i = 0, len = s.length; i < len; i++){
15945             indexes.push(s[i].nodeIndex);
15946         }
15947         return indexes;
15948     },
15949
15950     /**
15951      * Clear all selections
15952      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15953      */
15954     clearSelections : function(suppressEvent){
15955         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15956             this.cmp.elements = this.selections;
15957             this.cmp.removeClass(this.selectedClass);
15958             this.selections = [];
15959             if(!suppressEvent){
15960                 this.fireEvent("selectionchange", this, this.selections);
15961             }
15962         }
15963     },
15964
15965     /**
15966      * Returns true if the passed node is selected
15967      * @param {HTMLElement/Number} node The node or node index
15968      * @return {Boolean}
15969      */
15970     isSelected : function(node){
15971         var s = this.selections;
15972         if(s.length < 1){
15973             return false;
15974         }
15975         node = this.getNode(node);
15976         return s.indexOf(node) !== -1;
15977     },
15978
15979     /**
15980      * Selects 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 to keep existing selections
15983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15984      */
15985     select : function(nodeInfo, keepExisting, suppressEvent){
15986         if(nodeInfo instanceof Array){
15987             if(!keepExisting){
15988                 this.clearSelections(true);
15989             }
15990             for(var i = 0, len = nodeInfo.length; i < len; i++){
15991                 this.select(nodeInfo[i], true, true);
15992             }
15993             return;
15994         } 
15995         var node = this.getNode(nodeInfo);
15996         if(!node || this.isSelected(node)){
15997             return; // already selected.
15998         }
15999         if(!keepExisting){
16000             this.clearSelections(true);
16001         }
16002         
16003         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16004             Roo.fly(node).addClass(this.selectedClass);
16005             this.selections.push(node);
16006             if(!suppressEvent){
16007                 this.fireEvent("selectionchange", this, this.selections);
16008             }
16009         }
16010         
16011         
16012     },
16013       /**
16014      * Unselects nodes.
16015      * @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
16016      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16017      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16018      */
16019     unselect : function(nodeInfo, keepExisting, suppressEvent)
16020     {
16021         if(nodeInfo instanceof Array){
16022             Roo.each(this.selections, function(s) {
16023                 this.unselect(s, nodeInfo);
16024             }, this);
16025             return;
16026         }
16027         var node = this.getNode(nodeInfo);
16028         if(!node || !this.isSelected(node)){
16029             //Roo.log("not selected");
16030             return; // not selected.
16031         }
16032         // fireevent???
16033         var ns = [];
16034         Roo.each(this.selections, function(s) {
16035             if (s == node ) {
16036                 Roo.fly(node).removeClass(this.selectedClass);
16037
16038                 return;
16039             }
16040             ns.push(s);
16041         },this);
16042         
16043         this.selections= ns;
16044         this.fireEvent("selectionchange", this, this.selections);
16045     },
16046
16047     /**
16048      * Gets a template node.
16049      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16050      * @return {HTMLElement} The node or null if it wasn't found
16051      */
16052     getNode : function(nodeInfo){
16053         if(typeof nodeInfo == "string"){
16054             return document.getElementById(nodeInfo);
16055         }else if(typeof nodeInfo == "number"){
16056             return this.nodes[nodeInfo];
16057         }
16058         return nodeInfo;
16059     },
16060
16061     /**
16062      * Gets a range template nodes.
16063      * @param {Number} startIndex
16064      * @param {Number} endIndex
16065      * @return {Array} An array of nodes
16066      */
16067     getNodes : function(start, end){
16068         var ns = this.nodes;
16069         start = start || 0;
16070         end = typeof end == "undefined" ? ns.length - 1 : end;
16071         var nodes = [];
16072         if(start <= end){
16073             for(var i = start; i <= end; i++){
16074                 nodes.push(ns[i]);
16075             }
16076         } else{
16077             for(var i = start; i >= end; i--){
16078                 nodes.push(ns[i]);
16079             }
16080         }
16081         return nodes;
16082     },
16083
16084     /**
16085      * Finds the index of the passed node
16086      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16087      * @return {Number} The index of the node or -1
16088      */
16089     indexOf : function(node){
16090         node = this.getNode(node);
16091         if(typeof node.nodeIndex == "number"){
16092             return node.nodeIndex;
16093         }
16094         var ns = this.nodes;
16095         for(var i = 0, len = ns.length; i < len; i++){
16096             if(ns[i] == node){
16097                 return i;
16098             }
16099         }
16100         return -1;
16101     }
16102 });
16103 /*
16104  * - LGPL
16105  *
16106  * based on jquery fullcalendar
16107  * 
16108  */
16109
16110 Roo.bootstrap = Roo.bootstrap || {};
16111 /**
16112  * @class Roo.bootstrap.Calendar
16113  * @extends Roo.bootstrap.Component
16114  * Bootstrap Calendar class
16115  * @cfg {Boolean} loadMask (true|false) default false
16116  * @cfg {Object} header generate the user specific header of the calendar, default false
16117
16118  * @constructor
16119  * Create a new Container
16120  * @param {Object} config The config object
16121  */
16122
16123
16124
16125 Roo.bootstrap.Calendar = function(config){
16126     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16127      this.addEvents({
16128         /**
16129              * @event select
16130              * Fires when a date is selected
16131              * @param {DatePicker} this
16132              * @param {Date} date The selected date
16133              */
16134         'select': true,
16135         /**
16136              * @event monthchange
16137              * Fires when the displayed month changes 
16138              * @param {DatePicker} this
16139              * @param {Date} date The selected month
16140              */
16141         'monthchange': true,
16142         /**
16143              * @event evententer
16144              * Fires when mouse over an event
16145              * @param {Calendar} this
16146              * @param {event} Event
16147              */
16148         'evententer': true,
16149         /**
16150              * @event eventleave
16151              * Fires when the mouse leaves an
16152              * @param {Calendar} this
16153              * @param {event}
16154              */
16155         'eventleave': true,
16156         /**
16157              * @event eventclick
16158              * Fires when the mouse click an
16159              * @param {Calendar} this
16160              * @param {event}
16161              */
16162         'eventclick': true
16163         
16164     });
16165
16166 };
16167
16168 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16169     
16170      /**
16171      * @cfg {Number} startDay
16172      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16173      */
16174     startDay : 0,
16175     
16176     loadMask : false,
16177     
16178     header : false,
16179       
16180     getAutoCreate : function(){
16181         
16182         
16183         var fc_button = function(name, corner, style, content ) {
16184             return Roo.apply({},{
16185                 tag : 'span',
16186                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16187                          (corner.length ?
16188                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16189                             ''
16190                         ),
16191                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16192                 unselectable: 'on'
16193             });
16194         };
16195         
16196         var header = {};
16197         
16198         if(!this.header){
16199             header = {
16200                 tag : 'table',
16201                 cls : 'fc-header',
16202                 style : 'width:100%',
16203                 cn : [
16204                     {
16205                         tag: 'tr',
16206                         cn : [
16207                             {
16208                                 tag : 'td',
16209                                 cls : 'fc-header-left',
16210                                 cn : [
16211                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16212                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16213                                     { tag: 'span', cls: 'fc-header-space' },
16214                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16215
16216
16217                                 ]
16218                             },
16219
16220                             {
16221                                 tag : 'td',
16222                                 cls : 'fc-header-center',
16223                                 cn : [
16224                                     {
16225                                         tag: 'span',
16226                                         cls: 'fc-header-title',
16227                                         cn : {
16228                                             tag: 'H2',
16229                                             html : 'month / year'
16230                                         }
16231                                     }
16232
16233                                 ]
16234                             },
16235                             {
16236                                 tag : 'td',
16237                                 cls : 'fc-header-right',
16238                                 cn : [
16239                               /*      fc_button('month', 'left', '', 'month' ),
16240                                     fc_button('week', '', '', 'week' ),
16241                                     fc_button('day', 'right', '', 'day' )
16242                                 */    
16243
16244                                 ]
16245                             }
16246
16247                         ]
16248                     }
16249                 ]
16250             };
16251         }
16252         
16253         header = this.header;
16254         
16255        
16256         var cal_heads = function() {
16257             var ret = [];
16258             // fixme - handle this.
16259             
16260             for (var i =0; i < Date.dayNames.length; i++) {
16261                 var d = Date.dayNames[i];
16262                 ret.push({
16263                     tag: 'th',
16264                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16265                     html : d.substring(0,3)
16266                 });
16267                 
16268             }
16269             ret[0].cls += ' fc-first';
16270             ret[6].cls += ' fc-last';
16271             return ret;
16272         };
16273         var cal_cell = function(n) {
16274             return  {
16275                 tag: 'td',
16276                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16277                 cn : [
16278                     {
16279                         cn : [
16280                             {
16281                                 cls: 'fc-day-number',
16282                                 html: 'D'
16283                             },
16284                             {
16285                                 cls: 'fc-day-content',
16286                              
16287                                 cn : [
16288                                      {
16289                                         style: 'position: relative;' // height: 17px;
16290                                     }
16291                                 ]
16292                             }
16293                             
16294                             
16295                         ]
16296                     }
16297                 ]
16298                 
16299             }
16300         };
16301         var cal_rows = function() {
16302             
16303             var ret = [];
16304             for (var r = 0; r < 6; r++) {
16305                 var row= {
16306                     tag : 'tr',
16307                     cls : 'fc-week',
16308                     cn : []
16309                 };
16310                 
16311                 for (var i =0; i < Date.dayNames.length; i++) {
16312                     var d = Date.dayNames[i];
16313                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16314
16315                 }
16316                 row.cn[0].cls+=' fc-first';
16317                 row.cn[0].cn[0].style = 'min-height:90px';
16318                 row.cn[6].cls+=' fc-last';
16319                 ret.push(row);
16320                 
16321             }
16322             ret[0].cls += ' fc-first';
16323             ret[4].cls += ' fc-prev-last';
16324             ret[5].cls += ' fc-last';
16325             return ret;
16326             
16327         };
16328         
16329         var cal_table = {
16330             tag: 'table',
16331             cls: 'fc-border-separate',
16332             style : 'width:100%',
16333             cellspacing  : 0,
16334             cn : [
16335                 { 
16336                     tag: 'thead',
16337                     cn : [
16338                         { 
16339                             tag: 'tr',
16340                             cls : 'fc-first fc-last',
16341                             cn : cal_heads()
16342                         }
16343                     ]
16344                 },
16345                 { 
16346                     tag: 'tbody',
16347                     cn : cal_rows()
16348                 }
16349                   
16350             ]
16351         };
16352          
16353          var cfg = {
16354             cls : 'fc fc-ltr',
16355             cn : [
16356                 header,
16357                 {
16358                     cls : 'fc-content',
16359                     style : "position: relative;",
16360                     cn : [
16361                         {
16362                             cls : 'fc-view fc-view-month fc-grid',
16363                             style : 'position: relative',
16364                             unselectable : 'on',
16365                             cn : [
16366                                 {
16367                                     cls : 'fc-event-container',
16368                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16369                                 },
16370                                 cal_table
16371                             ]
16372                         }
16373                     ]
16374     
16375                 }
16376            ] 
16377             
16378         };
16379         
16380          
16381         
16382         return cfg;
16383     },
16384     
16385     
16386     initEvents : function()
16387     {
16388         if(!this.store){
16389             throw "can not find store for calendar";
16390         }
16391         
16392         var mark = {
16393             tag: "div",
16394             cls:"x-dlg-mask",
16395             style: "text-align:center",
16396             cn: [
16397                 {
16398                     tag: "div",
16399                     style: "background-color:white;width:50%;margin:250 auto",
16400                     cn: [
16401                         {
16402                             tag: "img",
16403                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16404                         },
16405                         {
16406                             tag: "span",
16407                             html: "Loading"
16408                         }
16409                         
16410                     ]
16411                 }
16412             ]
16413         };
16414         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16415         
16416         var size = this.el.select('.fc-content', true).first().getSize();
16417         this.maskEl.setSize(size.width, size.height);
16418         this.maskEl.enableDisplayMode("block");
16419         if(!this.loadMask){
16420             this.maskEl.hide();
16421         }
16422         
16423         this.store = Roo.factory(this.store, Roo.data);
16424         this.store.on('load', this.onLoad, this);
16425         this.store.on('beforeload', this.onBeforeLoad, this);
16426         
16427         this.resize();
16428         
16429         this.cells = this.el.select('.fc-day',true);
16430         //Roo.log(this.cells);
16431         this.textNodes = this.el.query('.fc-day-number');
16432         this.cells.addClassOnOver('fc-state-hover');
16433         
16434         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16435         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16436         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16437         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16438         
16439         this.on('monthchange', this.onMonthChange, this);
16440         
16441         this.update(new Date().clearTime());
16442     },
16443     
16444     resize : function() {
16445         var sz  = this.el.getSize();
16446         
16447         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16448         this.el.select('.fc-day-content div',true).setHeight(34);
16449     },
16450     
16451     
16452     // private
16453     showPrevMonth : function(e){
16454         this.update(this.activeDate.add("mo", -1));
16455     },
16456     showToday : function(e){
16457         this.update(new Date().clearTime());
16458     },
16459     // private
16460     showNextMonth : function(e){
16461         this.update(this.activeDate.add("mo", 1));
16462     },
16463
16464     // private
16465     showPrevYear : function(){
16466         this.update(this.activeDate.add("y", -1));
16467     },
16468
16469     // private
16470     showNextYear : function(){
16471         this.update(this.activeDate.add("y", 1));
16472     },
16473
16474     
16475    // private
16476     update : function(date)
16477     {
16478         var vd = this.activeDate;
16479         this.activeDate = date;
16480 //        if(vd && this.el){
16481 //            var t = date.getTime();
16482 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16483 //                Roo.log('using add remove');
16484 //                
16485 //                this.fireEvent('monthchange', this, date);
16486 //                
16487 //                this.cells.removeClass("fc-state-highlight");
16488 //                this.cells.each(function(c){
16489 //                   if(c.dateValue == t){
16490 //                       c.addClass("fc-state-highlight");
16491 //                       setTimeout(function(){
16492 //                            try{c.dom.firstChild.focus();}catch(e){}
16493 //                       }, 50);
16494 //                       return false;
16495 //                   }
16496 //                   return true;
16497 //                });
16498 //                return;
16499 //            }
16500 //        }
16501         
16502         var days = date.getDaysInMonth();
16503         
16504         var firstOfMonth = date.getFirstDateOfMonth();
16505         var startingPos = firstOfMonth.getDay()-this.startDay;
16506         
16507         if(startingPos < this.startDay){
16508             startingPos += 7;
16509         }
16510         
16511         var pm = date.add(Date.MONTH, -1);
16512         var prevStart = pm.getDaysInMonth()-startingPos;
16513 //        
16514         this.cells = this.el.select('.fc-day',true);
16515         this.textNodes = this.el.query('.fc-day-number');
16516         this.cells.addClassOnOver('fc-state-hover');
16517         
16518         var cells = this.cells.elements;
16519         var textEls = this.textNodes;
16520         
16521         Roo.each(cells, function(cell){
16522             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16523         });
16524         
16525         days += startingPos;
16526
16527         // convert everything to numbers so it's fast
16528         var day = 86400000;
16529         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16530         //Roo.log(d);
16531         //Roo.log(pm);
16532         //Roo.log(prevStart);
16533         
16534         var today = new Date().clearTime().getTime();
16535         var sel = date.clearTime().getTime();
16536         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16537         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16538         var ddMatch = this.disabledDatesRE;
16539         var ddText = this.disabledDatesText;
16540         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16541         var ddaysText = this.disabledDaysText;
16542         var format = this.format;
16543         
16544         var setCellClass = function(cal, cell){
16545             cell.row = 0;
16546             cell.events = [];
16547             cell.more = [];
16548             //Roo.log('set Cell Class');
16549             cell.title = "";
16550             var t = d.getTime();
16551             
16552             //Roo.log(d);
16553             
16554             cell.dateValue = t;
16555             if(t == today){
16556                 cell.className += " fc-today";
16557                 cell.className += " fc-state-highlight";
16558                 cell.title = cal.todayText;
16559             }
16560             if(t == sel){
16561                 // disable highlight in other month..
16562                 //cell.className += " fc-state-highlight";
16563                 
16564             }
16565             // disabling
16566             if(t < min) {
16567                 cell.className = " fc-state-disabled";
16568                 cell.title = cal.minText;
16569                 return;
16570             }
16571             if(t > max) {
16572                 cell.className = " fc-state-disabled";
16573                 cell.title = cal.maxText;
16574                 return;
16575             }
16576             if(ddays){
16577                 if(ddays.indexOf(d.getDay()) != -1){
16578                     cell.title = ddaysText;
16579                     cell.className = " fc-state-disabled";
16580                 }
16581             }
16582             if(ddMatch && format){
16583                 var fvalue = d.dateFormat(format);
16584                 if(ddMatch.test(fvalue)){
16585                     cell.title = ddText.replace("%0", fvalue);
16586                     cell.className = " fc-state-disabled";
16587                 }
16588             }
16589             
16590             if (!cell.initialClassName) {
16591                 cell.initialClassName = cell.dom.className;
16592             }
16593             
16594             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16595         };
16596
16597         var i = 0;
16598         
16599         for(; i < startingPos; i++) {
16600             textEls[i].innerHTML = (++prevStart);
16601             d.setDate(d.getDate()+1);
16602             
16603             cells[i].className = "fc-past fc-other-month";
16604             setCellClass(this, cells[i]);
16605         }
16606         
16607         var intDay = 0;
16608         
16609         for(; i < days; i++){
16610             intDay = i - startingPos + 1;
16611             textEls[i].innerHTML = (intDay);
16612             d.setDate(d.getDate()+1);
16613             
16614             cells[i].className = ''; // "x-date-active";
16615             setCellClass(this, cells[i]);
16616         }
16617         var extraDays = 0;
16618         
16619         for(; i < 42; i++) {
16620             textEls[i].innerHTML = (++extraDays);
16621             d.setDate(d.getDate()+1);
16622             
16623             cells[i].className = "fc-future fc-other-month";
16624             setCellClass(this, cells[i]);
16625         }
16626         
16627         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16628         
16629         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16630         
16631         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16632         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16633         
16634         if(totalRows != 6){
16635             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16636             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16637         }
16638         
16639         this.fireEvent('monthchange', this, date);
16640         
16641         
16642         /*
16643         if(!this.internalRender){
16644             var main = this.el.dom.firstChild;
16645             var w = main.offsetWidth;
16646             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16647             Roo.fly(main).setWidth(w);
16648             this.internalRender = true;
16649             // opera does not respect the auto grow header center column
16650             // then, after it gets a width opera refuses to recalculate
16651             // without a second pass
16652             if(Roo.isOpera && !this.secondPass){
16653                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16654                 this.secondPass = true;
16655                 this.update.defer(10, this, [date]);
16656             }
16657         }
16658         */
16659         
16660     },
16661     
16662     findCell : function(dt) {
16663         dt = dt.clearTime().getTime();
16664         var ret = false;
16665         this.cells.each(function(c){
16666             //Roo.log("check " +c.dateValue + '?=' + dt);
16667             if(c.dateValue == dt){
16668                 ret = c;
16669                 return false;
16670             }
16671             return true;
16672         });
16673         
16674         return ret;
16675     },
16676     
16677     findCells : function(ev) {
16678         var s = ev.start.clone().clearTime().getTime();
16679        // Roo.log(s);
16680         var e= ev.end.clone().clearTime().getTime();
16681        // Roo.log(e);
16682         var ret = [];
16683         this.cells.each(function(c){
16684              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16685             
16686             if(c.dateValue > e){
16687                 return ;
16688             }
16689             if(c.dateValue < s){
16690                 return ;
16691             }
16692             ret.push(c);
16693         });
16694         
16695         return ret;    
16696     },
16697     
16698 //    findBestRow: function(cells)
16699 //    {
16700 //        var ret = 0;
16701 //        
16702 //        for (var i =0 ; i < cells.length;i++) {
16703 //            ret  = Math.max(cells[i].rows || 0,ret);
16704 //        }
16705 //        return ret;
16706 //        
16707 //    },
16708     
16709     
16710     addItem : function(ev)
16711     {
16712         // look for vertical location slot in
16713         var cells = this.findCells(ev);
16714         
16715 //        ev.row = this.findBestRow(cells);
16716         
16717         // work out the location.
16718         
16719         var crow = false;
16720         var rows = [];
16721         for(var i =0; i < cells.length; i++) {
16722             
16723             cells[i].row = cells[0].row;
16724             
16725             if(i == 0){
16726                 cells[i].row = cells[i].row + 1;
16727             }
16728             
16729             if (!crow) {
16730                 crow = {
16731                     start : cells[i],
16732                     end :  cells[i]
16733                 };
16734                 continue;
16735             }
16736             if (crow.start.getY() == cells[i].getY()) {
16737                 // on same row.
16738                 crow.end = cells[i];
16739                 continue;
16740             }
16741             // different row.
16742             rows.push(crow);
16743             crow = {
16744                 start: cells[i],
16745                 end : cells[i]
16746             };
16747             
16748         }
16749         
16750         rows.push(crow);
16751         ev.els = [];
16752         ev.rows = rows;
16753         ev.cells = cells;
16754         
16755         cells[0].events.push(ev);
16756         
16757         this.calevents.push(ev);
16758     },
16759     
16760     clearEvents: function() {
16761         
16762         if(!this.calevents){
16763             return;
16764         }
16765         
16766         Roo.each(this.cells.elements, function(c){
16767             c.row = 0;
16768             c.events = [];
16769             c.more = [];
16770         });
16771         
16772         Roo.each(this.calevents, function(e) {
16773             Roo.each(e.els, function(el) {
16774                 el.un('mouseenter' ,this.onEventEnter, this);
16775                 el.un('mouseleave' ,this.onEventLeave, this);
16776                 el.remove();
16777             },this);
16778         },this);
16779         
16780         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16781             e.remove();
16782         });
16783         
16784     },
16785     
16786     renderEvents: function()
16787     {   
16788         var _this = this;
16789         
16790         this.cells.each(function(c) {
16791             
16792             if(c.row < 5){
16793                 return;
16794             }
16795             
16796             var ev = c.events;
16797             
16798             var r = 4;
16799             if(c.row != c.events.length){
16800                 r = 4 - (4 - (c.row - c.events.length));
16801             }
16802             
16803             c.events = ev.slice(0, r);
16804             c.more = ev.slice(r);
16805             
16806             if(c.more.length && c.more.length == 1){
16807                 c.events.push(c.more.pop());
16808             }
16809             
16810             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16811             
16812         });
16813             
16814         this.cells.each(function(c) {
16815             
16816             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16817             
16818             
16819             for (var e = 0; e < c.events.length; e++){
16820                 var ev = c.events[e];
16821                 var rows = ev.rows;
16822                 
16823                 for(var i = 0; i < rows.length; i++) {
16824                 
16825                     // how many rows should it span..
16826
16827                     var  cfg = {
16828                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16829                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16830
16831                         unselectable : "on",
16832                         cn : [
16833                             {
16834                                 cls: 'fc-event-inner',
16835                                 cn : [
16836     //                                {
16837     //                                  tag:'span',
16838     //                                  cls: 'fc-event-time',
16839     //                                  html : cells.length > 1 ? '' : ev.time
16840     //                                },
16841                                     {
16842                                       tag:'span',
16843                                       cls: 'fc-event-title',
16844                                       html : String.format('{0}', ev.title)
16845                                     }
16846
16847
16848                                 ]
16849                             },
16850                             {
16851                                 cls: 'ui-resizable-handle ui-resizable-e',
16852                                 html : '&nbsp;&nbsp;&nbsp'
16853                             }
16854
16855                         ]
16856                     };
16857
16858                     if (i == 0) {
16859                         cfg.cls += ' fc-event-start';
16860                     }
16861                     if ((i+1) == rows.length) {
16862                         cfg.cls += ' fc-event-end';
16863                     }
16864
16865                     var ctr = _this.el.select('.fc-event-container',true).first();
16866                     var cg = ctr.createChild(cfg);
16867
16868                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16869                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16870
16871                     var r = (c.more.length) ? 1 : 0;
16872                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16873                     cg.setWidth(ebox.right - sbox.x -2);
16874
16875                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16876                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16877                     cg.on('click', _this.onEventClick, _this, ev);
16878
16879                     ev.els.push(cg);
16880                     
16881                 }
16882                 
16883             }
16884             
16885             
16886             if(c.more.length){
16887                 var  cfg = {
16888                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16889                     style : 'position: absolute',
16890                     unselectable : "on",
16891                     cn : [
16892                         {
16893                             cls: 'fc-event-inner',
16894                             cn : [
16895                                 {
16896                                   tag:'span',
16897                                   cls: 'fc-event-title',
16898                                   html : 'More'
16899                                 }
16900
16901
16902                             ]
16903                         },
16904                         {
16905                             cls: 'ui-resizable-handle ui-resizable-e',
16906                             html : '&nbsp;&nbsp;&nbsp'
16907                         }
16908
16909                     ]
16910                 };
16911
16912                 var ctr = _this.el.select('.fc-event-container',true).first();
16913                 var cg = ctr.createChild(cfg);
16914
16915                 var sbox = c.select('.fc-day-content',true).first().getBox();
16916                 var ebox = c.select('.fc-day-content',true).first().getBox();
16917                 //Roo.log(cg);
16918                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16919                 cg.setWidth(ebox.right - sbox.x -2);
16920
16921                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16922                 
16923             }
16924             
16925         });
16926         
16927         
16928         
16929     },
16930     
16931     onEventEnter: function (e, el,event,d) {
16932         this.fireEvent('evententer', this, el, event);
16933     },
16934     
16935     onEventLeave: function (e, el,event,d) {
16936         this.fireEvent('eventleave', this, el, event);
16937     },
16938     
16939     onEventClick: function (e, el,event,d) {
16940         this.fireEvent('eventclick', this, el, event);
16941     },
16942     
16943     onMonthChange: function () {
16944         this.store.load();
16945     },
16946     
16947     onMoreEventClick: function(e, el, more)
16948     {
16949         var _this = this;
16950         
16951         this.calpopover.placement = 'right';
16952         this.calpopover.setTitle('More');
16953         
16954         this.calpopover.setContent('');
16955         
16956         var ctr = this.calpopover.el.select('.popover-content', true).first();
16957         
16958         Roo.each(more, function(m){
16959             var cfg = {
16960                 cls : 'fc-event-hori fc-event-draggable',
16961                 html : m.title
16962             };
16963             var cg = ctr.createChild(cfg);
16964             
16965             cg.on('click', _this.onEventClick, _this, m);
16966         });
16967         
16968         this.calpopover.show(el);
16969         
16970         
16971     },
16972     
16973     onLoad: function () 
16974     {   
16975         this.calevents = [];
16976         var cal = this;
16977         
16978         if(this.store.getCount() > 0){
16979             this.store.data.each(function(d){
16980                cal.addItem({
16981                     id : d.data.id,
16982                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16983                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16984                     time : d.data.start_time,
16985                     title : d.data.title,
16986                     description : d.data.description,
16987                     venue : d.data.venue
16988                 });
16989             });
16990         }
16991         
16992         this.renderEvents();
16993         
16994         if(this.calevents.length && this.loadMask){
16995             this.maskEl.hide();
16996         }
16997     },
16998     
16999     onBeforeLoad: function()
17000     {
17001         this.clearEvents();
17002         if(this.loadMask){
17003             this.maskEl.show();
17004         }
17005     }
17006 });
17007
17008  
17009  /*
17010  * - LGPL
17011  *
17012  * element
17013  * 
17014  */
17015
17016 /**
17017  * @class Roo.bootstrap.Popover
17018  * @extends Roo.bootstrap.Component
17019  * Bootstrap Popover class
17020  * @cfg {String} html contents of the popover   (or false to use children..)
17021  * @cfg {String} title of popover (or false to hide)
17022  * @cfg {String} placement how it is placed
17023  * @cfg {String} trigger click || hover (or false to trigger manually)
17024  * @cfg {String} over what (parent or false to trigger manually.)
17025  * @cfg {Number} delay - delay before showing
17026  
17027  * @constructor
17028  * Create a new Popover
17029  * @param {Object} config The config object
17030  */
17031
17032 Roo.bootstrap.Popover = function(config){
17033     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17034     
17035     this.addEvents({
17036         // raw events
17037          /**
17038          * @event show
17039          * After the popover show
17040          * 
17041          * @param {Roo.bootstrap.Popover} this
17042          */
17043         "show" : true,
17044         /**
17045          * @event hide
17046          * After the popover hide
17047          * 
17048          * @param {Roo.bootstrap.Popover} this
17049          */
17050         "hide" : true
17051     });
17052 };
17053
17054 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17055     
17056     title: 'Fill in a title',
17057     html: false,
17058     
17059     placement : 'right',
17060     trigger : 'hover', // hover
17061     
17062     delay : 0,
17063     
17064     over: 'parent',
17065     
17066     can_build_overlaid : false,
17067     
17068     getChildContainer : function()
17069     {
17070         return this.el.select('.popover-content',true).first();
17071     },
17072     
17073     getAutoCreate : function(){
17074          
17075         var cfg = {
17076            cls : 'popover roo-dynamic',
17077            style: 'display:block',
17078            cn : [
17079                 {
17080                     cls : 'arrow'
17081                 },
17082                 {
17083                     cls : 'popover-inner',
17084                     cn : [
17085                         {
17086                             tag: 'h3',
17087                             cls: 'popover-title',
17088                             html : this.title
17089                         },
17090                         {
17091                             cls : 'popover-content',
17092                             html : this.html
17093                         }
17094                     ]
17095                     
17096                 }
17097            ]
17098         };
17099         
17100         return cfg;
17101     },
17102     setTitle: function(str)
17103     {
17104         this.title = str;
17105         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17106     },
17107     setContent: function(str)
17108     {
17109         this.html = str;
17110         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17111     },
17112     // as it get's added to the bottom of the page.
17113     onRender : function(ct, position)
17114     {
17115         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17116         if(!this.el){
17117             var cfg = Roo.apply({},  this.getAutoCreate());
17118             cfg.id = Roo.id();
17119             
17120             if (this.cls) {
17121                 cfg.cls += ' ' + this.cls;
17122             }
17123             if (this.style) {
17124                 cfg.style = this.style;
17125             }
17126             //Roo.log("adding to ");
17127             this.el = Roo.get(document.body).createChild(cfg, position);
17128 //            Roo.log(this.el);
17129         }
17130         this.initEvents();
17131     },
17132     
17133     initEvents : function()
17134     {
17135         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17136         this.el.enableDisplayMode('block');
17137         this.el.hide();
17138         if (this.over === false) {
17139             return; 
17140         }
17141         if (this.triggers === false) {
17142             return;
17143         }
17144         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17145         var triggers = this.trigger ? this.trigger.split(' ') : [];
17146         Roo.each(triggers, function(trigger) {
17147         
17148             if (trigger == 'click') {
17149                 on_el.on('click', this.toggle, this);
17150             } else if (trigger != 'manual') {
17151                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17152                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17153       
17154                 on_el.on(eventIn  ,this.enter, this);
17155                 on_el.on(eventOut, this.leave, this);
17156             }
17157         }, this);
17158         
17159     },
17160     
17161     
17162     // private
17163     timeout : null,
17164     hoverState : null,
17165     
17166     toggle : function () {
17167         this.hoverState == 'in' ? this.leave() : this.enter();
17168     },
17169     
17170     enter : function () {
17171         
17172         clearTimeout(this.timeout);
17173     
17174         this.hoverState = 'in';
17175     
17176         if (!this.delay || !this.delay.show) {
17177             this.show();
17178             return;
17179         }
17180         var _t = this;
17181         this.timeout = setTimeout(function () {
17182             if (_t.hoverState == 'in') {
17183                 _t.show();
17184             }
17185         }, this.delay.show)
17186     },
17187     
17188     leave : function() {
17189         clearTimeout(this.timeout);
17190     
17191         this.hoverState = 'out';
17192     
17193         if (!this.delay || !this.delay.hide) {
17194             this.hide();
17195             return;
17196         }
17197         var _t = this;
17198         this.timeout = setTimeout(function () {
17199             if (_t.hoverState == 'out') {
17200                 _t.hide();
17201             }
17202         }, this.delay.hide)
17203     },
17204     
17205     show : function (on_el)
17206     {
17207         if (!on_el) {
17208             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17209         }
17210         
17211         // set content.
17212         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17213         if (this.html !== false) {
17214             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17215         }
17216         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17217         if (!this.title.length) {
17218             this.el.select('.popover-title',true).hide();
17219         }
17220         
17221         var placement = typeof this.placement == 'function' ?
17222             this.placement.call(this, this.el, on_el) :
17223             this.placement;
17224             
17225         var autoToken = /\s?auto?\s?/i;
17226         var autoPlace = autoToken.test(placement);
17227         if (autoPlace) {
17228             placement = placement.replace(autoToken, '') || 'top';
17229         }
17230         
17231         //this.el.detach()
17232         //this.el.setXY([0,0]);
17233         this.el.show();
17234         this.el.dom.style.display='block';
17235         this.el.addClass(placement);
17236         
17237         //this.el.appendTo(on_el);
17238         
17239         var p = this.getPosition();
17240         var box = this.el.getBox();
17241         
17242         if (autoPlace) {
17243             // fixme..
17244         }
17245         var align = Roo.bootstrap.Popover.alignment[placement];
17246         this.el.alignTo(on_el, align[0],align[1]);
17247         //var arrow = this.el.select('.arrow',true).first();
17248         //arrow.set(align[2], 
17249         
17250         this.el.addClass('in');
17251         
17252         
17253         if (this.el.hasClass('fade')) {
17254             // fade it?
17255         }
17256         
17257         this.hoverState = 'in';
17258         
17259         this.fireEvent('show', this);
17260         
17261     },
17262     hide : function()
17263     {
17264         this.el.setXY([0,0]);
17265         this.el.removeClass('in');
17266         this.el.hide();
17267         this.hoverState = null;
17268         
17269         this.fireEvent('hide', this);
17270     }
17271     
17272 });
17273
17274 Roo.bootstrap.Popover.alignment = {
17275     'left' : ['r-l', [-10,0], 'right'],
17276     'right' : ['l-r', [10,0], 'left'],
17277     'bottom' : ['t-b', [0,10], 'top'],
17278     'top' : [ 'b-t', [0,-10], 'bottom']
17279 };
17280
17281  /*
17282  * - LGPL
17283  *
17284  * Progress
17285  * 
17286  */
17287
17288 /**
17289  * @class Roo.bootstrap.Progress
17290  * @extends Roo.bootstrap.Component
17291  * Bootstrap Progress class
17292  * @cfg {Boolean} striped striped of the progress bar
17293  * @cfg {Boolean} active animated of the progress bar
17294  * 
17295  * 
17296  * @constructor
17297  * Create a new Progress
17298  * @param {Object} config The config object
17299  */
17300
17301 Roo.bootstrap.Progress = function(config){
17302     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17303 };
17304
17305 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17306     
17307     striped : false,
17308     active: false,
17309     
17310     getAutoCreate : function(){
17311         var cfg = {
17312             tag: 'div',
17313             cls: 'progress'
17314         };
17315         
17316         
17317         if(this.striped){
17318             cfg.cls += ' progress-striped';
17319         }
17320       
17321         if(this.active){
17322             cfg.cls += ' active';
17323         }
17324         
17325         
17326         return cfg;
17327     }
17328    
17329 });
17330
17331  
17332
17333  /*
17334  * - LGPL
17335  *
17336  * ProgressBar
17337  * 
17338  */
17339
17340 /**
17341  * @class Roo.bootstrap.ProgressBar
17342  * @extends Roo.bootstrap.Component
17343  * Bootstrap ProgressBar class
17344  * @cfg {Number} aria_valuenow aria-value now
17345  * @cfg {Number} aria_valuemin aria-value min
17346  * @cfg {Number} aria_valuemax aria-value max
17347  * @cfg {String} label label for the progress bar
17348  * @cfg {String} panel (success | info | warning | danger )
17349  * @cfg {String} role role of the progress bar
17350  * @cfg {String} sr_only text
17351  * 
17352  * 
17353  * @constructor
17354  * Create a new ProgressBar
17355  * @param {Object} config The config object
17356  */
17357
17358 Roo.bootstrap.ProgressBar = function(config){
17359     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17360 };
17361
17362 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17363     
17364     aria_valuenow : 0,
17365     aria_valuemin : 0,
17366     aria_valuemax : 100,
17367     label : false,
17368     panel : false,
17369     role : false,
17370     sr_only: false,
17371     
17372     getAutoCreate : function()
17373     {
17374         
17375         var cfg = {
17376             tag: 'div',
17377             cls: 'progress-bar',
17378             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17379         };
17380         
17381         if(this.sr_only){
17382             cfg.cn = {
17383                 tag: 'span',
17384                 cls: 'sr-only',
17385                 html: this.sr_only
17386             }
17387         }
17388         
17389         if(this.role){
17390             cfg.role = this.role;
17391         }
17392         
17393         if(this.aria_valuenow){
17394             cfg['aria-valuenow'] = this.aria_valuenow;
17395         }
17396         
17397         if(this.aria_valuemin){
17398             cfg['aria-valuemin'] = this.aria_valuemin;
17399         }
17400         
17401         if(this.aria_valuemax){
17402             cfg['aria-valuemax'] = this.aria_valuemax;
17403         }
17404         
17405         if(this.label && !this.sr_only){
17406             cfg.html = this.label;
17407         }
17408         
17409         if(this.panel){
17410             cfg.cls += ' progress-bar-' + this.panel;
17411         }
17412         
17413         return cfg;
17414     },
17415     
17416     update : function(aria_valuenow)
17417     {
17418         this.aria_valuenow = aria_valuenow;
17419         
17420         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17421     }
17422    
17423 });
17424
17425  
17426
17427  /*
17428  * - LGPL
17429  *
17430  * column
17431  * 
17432  */
17433
17434 /**
17435  * @class Roo.bootstrap.TabGroup
17436  * @extends Roo.bootstrap.Column
17437  * Bootstrap Column class
17438  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17439  * @cfg {Boolean} carousel true to make the group behave like a carousel
17440  * @cfg {Boolean} bullets show bullets for the panels
17441  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17442  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17443  * @cfg {Boolean} showarrow (true|false) show arrow default true
17444  * 
17445  * @constructor
17446  * Create a new TabGroup
17447  * @param {Object} config The config object
17448  */
17449
17450 Roo.bootstrap.TabGroup = function(config){
17451     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17452     if (!this.navId) {
17453         this.navId = Roo.id();
17454     }
17455     this.tabs = [];
17456     Roo.bootstrap.TabGroup.register(this);
17457     
17458 };
17459
17460 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17461     
17462     carousel : false,
17463     transition : false,
17464     bullets : 0,
17465     timer : 0,
17466     autoslide : false,
17467     slideFn : false,
17468     slideOnTouch : false,
17469     showarrow : true,
17470     
17471     getAutoCreate : function()
17472     {
17473         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17474         
17475         cfg.cls += ' tab-content';
17476         
17477         if (this.carousel) {
17478             cfg.cls += ' carousel slide';
17479             
17480             cfg.cn = [{
17481                cls : 'carousel-inner',
17482                cn : []
17483             }];
17484         
17485             if(this.bullets  && !Roo.isTouch){
17486                 
17487                 var bullets = {
17488                     cls : 'carousel-bullets',
17489                     cn : []
17490                 };
17491                
17492                 if(this.bullets_cls){
17493                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17494                 }
17495                 
17496                 bullets.cn.push({
17497                     cls : 'clear'
17498                 });
17499                 
17500                 cfg.cn[0].cn.push(bullets);
17501             }
17502             
17503             if(this.showarrow){
17504                 cfg.cn[0].cn.push({
17505                     tag : 'div',
17506                     class : 'carousel-arrow',
17507                     cn : [
17508                         {
17509                             tag : 'div',
17510                             class : 'carousel-prev',
17511                             cn : [
17512                                 {
17513                                     tag : 'i',
17514                                     class : 'fa fa-chevron-left'
17515                                 }
17516                             ]
17517                         },
17518                         {
17519                             tag : 'div',
17520                             class : 'carousel-next',
17521                             cn : [
17522                                 {
17523                                     tag : 'i',
17524                                     class : 'fa fa-chevron-right'
17525                                 }
17526                             ]
17527                         }
17528                     ]
17529                 });
17530             }
17531             
17532         }
17533         
17534         return cfg;
17535     },
17536     
17537     initEvents:  function()
17538     {
17539 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17540 //            this.el.on("touchstart", this.onTouchStart, this);
17541 //        }
17542         
17543         if(this.autoslide){
17544             var _this = this;
17545             
17546             this.slideFn = window.setInterval(function() {
17547                 _this.showPanelNext();
17548             }, this.timer);
17549         }
17550         
17551         if(this.showarrow){
17552             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17553             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17554         }
17555         
17556         
17557     },
17558     
17559 //    onTouchStart : function(e, el, o)
17560 //    {
17561 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17562 //            return;
17563 //        }
17564 //        
17565 //        this.showPanelNext();
17566 //    },
17567     
17568     
17569     getChildContainer : function()
17570     {
17571         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17572     },
17573     
17574     /**
17575     * register a Navigation item
17576     * @param {Roo.bootstrap.NavItem} the navitem to add
17577     */
17578     register : function(item)
17579     {
17580         this.tabs.push( item);
17581         item.navId = this.navId; // not really needed..
17582         this.addBullet();
17583     
17584     },
17585     
17586     getActivePanel : function()
17587     {
17588         var r = false;
17589         Roo.each(this.tabs, function(t) {
17590             if (t.active) {
17591                 r = t;
17592                 return false;
17593             }
17594             return null;
17595         });
17596         return r;
17597         
17598     },
17599     getPanelByName : function(n)
17600     {
17601         var r = false;
17602         Roo.each(this.tabs, function(t) {
17603             if (t.tabId == n) {
17604                 r = t;
17605                 return false;
17606             }
17607             return null;
17608         });
17609         return r;
17610     },
17611     indexOfPanel : function(p)
17612     {
17613         var r = false;
17614         Roo.each(this.tabs, function(t,i) {
17615             if (t.tabId == p.tabId) {
17616                 r = i;
17617                 return false;
17618             }
17619             return null;
17620         });
17621         return r;
17622     },
17623     /**
17624      * show a specific panel
17625      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17626      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17627      */
17628     showPanel : function (pan)
17629     {
17630         if(this.transition || typeof(pan) == 'undefined'){
17631             Roo.log("waiting for the transitionend");
17632             return;
17633         }
17634         
17635         if (typeof(pan) == 'number') {
17636             pan = this.tabs[pan];
17637         }
17638         
17639         if (typeof(pan) == 'string') {
17640             pan = this.getPanelByName(pan);
17641         }
17642         
17643         var cur = this.getActivePanel();
17644         
17645         if(!pan || !cur){
17646             Roo.log('pan or acitve pan is undefined');
17647             return false;
17648         }
17649         
17650         if (pan.tabId == this.getActivePanel().tabId) {
17651             return true;
17652         }
17653         
17654         if (false === cur.fireEvent('beforedeactivate')) {
17655             return false;
17656         }
17657         
17658         if(this.bullets > 0 && !Roo.isTouch){
17659             this.setActiveBullet(this.indexOfPanel(pan));
17660         }
17661         
17662         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17663             
17664             this.transition = true;
17665             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17666             var lr = dir == 'next' ? 'left' : 'right';
17667             pan.el.addClass(dir); // or prev
17668             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17669             cur.el.addClass(lr); // or right
17670             pan.el.addClass(lr);
17671             
17672             var _this = this;
17673             cur.el.on('transitionend', function() {
17674                 Roo.log("trans end?");
17675                 
17676                 pan.el.removeClass([lr,dir]);
17677                 pan.setActive(true);
17678                 
17679                 cur.el.removeClass([lr]);
17680                 cur.setActive(false);
17681                 
17682                 _this.transition = false;
17683                 
17684             }, this, { single:  true } );
17685             
17686             return true;
17687         }
17688         
17689         cur.setActive(false);
17690         pan.setActive(true);
17691         
17692         return true;
17693         
17694     },
17695     showPanelNext : function()
17696     {
17697         var i = this.indexOfPanel(this.getActivePanel());
17698         
17699         if (i >= this.tabs.length - 1 && !this.autoslide) {
17700             return;
17701         }
17702         
17703         if (i >= this.tabs.length - 1 && this.autoslide) {
17704             i = -1;
17705         }
17706         
17707         this.showPanel(this.tabs[i+1]);
17708     },
17709     
17710     showPanelPrev : function()
17711     {
17712         var i = this.indexOfPanel(this.getActivePanel());
17713         
17714         if (i  < 1 && !this.autoslide) {
17715             return;
17716         }
17717         
17718         if (i < 1 && this.autoslide) {
17719             i = this.tabs.length;
17720         }
17721         
17722         this.showPanel(this.tabs[i-1]);
17723     },
17724     
17725     
17726     addBullet: function()
17727     {
17728         if(!this.bullets || Roo.isTouch){
17729             return;
17730         }
17731         var ctr = this.el.select('.carousel-bullets',true).first();
17732         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17733         var bullet = ctr.createChild({
17734             cls : 'bullet bullet-' + i
17735         },ctr.dom.lastChild);
17736         
17737         
17738         var _this = this;
17739         
17740         bullet.on('click', (function(e, el, o, ii, t){
17741
17742             e.preventDefault();
17743
17744             this.showPanel(ii);
17745
17746             if(this.autoslide && this.slideFn){
17747                 clearInterval(this.slideFn);
17748                 this.slideFn = window.setInterval(function() {
17749                     _this.showPanelNext();
17750                 }, this.timer);
17751             }
17752
17753         }).createDelegate(this, [i, bullet], true));
17754                 
17755         
17756     },
17757      
17758     setActiveBullet : function(i)
17759     {
17760         if(Roo.isTouch){
17761             return;
17762         }
17763         
17764         Roo.each(this.el.select('.bullet', true).elements, function(el){
17765             el.removeClass('selected');
17766         });
17767
17768         var bullet = this.el.select('.bullet-' + i, true).first();
17769         
17770         if(!bullet){
17771             return;
17772         }
17773         
17774         bullet.addClass('selected');
17775     }
17776     
17777     
17778   
17779 });
17780
17781  
17782
17783  
17784  
17785 Roo.apply(Roo.bootstrap.TabGroup, {
17786     
17787     groups: {},
17788      /**
17789     * register a Navigation Group
17790     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17791     */
17792     register : function(navgrp)
17793     {
17794         this.groups[navgrp.navId] = navgrp;
17795         
17796     },
17797     /**
17798     * fetch a Navigation Group based on the navigation ID
17799     * if one does not exist , it will get created.
17800     * @param {string} the navgroup to add
17801     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17802     */
17803     get: function(navId) {
17804         if (typeof(this.groups[navId]) == 'undefined') {
17805             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17806         }
17807         return this.groups[navId] ;
17808     }
17809     
17810     
17811     
17812 });
17813
17814  /*
17815  * - LGPL
17816  *
17817  * TabPanel
17818  * 
17819  */
17820
17821 /**
17822  * @class Roo.bootstrap.TabPanel
17823  * @extends Roo.bootstrap.Component
17824  * Bootstrap TabPanel class
17825  * @cfg {Boolean} active panel active
17826  * @cfg {String} html panel content
17827  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17828  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17829  * @cfg {String} href click to link..
17830  * 
17831  * 
17832  * @constructor
17833  * Create a new TabPanel
17834  * @param {Object} config The config object
17835  */
17836
17837 Roo.bootstrap.TabPanel = function(config){
17838     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17839     this.addEvents({
17840         /**
17841              * @event changed
17842              * Fires when the active status changes
17843              * @param {Roo.bootstrap.TabPanel} this
17844              * @param {Boolean} state the new state
17845             
17846          */
17847         'changed': true,
17848         /**
17849              * @event beforedeactivate
17850              * Fires before a tab is de-activated - can be used to do validation on a form.
17851              * @param {Roo.bootstrap.TabPanel} this
17852              * @return {Boolean} false if there is an error
17853             
17854          */
17855         'beforedeactivate': true
17856      });
17857     
17858     this.tabId = this.tabId || Roo.id();
17859   
17860 };
17861
17862 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17863     
17864     active: false,
17865     html: false,
17866     tabId: false,
17867     navId : false,
17868     href : '',
17869     
17870     getAutoCreate : function(){
17871         var cfg = {
17872             tag: 'div',
17873             // item is needed for carousel - not sure if it has any effect otherwise
17874             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17875             html: this.html || ''
17876         };
17877         
17878         if(this.active){
17879             cfg.cls += ' active';
17880         }
17881         
17882         if(this.tabId){
17883             cfg.tabId = this.tabId;
17884         }
17885         
17886         
17887         return cfg;
17888     },
17889     
17890     initEvents:  function()
17891     {
17892         var p = this.parent();
17893         
17894         this.navId = this.navId || p.navId;
17895         
17896         if (typeof(this.navId) != 'undefined') {
17897             // not really needed.. but just in case.. parent should be a NavGroup.
17898             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17899             
17900             tg.register(this);
17901             
17902             var i = tg.tabs.length - 1;
17903             
17904             if(this.active && tg.bullets > 0 && i < tg.bullets){
17905                 tg.setActiveBullet(i);
17906             }
17907         }
17908         
17909         this.el.on('click', this.onClick, this);
17910         
17911         if(Roo.isTouch){
17912             this.el.on("touchstart", this.onTouchStart, this);
17913             this.el.on("touchmove", this.onTouchMove, this);
17914             this.el.on("touchend", this.onTouchEnd, this);
17915         }
17916         
17917     },
17918     
17919     onRender : function(ct, position)
17920     {
17921         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17922     },
17923     
17924     setActive : function(state)
17925     {
17926         Roo.log("panel - set active " + this.tabId + "=" + state);
17927         
17928         this.active = state;
17929         if (!state) {
17930             this.el.removeClass('active');
17931             
17932         } else  if (!this.el.hasClass('active')) {
17933             this.el.addClass('active');
17934         }
17935         
17936         this.fireEvent('changed', this, state);
17937     },
17938     
17939     onClick : function(e)
17940     {
17941         e.preventDefault();
17942         
17943         if(!this.href.length){
17944             return;
17945         }
17946         
17947         window.location.href = this.href;
17948     },
17949     
17950     startX : 0,
17951     startY : 0,
17952     endX : 0,
17953     endY : 0,
17954     swiping : false,
17955     
17956     onTouchStart : function(e)
17957     {
17958         this.swiping = false;
17959         
17960         this.startX = e.browserEvent.touches[0].clientX;
17961         this.startY = e.browserEvent.touches[0].clientY;
17962     },
17963     
17964     onTouchMove : function(e)
17965     {
17966         this.swiping = true;
17967         
17968         this.endX = e.browserEvent.touches[0].clientX;
17969         this.endY = e.browserEvent.touches[0].clientY;
17970     },
17971     
17972     onTouchEnd : function(e)
17973     {
17974         if(!this.swiping){
17975             this.onClick(e);
17976             return;
17977         }
17978         
17979         var tabGroup = this.parent();
17980         
17981         if(this.endX > this.startX){ // swiping right
17982             tabGroup.showPanelPrev();
17983             return;
17984         }
17985         
17986         if(this.startX > this.endX){ // swiping left
17987             tabGroup.showPanelNext();
17988             return;
17989         }
17990     }
17991     
17992     
17993 });
17994  
17995
17996  
17997
17998  /*
17999  * - LGPL
18000  *
18001  * DateField
18002  * 
18003  */
18004
18005 /**
18006  * @class Roo.bootstrap.DateField
18007  * @extends Roo.bootstrap.Input
18008  * Bootstrap DateField class
18009  * @cfg {Number} weekStart default 0
18010  * @cfg {String} viewMode default empty, (months|years)
18011  * @cfg {String} minViewMode default empty, (months|years)
18012  * @cfg {Number} startDate default -Infinity
18013  * @cfg {Number} endDate default Infinity
18014  * @cfg {Boolean} todayHighlight default false
18015  * @cfg {Boolean} todayBtn default false
18016  * @cfg {Boolean} calendarWeeks default false
18017  * @cfg {Object} daysOfWeekDisabled default empty
18018  * @cfg {Boolean} singleMode default false (true | false)
18019  * 
18020  * @cfg {Boolean} keyboardNavigation default true
18021  * @cfg {String} language default en
18022  * 
18023  * @constructor
18024  * Create a new DateField
18025  * @param {Object} config The config object
18026  */
18027
18028 Roo.bootstrap.DateField = function(config){
18029     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18030      this.addEvents({
18031             /**
18032              * @event show
18033              * Fires when this field show.
18034              * @param {Roo.bootstrap.DateField} this
18035              * @param {Mixed} date The date value
18036              */
18037             show : true,
18038             /**
18039              * @event show
18040              * Fires when this field hide.
18041              * @param {Roo.bootstrap.DateField} this
18042              * @param {Mixed} date The date value
18043              */
18044             hide : true,
18045             /**
18046              * @event select
18047              * Fires when select a date.
18048              * @param {Roo.bootstrap.DateField} this
18049              * @param {Mixed} date The date value
18050              */
18051             select : true,
18052             /**
18053              * @event beforeselect
18054              * Fires when before select a date.
18055              * @param {Roo.bootstrap.DateField} this
18056              * @param {Mixed} date The date value
18057              */
18058             beforeselect : true
18059         });
18060 };
18061
18062 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18063     
18064     /**
18065      * @cfg {String} format
18066      * The default date format string which can be overriden for localization support.  The format must be
18067      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18068      */
18069     format : "m/d/y",
18070     /**
18071      * @cfg {String} altFormats
18072      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18073      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18074      */
18075     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18076     
18077     weekStart : 0,
18078     
18079     viewMode : '',
18080     
18081     minViewMode : '',
18082     
18083     todayHighlight : false,
18084     
18085     todayBtn: false,
18086     
18087     language: 'en',
18088     
18089     keyboardNavigation: true,
18090     
18091     calendarWeeks: false,
18092     
18093     startDate: -Infinity,
18094     
18095     endDate: Infinity,
18096     
18097     daysOfWeekDisabled: [],
18098     
18099     _events: [],
18100     
18101     singleMode : false,
18102     
18103     UTCDate: function()
18104     {
18105         return new Date(Date.UTC.apply(Date, arguments));
18106     },
18107     
18108     UTCToday: function()
18109     {
18110         var today = new Date();
18111         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18112     },
18113     
18114     getDate: function() {
18115             var d = this.getUTCDate();
18116             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18117     },
18118     
18119     getUTCDate: function() {
18120             return this.date;
18121     },
18122     
18123     setDate: function(d) {
18124             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18125     },
18126     
18127     setUTCDate: function(d) {
18128             this.date = d;
18129             this.setValue(this.formatDate(this.date));
18130     },
18131         
18132     onRender: function(ct, position)
18133     {
18134         
18135         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18136         
18137         this.language = this.language || 'en';
18138         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18140         
18141         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18142         this.format = this.format || 'm/d/y';
18143         this.isInline = false;
18144         this.isInput = true;
18145         this.component = this.el.select('.add-on', true).first() || false;
18146         this.component = (this.component && this.component.length === 0) ? false : this.component;
18147         this.hasInput = this.component && this.inputEl().length;
18148         
18149         if (typeof(this.minViewMode === 'string')) {
18150             switch (this.minViewMode) {
18151                 case 'months':
18152                     this.minViewMode = 1;
18153                     break;
18154                 case 'years':
18155                     this.minViewMode = 2;
18156                     break;
18157                 default:
18158                     this.minViewMode = 0;
18159                     break;
18160             }
18161         }
18162         
18163         if (typeof(this.viewMode === 'string')) {
18164             switch (this.viewMode) {
18165                 case 'months':
18166                     this.viewMode = 1;
18167                     break;
18168                 case 'years':
18169                     this.viewMode = 2;
18170                     break;
18171                 default:
18172                     this.viewMode = 0;
18173                     break;
18174             }
18175         }
18176                 
18177         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18178         
18179 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18180         
18181         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18182         
18183         this.picker().on('mousedown', this.onMousedown, this);
18184         this.picker().on('click', this.onClick, this);
18185         
18186         this.picker().addClass('datepicker-dropdown');
18187         
18188         this.startViewMode = this.viewMode;
18189         
18190         if(this.singleMode){
18191             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18192                 v.setVisibilityMode(Roo.Element.DISPLAY);
18193                 v.hide();
18194             });
18195             
18196             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18197                 v.setStyle('width', '189px');
18198             });
18199         }
18200         
18201         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18202             if(!this.calendarWeeks){
18203                 v.remove();
18204                 return;
18205             }
18206             
18207             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18208             v.attr('colspan', function(i, val){
18209                 return parseInt(val) + 1;
18210             });
18211         });
18212                         
18213         
18214         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18215         
18216         this.setStartDate(this.startDate);
18217         this.setEndDate(this.endDate);
18218         
18219         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18220         
18221         this.fillDow();
18222         this.fillMonths();
18223         this.update();
18224         this.showMode();
18225         
18226         if(this.isInline) {
18227             this.show();
18228         }
18229     },
18230     
18231     picker : function()
18232     {
18233         return this.pickerEl;
18234 //        return this.el.select('.datepicker', true).first();
18235     },
18236     
18237     fillDow: function()
18238     {
18239         var dowCnt = this.weekStart;
18240         
18241         var dow = {
18242             tag: 'tr',
18243             cn: [
18244                 
18245             ]
18246         };
18247         
18248         if(this.calendarWeeks){
18249             dow.cn.push({
18250                 tag: 'th',
18251                 cls: 'cw',
18252                 html: '&nbsp;'
18253             })
18254         }
18255         
18256         while (dowCnt < this.weekStart + 7) {
18257             dow.cn.push({
18258                 tag: 'th',
18259                 cls: 'dow',
18260                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18261             });
18262         }
18263         
18264         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18265     },
18266     
18267     fillMonths: function()
18268     {    
18269         var i = 0;
18270         var months = this.picker().select('>.datepicker-months td', true).first();
18271         
18272         months.dom.innerHTML = '';
18273         
18274         while (i < 12) {
18275             var month = {
18276                 tag: 'span',
18277                 cls: 'month',
18278                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18279             };
18280             
18281             months.createChild(month);
18282         }
18283         
18284     },
18285     
18286     update: function()
18287     {
18288         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;
18289         
18290         if (this.date < this.startDate) {
18291             this.viewDate = new Date(this.startDate);
18292         } else if (this.date > this.endDate) {
18293             this.viewDate = new Date(this.endDate);
18294         } else {
18295             this.viewDate = new Date(this.date);
18296         }
18297         
18298         this.fill();
18299     },
18300     
18301     fill: function() 
18302     {
18303         var d = new Date(this.viewDate),
18304                 year = d.getUTCFullYear(),
18305                 month = d.getUTCMonth(),
18306                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18307                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18308                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18309                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18310                 currentDate = this.date && this.date.valueOf(),
18311                 today = this.UTCToday();
18312         
18313         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18314         
18315 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18316         
18317 //        this.picker.select('>tfoot th.today').
18318 //                                              .text(dates[this.language].today)
18319 //                                              .toggle(this.todayBtn !== false);
18320     
18321         this.updateNavArrows();
18322         this.fillMonths();
18323                                                 
18324         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18325         
18326         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18327          
18328         prevMonth.setUTCDate(day);
18329         
18330         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18331         
18332         var nextMonth = new Date(prevMonth);
18333         
18334         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18335         
18336         nextMonth = nextMonth.valueOf();
18337         
18338         var fillMonths = false;
18339         
18340         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18341         
18342         while(prevMonth.valueOf() < nextMonth) {
18343             var clsName = '';
18344             
18345             if (prevMonth.getUTCDay() === this.weekStart) {
18346                 if(fillMonths){
18347                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18348                 }
18349                     
18350                 fillMonths = {
18351                     tag: 'tr',
18352                     cn: []
18353                 };
18354                 
18355                 if(this.calendarWeeks){
18356                     // ISO 8601: First week contains first thursday.
18357                     // ISO also states week starts on Monday, but we can be more abstract here.
18358                     var
18359                     // Start of current week: based on weekstart/current date
18360                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18361                     // Thursday of this week
18362                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18363                     // First Thursday of year, year from thursday
18364                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18365                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18366                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18367                     
18368                     fillMonths.cn.push({
18369                         tag: 'td',
18370                         cls: 'cw',
18371                         html: calWeek
18372                     });
18373                 }
18374             }
18375             
18376             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18377                 clsName += ' old';
18378             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18379                 clsName += ' new';
18380             }
18381             if (this.todayHighlight &&
18382                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18383                 prevMonth.getUTCMonth() == today.getMonth() &&
18384                 prevMonth.getUTCDate() == today.getDate()) {
18385                 clsName += ' today';
18386             }
18387             
18388             if (currentDate && prevMonth.valueOf() === currentDate) {
18389                 clsName += ' active';
18390             }
18391             
18392             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18393                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18394                     clsName += ' disabled';
18395             }
18396             
18397             fillMonths.cn.push({
18398                 tag: 'td',
18399                 cls: 'day ' + clsName,
18400                 html: prevMonth.getDate()
18401             });
18402             
18403             prevMonth.setDate(prevMonth.getDate()+1);
18404         }
18405           
18406         var currentYear = this.date && this.date.getUTCFullYear();
18407         var currentMonth = this.date && this.date.getUTCMonth();
18408         
18409         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18410         
18411         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18412             v.removeClass('active');
18413             
18414             if(currentYear === year && k === currentMonth){
18415                 v.addClass('active');
18416             }
18417             
18418             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18419                 v.addClass('disabled');
18420             }
18421             
18422         });
18423         
18424         
18425         year = parseInt(year/10, 10) * 10;
18426         
18427         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18428         
18429         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18430         
18431         year -= 1;
18432         for (var i = -1; i < 11; i++) {
18433             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18434                 tag: 'span',
18435                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18436                 html: year
18437             });
18438             
18439             year += 1;
18440         }
18441     },
18442     
18443     showMode: function(dir) 
18444     {
18445         if (dir) {
18446             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18447         }
18448         
18449         Roo.each(this.picker().select('>div',true).elements, function(v){
18450             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18451             v.hide();
18452         });
18453         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18454     },
18455     
18456     place: function()
18457     {
18458         if(this.isInline) {
18459             return;
18460         }
18461         
18462         this.picker().removeClass(['bottom', 'top']);
18463         
18464         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18465             /*
18466              * place to the top of element!
18467              *
18468              */
18469             
18470             this.picker().addClass('top');
18471             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18472             
18473             return;
18474         }
18475         
18476         this.picker().addClass('bottom');
18477         
18478         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18479     },
18480     
18481     parseDate : function(value)
18482     {
18483         if(!value || value instanceof Date){
18484             return value;
18485         }
18486         var v = Date.parseDate(value, this.format);
18487         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18488             v = Date.parseDate(value, 'Y-m-d');
18489         }
18490         if(!v && this.altFormats){
18491             if(!this.altFormatsArray){
18492                 this.altFormatsArray = this.altFormats.split("|");
18493             }
18494             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18495                 v = Date.parseDate(value, this.altFormatsArray[i]);
18496             }
18497         }
18498         return v;
18499     },
18500     
18501     formatDate : function(date, fmt)
18502     {   
18503         return (!date || !(date instanceof Date)) ?
18504         date : date.dateFormat(fmt || this.format);
18505     },
18506     
18507     onFocus : function()
18508     {
18509         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18510         this.show();
18511     },
18512     
18513     onBlur : function()
18514     {
18515         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18516         
18517         var d = this.inputEl().getValue();
18518         
18519         this.setValue(d);
18520                 
18521         this.hide();
18522     },
18523     
18524     show : function()
18525     {
18526         this.picker().show();
18527         this.update();
18528         this.place();
18529         
18530         this.fireEvent('show', this, this.date);
18531     },
18532     
18533     hide : function()
18534     {
18535         if(this.isInline) {
18536             return;
18537         }
18538         this.picker().hide();
18539         this.viewMode = this.startViewMode;
18540         this.showMode();
18541         
18542         this.fireEvent('hide', this, this.date);
18543         
18544     },
18545     
18546     onMousedown: function(e)
18547     {
18548         e.stopPropagation();
18549         e.preventDefault();
18550     },
18551     
18552     keyup: function(e)
18553     {
18554         Roo.bootstrap.DateField.superclass.keyup.call(this);
18555         this.update();
18556     },
18557
18558     setValue: function(v)
18559     {
18560         if(this.fireEvent('beforeselect', this, v) !== false){
18561             var d = new Date(this.parseDate(v) ).clearTime();
18562         
18563             if(isNaN(d.getTime())){
18564                 this.date = this.viewDate = '';
18565                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18566                 return;
18567             }
18568
18569             v = this.formatDate(d);
18570
18571             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18572
18573             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18574
18575             this.update();
18576
18577             this.fireEvent('select', this, this.date);
18578         }
18579     },
18580     
18581     getValue: function()
18582     {
18583         return this.formatDate(this.date);
18584     },
18585     
18586     fireKey: function(e)
18587     {
18588         if (!this.picker().isVisible()){
18589             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18590                 this.show();
18591             }
18592             return;
18593         }
18594         
18595         var dateChanged = false,
18596         dir, day, month,
18597         newDate, newViewDate;
18598         
18599         switch(e.keyCode){
18600             case 27: // escape
18601                 this.hide();
18602                 e.preventDefault();
18603                 break;
18604             case 37: // left
18605             case 39: // right
18606                 if (!this.keyboardNavigation) {
18607                     break;
18608                 }
18609                 dir = e.keyCode == 37 ? -1 : 1;
18610                 
18611                 if (e.ctrlKey){
18612                     newDate = this.moveYear(this.date, dir);
18613                     newViewDate = this.moveYear(this.viewDate, dir);
18614                 } else if (e.shiftKey){
18615                     newDate = this.moveMonth(this.date, dir);
18616                     newViewDate = this.moveMonth(this.viewDate, dir);
18617                 } else {
18618                     newDate = new Date(this.date);
18619                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18620                     newViewDate = new Date(this.viewDate);
18621                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18622                 }
18623                 if (this.dateWithinRange(newDate)){
18624                     this.date = newDate;
18625                     this.viewDate = newViewDate;
18626                     this.setValue(this.formatDate(this.date));
18627 //                    this.update();
18628                     e.preventDefault();
18629                     dateChanged = true;
18630                 }
18631                 break;
18632             case 38: // up
18633             case 40: // down
18634                 if (!this.keyboardNavigation) {
18635                     break;
18636                 }
18637                 dir = e.keyCode == 38 ? -1 : 1;
18638                 if (e.ctrlKey){
18639                     newDate = this.moveYear(this.date, dir);
18640                     newViewDate = this.moveYear(this.viewDate, dir);
18641                 } else if (e.shiftKey){
18642                     newDate = this.moveMonth(this.date, dir);
18643                     newViewDate = this.moveMonth(this.viewDate, dir);
18644                 } else {
18645                     newDate = new Date(this.date);
18646                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18647                     newViewDate = new Date(this.viewDate);
18648                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18649                 }
18650                 if (this.dateWithinRange(newDate)){
18651                     this.date = newDate;
18652                     this.viewDate = newViewDate;
18653                     this.setValue(this.formatDate(this.date));
18654 //                    this.update();
18655                     e.preventDefault();
18656                     dateChanged = true;
18657                 }
18658                 break;
18659             case 13: // enter
18660                 this.setValue(this.formatDate(this.date));
18661                 this.hide();
18662                 e.preventDefault();
18663                 break;
18664             case 9: // tab
18665                 this.setValue(this.formatDate(this.date));
18666                 this.hide();
18667                 break;
18668             case 16: // shift
18669             case 17: // ctrl
18670             case 18: // alt
18671                 break;
18672             default :
18673                 this.hide();
18674                 
18675         }
18676     },
18677     
18678     
18679     onClick: function(e) 
18680     {
18681         e.stopPropagation();
18682         e.preventDefault();
18683         
18684         var target = e.getTarget();
18685         
18686         if(target.nodeName.toLowerCase() === 'i'){
18687             target = Roo.get(target).dom.parentNode;
18688         }
18689         
18690         var nodeName = target.nodeName;
18691         var className = target.className;
18692         var html = target.innerHTML;
18693         //Roo.log(nodeName);
18694         
18695         switch(nodeName.toLowerCase()) {
18696             case 'th':
18697                 switch(className) {
18698                     case 'switch':
18699                         this.showMode(1);
18700                         break;
18701                     case 'prev':
18702                     case 'next':
18703                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18704                         switch(this.viewMode){
18705                                 case 0:
18706                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18707                                         break;
18708                                 case 1:
18709                                 case 2:
18710                                         this.viewDate = this.moveYear(this.viewDate, dir);
18711                                         break;
18712                         }
18713                         this.fill();
18714                         break;
18715                     case 'today':
18716                         var date = new Date();
18717                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18718 //                        this.fill()
18719                         this.setValue(this.formatDate(this.date));
18720                         
18721                         this.hide();
18722                         break;
18723                 }
18724                 break;
18725             case 'span':
18726                 if (className.indexOf('disabled') < 0) {
18727                     this.viewDate.setUTCDate(1);
18728                     if (className.indexOf('month') > -1) {
18729                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18730                     } else {
18731                         var year = parseInt(html, 10) || 0;
18732                         this.viewDate.setUTCFullYear(year);
18733                         
18734                     }
18735                     
18736                     if(this.singleMode){
18737                         this.setValue(this.formatDate(this.viewDate));
18738                         this.hide();
18739                         return;
18740                     }
18741                     
18742                     this.showMode(-1);
18743                     this.fill();
18744                 }
18745                 break;
18746                 
18747             case 'td':
18748                 //Roo.log(className);
18749                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18750                     var day = parseInt(html, 10) || 1;
18751                     var year = this.viewDate.getUTCFullYear(),
18752                         month = this.viewDate.getUTCMonth();
18753
18754                     if (className.indexOf('old') > -1) {
18755                         if(month === 0 ){
18756                             month = 11;
18757                             year -= 1;
18758                         }else{
18759                             month -= 1;
18760                         }
18761                     } else if (className.indexOf('new') > -1) {
18762                         if (month == 11) {
18763                             month = 0;
18764                             year += 1;
18765                         } else {
18766                             month += 1;
18767                         }
18768                     }
18769                     //Roo.log([year,month,day]);
18770                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18771                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18772 //                    this.fill();
18773                     //Roo.log(this.formatDate(this.date));
18774                     this.setValue(this.formatDate(this.date));
18775                     this.hide();
18776                 }
18777                 break;
18778         }
18779     },
18780     
18781     setStartDate: function(startDate)
18782     {
18783         this.startDate = startDate || -Infinity;
18784         if (this.startDate !== -Infinity) {
18785             this.startDate = this.parseDate(this.startDate);
18786         }
18787         this.update();
18788         this.updateNavArrows();
18789     },
18790
18791     setEndDate: function(endDate)
18792     {
18793         this.endDate = endDate || Infinity;
18794         if (this.endDate !== Infinity) {
18795             this.endDate = this.parseDate(this.endDate);
18796         }
18797         this.update();
18798         this.updateNavArrows();
18799     },
18800     
18801     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18802     {
18803         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18804         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18805             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18806         }
18807         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18808             return parseInt(d, 10);
18809         });
18810         this.update();
18811         this.updateNavArrows();
18812     },
18813     
18814     updateNavArrows: function() 
18815     {
18816         if(this.singleMode){
18817             return;
18818         }
18819         
18820         var d = new Date(this.viewDate),
18821         year = d.getUTCFullYear(),
18822         month = d.getUTCMonth();
18823         
18824         Roo.each(this.picker().select('.prev', true).elements, function(v){
18825             v.show();
18826             switch (this.viewMode) {
18827                 case 0:
18828
18829                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18830                         v.hide();
18831                     }
18832                     break;
18833                 case 1:
18834                 case 2:
18835                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18836                         v.hide();
18837                     }
18838                     break;
18839             }
18840         });
18841         
18842         Roo.each(this.picker().select('.next', true).elements, function(v){
18843             v.show();
18844             switch (this.viewMode) {
18845                 case 0:
18846
18847                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18848                         v.hide();
18849                     }
18850                     break;
18851                 case 1:
18852                 case 2:
18853                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18854                         v.hide();
18855                     }
18856                     break;
18857             }
18858         })
18859     },
18860     
18861     moveMonth: function(date, dir)
18862     {
18863         if (!dir) {
18864             return date;
18865         }
18866         var new_date = new Date(date.valueOf()),
18867         day = new_date.getUTCDate(),
18868         month = new_date.getUTCMonth(),
18869         mag = Math.abs(dir),
18870         new_month, test;
18871         dir = dir > 0 ? 1 : -1;
18872         if (mag == 1){
18873             test = dir == -1
18874             // If going back one month, make sure month is not current month
18875             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18876             ? function(){
18877                 return new_date.getUTCMonth() == month;
18878             }
18879             // If going forward one month, make sure month is as expected
18880             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18881             : function(){
18882                 return new_date.getUTCMonth() != new_month;
18883             };
18884             new_month = month + dir;
18885             new_date.setUTCMonth(new_month);
18886             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18887             if (new_month < 0 || new_month > 11) {
18888                 new_month = (new_month + 12) % 12;
18889             }
18890         } else {
18891             // For magnitudes >1, move one month at a time...
18892             for (var i=0; i<mag; i++) {
18893                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18894                 new_date = this.moveMonth(new_date, dir);
18895             }
18896             // ...then reset the day, keeping it in the new month
18897             new_month = new_date.getUTCMonth();
18898             new_date.setUTCDate(day);
18899             test = function(){
18900                 return new_month != new_date.getUTCMonth();
18901             };
18902         }
18903         // Common date-resetting loop -- if date is beyond end of month, make it
18904         // end of month
18905         while (test()){
18906             new_date.setUTCDate(--day);
18907             new_date.setUTCMonth(new_month);
18908         }
18909         return new_date;
18910     },
18911
18912     moveYear: function(date, dir)
18913     {
18914         return this.moveMonth(date, dir*12);
18915     },
18916
18917     dateWithinRange: function(date)
18918     {
18919         return date >= this.startDate && date <= this.endDate;
18920     },
18921
18922     
18923     remove: function() 
18924     {
18925         this.picker().remove();
18926     },
18927     
18928     validateValue : function(value)
18929     {
18930         if(value.length < 1)  {
18931             if(this.allowBlank){
18932                 return true;
18933             }
18934             return false;
18935         }
18936         
18937         if(value.length < this.minLength){
18938             return false;
18939         }
18940         if(value.length > this.maxLength){
18941             return false;
18942         }
18943         if(this.vtype){
18944             var vt = Roo.form.VTypes;
18945             if(!vt[this.vtype](value, this)){
18946                 return false;
18947             }
18948         }
18949         if(typeof this.validator == "function"){
18950             var msg = this.validator(value);
18951             if(msg !== true){
18952                 return false;
18953             }
18954         }
18955         
18956         if(this.regex && !this.regex.test(value)){
18957             return false;
18958         }
18959         
18960         if(typeof(this.parseDate(value)) == 'undefined'){
18961             return false;
18962         }
18963         
18964         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18965             return false;
18966         }      
18967         
18968         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18969             return false;
18970         } 
18971         
18972         
18973         return true;
18974     }
18975    
18976 });
18977
18978 Roo.apply(Roo.bootstrap.DateField,  {
18979     
18980     head : {
18981         tag: 'thead',
18982         cn: [
18983         {
18984             tag: 'tr',
18985             cn: [
18986             {
18987                 tag: 'th',
18988                 cls: 'prev',
18989                 html: '<i class="fa fa-arrow-left"/>'
18990             },
18991             {
18992                 tag: 'th',
18993                 cls: 'switch',
18994                 colspan: '5'
18995             },
18996             {
18997                 tag: 'th',
18998                 cls: 'next',
18999                 html: '<i class="fa fa-arrow-right"/>'
19000             }
19001
19002             ]
19003         }
19004         ]
19005     },
19006     
19007     content : {
19008         tag: 'tbody',
19009         cn: [
19010         {
19011             tag: 'tr',
19012             cn: [
19013             {
19014                 tag: 'td',
19015                 colspan: '7'
19016             }
19017             ]
19018         }
19019         ]
19020     },
19021     
19022     footer : {
19023         tag: 'tfoot',
19024         cn: [
19025         {
19026             tag: 'tr',
19027             cn: [
19028             {
19029                 tag: 'th',
19030                 colspan: '7',
19031                 cls: 'today'
19032             }
19033                     
19034             ]
19035         }
19036         ]
19037     },
19038     
19039     dates:{
19040         en: {
19041             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19042             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19043             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19044             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19045             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19046             today: "Today"
19047         }
19048     },
19049     
19050     modes: [
19051     {
19052         clsName: 'days',
19053         navFnc: 'Month',
19054         navStep: 1
19055     },
19056     {
19057         clsName: 'months',
19058         navFnc: 'FullYear',
19059         navStep: 1
19060     },
19061     {
19062         clsName: 'years',
19063         navFnc: 'FullYear',
19064         navStep: 10
19065     }]
19066 });
19067
19068 Roo.apply(Roo.bootstrap.DateField,  {
19069   
19070     template : {
19071         tag: 'div',
19072         cls: 'datepicker dropdown-menu roo-dynamic',
19073         cn: [
19074         {
19075             tag: 'div',
19076             cls: 'datepicker-days',
19077             cn: [
19078             {
19079                 tag: 'table',
19080                 cls: 'table-condensed',
19081                 cn:[
19082                 Roo.bootstrap.DateField.head,
19083                 {
19084                     tag: 'tbody'
19085                 },
19086                 Roo.bootstrap.DateField.footer
19087                 ]
19088             }
19089             ]
19090         },
19091         {
19092             tag: 'div',
19093             cls: 'datepicker-months',
19094             cn: [
19095             {
19096                 tag: 'table',
19097                 cls: 'table-condensed',
19098                 cn:[
19099                 Roo.bootstrap.DateField.head,
19100                 Roo.bootstrap.DateField.content,
19101                 Roo.bootstrap.DateField.footer
19102                 ]
19103             }
19104             ]
19105         },
19106         {
19107             tag: 'div',
19108             cls: 'datepicker-years',
19109             cn: [
19110             {
19111                 tag: 'table',
19112                 cls: 'table-condensed',
19113                 cn:[
19114                 Roo.bootstrap.DateField.head,
19115                 Roo.bootstrap.DateField.content,
19116                 Roo.bootstrap.DateField.footer
19117                 ]
19118             }
19119             ]
19120         }
19121         ]
19122     }
19123 });
19124
19125  
19126
19127  /*
19128  * - LGPL
19129  *
19130  * TimeField
19131  * 
19132  */
19133
19134 /**
19135  * @class Roo.bootstrap.TimeField
19136  * @extends Roo.bootstrap.Input
19137  * Bootstrap DateField class
19138  * 
19139  * 
19140  * @constructor
19141  * Create a new TimeField
19142  * @param {Object} config The config object
19143  */
19144
19145 Roo.bootstrap.TimeField = function(config){
19146     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19147     this.addEvents({
19148             /**
19149              * @event show
19150              * Fires when this field show.
19151              * @param {Roo.bootstrap.DateField} thisthis
19152              * @param {Mixed} date The date value
19153              */
19154             show : true,
19155             /**
19156              * @event show
19157              * Fires when this field hide.
19158              * @param {Roo.bootstrap.DateField} this
19159              * @param {Mixed} date The date value
19160              */
19161             hide : true,
19162             /**
19163              * @event select
19164              * Fires when select a date.
19165              * @param {Roo.bootstrap.DateField} this
19166              * @param {Mixed} date The date value
19167              */
19168             select : true
19169         });
19170 };
19171
19172 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19173     
19174     /**
19175      * @cfg {String} format
19176      * The default time format string which can be overriden for localization support.  The format must be
19177      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19178      */
19179     format : "H:i",
19180        
19181     onRender: function(ct, position)
19182     {
19183         
19184         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19185                 
19186         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19187         
19188         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19189         
19190         this.pop = this.picker().select('>.datepicker-time',true).first();
19191         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19192         
19193         this.picker().on('mousedown', this.onMousedown, this);
19194         this.picker().on('click', this.onClick, this);
19195         
19196         this.picker().addClass('datepicker-dropdown');
19197     
19198         this.fillTime();
19199         this.update();
19200             
19201         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19202         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19203         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19204         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19205         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19206         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19207
19208     },
19209     
19210     fireKey: function(e){
19211         if (!this.picker().isVisible()){
19212             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19213                 this.show();
19214             }
19215             return;
19216         }
19217
19218         e.preventDefault();
19219         
19220         switch(e.keyCode){
19221             case 27: // escape
19222                 this.hide();
19223                 break;
19224             case 37: // left
19225             case 39: // right
19226                 this.onTogglePeriod();
19227                 break;
19228             case 38: // up
19229                 this.onIncrementMinutes();
19230                 break;
19231             case 40: // down
19232                 this.onDecrementMinutes();
19233                 break;
19234             case 13: // enter
19235             case 9: // tab
19236                 this.setTime();
19237                 break;
19238         }
19239     },
19240     
19241     onClick: function(e) {
19242         e.stopPropagation();
19243         e.preventDefault();
19244     },
19245     
19246     picker : function()
19247     {
19248         return this.el.select('.datepicker', true).first();
19249     },
19250     
19251     fillTime: function()
19252     {    
19253         var time = this.pop.select('tbody', true).first();
19254         
19255         time.dom.innerHTML = '';
19256         
19257         time.createChild({
19258             tag: 'tr',
19259             cn: [
19260                 {
19261                     tag: 'td',
19262                     cn: [
19263                         {
19264                             tag: 'a',
19265                             href: '#',
19266                             cls: 'btn',
19267                             cn: [
19268                                 {
19269                                     tag: 'span',
19270                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19271                                 }
19272                             ]
19273                         } 
19274                     ]
19275                 },
19276                 {
19277                     tag: 'td',
19278                     cls: 'separator'
19279                 },
19280                 {
19281                     tag: 'td',
19282                     cn: [
19283                         {
19284                             tag: 'a',
19285                             href: '#',
19286                             cls: 'btn',
19287                             cn: [
19288                                 {
19289                                     tag: 'span',
19290                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19291                                 }
19292                             ]
19293                         }
19294                     ]
19295                 },
19296                 {
19297                     tag: 'td',
19298                     cls: 'separator'
19299                 }
19300             ]
19301         });
19302         
19303         time.createChild({
19304             tag: 'tr',
19305             cn: [
19306                 {
19307                     tag: 'td',
19308                     cn: [
19309                         {
19310                             tag: 'span',
19311                             cls: 'timepicker-hour',
19312                             html: '00'
19313                         }  
19314                     ]
19315                 },
19316                 {
19317                     tag: 'td',
19318                     cls: 'separator',
19319                     html: ':'
19320                 },
19321                 {
19322                     tag: 'td',
19323                     cn: [
19324                         {
19325                             tag: 'span',
19326                             cls: 'timepicker-minute',
19327                             html: '00'
19328                         }  
19329                     ]
19330                 },
19331                 {
19332                     tag: 'td',
19333                     cls: 'separator'
19334                 },
19335                 {
19336                     tag: 'td',
19337                     cn: [
19338                         {
19339                             tag: 'button',
19340                             type: 'button',
19341                             cls: 'btn btn-primary period',
19342                             html: 'AM'
19343                             
19344                         }
19345                     ]
19346                 }
19347             ]
19348         });
19349         
19350         time.createChild({
19351             tag: 'tr',
19352             cn: [
19353                 {
19354                     tag: 'td',
19355                     cn: [
19356                         {
19357                             tag: 'a',
19358                             href: '#',
19359                             cls: 'btn',
19360                             cn: [
19361                                 {
19362                                     tag: 'span',
19363                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19364                                 }
19365                             ]
19366                         }
19367                     ]
19368                 },
19369                 {
19370                     tag: 'td',
19371                     cls: 'separator'
19372                 },
19373                 {
19374                     tag: 'td',
19375                     cn: [
19376                         {
19377                             tag: 'a',
19378                             href: '#',
19379                             cls: 'btn',
19380                             cn: [
19381                                 {
19382                                     tag: 'span',
19383                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19384                                 }
19385                             ]
19386                         }
19387                     ]
19388                 },
19389                 {
19390                     tag: 'td',
19391                     cls: 'separator'
19392                 }
19393             ]
19394         });
19395         
19396     },
19397     
19398     update: function()
19399     {
19400         
19401         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19402         
19403         this.fill();
19404     },
19405     
19406     fill: function() 
19407     {
19408         var hours = this.time.getHours();
19409         var minutes = this.time.getMinutes();
19410         var period = 'AM';
19411         
19412         if(hours > 11){
19413             period = 'PM';
19414         }
19415         
19416         if(hours == 0){
19417             hours = 12;
19418         }
19419         
19420         
19421         if(hours > 12){
19422             hours = hours - 12;
19423         }
19424         
19425         if(hours < 10){
19426             hours = '0' + hours;
19427         }
19428         
19429         if(minutes < 10){
19430             minutes = '0' + minutes;
19431         }
19432         
19433         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19434         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19435         this.pop.select('button', true).first().dom.innerHTML = period;
19436         
19437     },
19438     
19439     place: function()
19440     {   
19441         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19442         
19443         var cls = ['bottom'];
19444         
19445         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19446             cls.pop();
19447             cls.push('top');
19448         }
19449         
19450         cls.push('right');
19451         
19452         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19453             cls.pop();
19454             cls.push('left');
19455         }
19456         
19457         this.picker().addClass(cls.join('-'));
19458         
19459         var _this = this;
19460         
19461         Roo.each(cls, function(c){
19462             if(c == 'bottom'){
19463                 _this.picker().setTop(_this.inputEl().getHeight());
19464                 return;
19465             }
19466             if(c == 'top'){
19467                 _this.picker().setTop(0 - _this.picker().getHeight());
19468                 return;
19469             }
19470             
19471             if(c == 'left'){
19472                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19473                 return;
19474             }
19475             if(c == 'right'){
19476                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19477                 return;
19478             }
19479         });
19480         
19481     },
19482   
19483     onFocus : function()
19484     {
19485         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19486         this.show();
19487     },
19488     
19489     onBlur : function()
19490     {
19491         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19492         this.hide();
19493     },
19494     
19495     show : function()
19496     {
19497         this.picker().show();
19498         this.pop.show();
19499         this.update();
19500         this.place();
19501         
19502         this.fireEvent('show', this, this.date);
19503     },
19504     
19505     hide : function()
19506     {
19507         this.picker().hide();
19508         this.pop.hide();
19509         
19510         this.fireEvent('hide', this, this.date);
19511     },
19512     
19513     setTime : function()
19514     {
19515         this.hide();
19516         this.setValue(this.time.format(this.format));
19517         
19518         this.fireEvent('select', this, this.date);
19519         
19520         
19521     },
19522     
19523     onMousedown: function(e){
19524         e.stopPropagation();
19525         e.preventDefault();
19526     },
19527     
19528     onIncrementHours: function()
19529     {
19530         Roo.log('onIncrementHours');
19531         this.time = this.time.add(Date.HOUR, 1);
19532         this.update();
19533         
19534     },
19535     
19536     onDecrementHours: function()
19537     {
19538         Roo.log('onDecrementHours');
19539         this.time = this.time.add(Date.HOUR, -1);
19540         this.update();
19541     },
19542     
19543     onIncrementMinutes: function()
19544     {
19545         Roo.log('onIncrementMinutes');
19546         this.time = this.time.add(Date.MINUTE, 1);
19547         this.update();
19548     },
19549     
19550     onDecrementMinutes: function()
19551     {
19552         Roo.log('onDecrementMinutes');
19553         this.time = this.time.add(Date.MINUTE, -1);
19554         this.update();
19555     },
19556     
19557     onTogglePeriod: function()
19558     {
19559         Roo.log('onTogglePeriod');
19560         this.time = this.time.add(Date.HOUR, 12);
19561         this.update();
19562     }
19563     
19564    
19565 });
19566
19567 Roo.apply(Roo.bootstrap.TimeField,  {
19568     
19569     content : {
19570         tag: 'tbody',
19571         cn: [
19572             {
19573                 tag: 'tr',
19574                 cn: [
19575                 {
19576                     tag: 'td',
19577                     colspan: '7'
19578                 }
19579                 ]
19580             }
19581         ]
19582     },
19583     
19584     footer : {
19585         tag: 'tfoot',
19586         cn: [
19587             {
19588                 tag: 'tr',
19589                 cn: [
19590                 {
19591                     tag: 'th',
19592                     colspan: '7',
19593                     cls: '',
19594                     cn: [
19595                         {
19596                             tag: 'button',
19597                             cls: 'btn btn-info ok',
19598                             html: 'OK'
19599                         }
19600                     ]
19601                 }
19602
19603                 ]
19604             }
19605         ]
19606     }
19607 });
19608
19609 Roo.apply(Roo.bootstrap.TimeField,  {
19610   
19611     template : {
19612         tag: 'div',
19613         cls: 'datepicker dropdown-menu',
19614         cn: [
19615             {
19616                 tag: 'div',
19617                 cls: 'datepicker-time',
19618                 cn: [
19619                 {
19620                     tag: 'table',
19621                     cls: 'table-condensed',
19622                     cn:[
19623                     Roo.bootstrap.TimeField.content,
19624                     Roo.bootstrap.TimeField.footer
19625                     ]
19626                 }
19627                 ]
19628             }
19629         ]
19630     }
19631 });
19632
19633  
19634
19635  /*
19636  * - LGPL
19637  *
19638  * MonthField
19639  * 
19640  */
19641
19642 /**
19643  * @class Roo.bootstrap.MonthField
19644  * @extends Roo.bootstrap.Input
19645  * Bootstrap MonthField class
19646  * 
19647  * @cfg {String} language default en
19648  * 
19649  * @constructor
19650  * Create a new MonthField
19651  * @param {Object} config The config object
19652  */
19653
19654 Roo.bootstrap.MonthField = function(config){
19655     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19656     
19657     this.addEvents({
19658         /**
19659          * @event show
19660          * Fires when this field show.
19661          * @param {Roo.bootstrap.MonthField} this
19662          * @param {Mixed} date The date value
19663          */
19664         show : true,
19665         /**
19666          * @event show
19667          * Fires when this field hide.
19668          * @param {Roo.bootstrap.MonthField} this
19669          * @param {Mixed} date The date value
19670          */
19671         hide : true,
19672         /**
19673          * @event select
19674          * Fires when select a date.
19675          * @param {Roo.bootstrap.MonthField} this
19676          * @param {String} oldvalue The old value
19677          * @param {String} newvalue The new value
19678          */
19679         select : true
19680     });
19681 };
19682
19683 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19684     
19685     onRender: function(ct, position)
19686     {
19687         
19688         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19689         
19690         this.language = this.language || 'en';
19691         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19692         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19693         
19694         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19695         this.isInline = false;
19696         this.isInput = true;
19697         this.component = this.el.select('.add-on', true).first() || false;
19698         this.component = (this.component && this.component.length === 0) ? false : this.component;
19699         this.hasInput = this.component && this.inputEL().length;
19700         
19701         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19702         
19703         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704         
19705         this.picker().on('mousedown', this.onMousedown, this);
19706         this.picker().on('click', this.onClick, this);
19707         
19708         this.picker().addClass('datepicker-dropdown');
19709         
19710         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19711             v.setStyle('width', '189px');
19712         });
19713         
19714         this.fillMonths();
19715         
19716         this.update();
19717         
19718         if(this.isInline) {
19719             this.show();
19720         }
19721         
19722     },
19723     
19724     setValue: function(v, suppressEvent)
19725     {   
19726         var o = this.getValue();
19727         
19728         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19729         
19730         this.update();
19731
19732         if(suppressEvent !== true){
19733             this.fireEvent('select', this, o, v);
19734         }
19735         
19736     },
19737     
19738     getValue: function()
19739     {
19740         return this.value;
19741     },
19742     
19743     onClick: function(e) 
19744     {
19745         e.stopPropagation();
19746         e.preventDefault();
19747         
19748         var target = e.getTarget();
19749         
19750         if(target.nodeName.toLowerCase() === 'i'){
19751             target = Roo.get(target).dom.parentNode;
19752         }
19753         
19754         var nodeName = target.nodeName;
19755         var className = target.className;
19756         var html = target.innerHTML;
19757         
19758         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19759             return;
19760         }
19761         
19762         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19763         
19764         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19765         
19766         this.hide();
19767                         
19768     },
19769     
19770     picker : function()
19771     {
19772         return this.pickerEl;
19773     },
19774     
19775     fillMonths: function()
19776     {    
19777         var i = 0;
19778         var months = this.picker().select('>.datepicker-months td', true).first();
19779         
19780         months.dom.innerHTML = '';
19781         
19782         while (i < 12) {
19783             var month = {
19784                 tag: 'span',
19785                 cls: 'month',
19786                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19787             };
19788             
19789             months.createChild(month);
19790         }
19791         
19792     },
19793     
19794     update: function()
19795     {
19796         var _this = this;
19797         
19798         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19799             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19800         }
19801         
19802         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19803             e.removeClass('active');
19804             
19805             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19806                 e.addClass('active');
19807             }
19808         })
19809     },
19810     
19811     place: function()
19812     {
19813         if(this.isInline) {
19814             return;
19815         }
19816         
19817         this.picker().removeClass(['bottom', 'top']);
19818         
19819         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19820             /*
19821              * place to the top of element!
19822              *
19823              */
19824             
19825             this.picker().addClass('top');
19826             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19827             
19828             return;
19829         }
19830         
19831         this.picker().addClass('bottom');
19832         
19833         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19834     },
19835     
19836     onFocus : function()
19837     {
19838         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19839         this.show();
19840     },
19841     
19842     onBlur : function()
19843     {
19844         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19845         
19846         var d = this.inputEl().getValue();
19847         
19848         this.setValue(d);
19849                 
19850         this.hide();
19851     },
19852     
19853     show : function()
19854     {
19855         this.picker().show();
19856         this.picker().select('>.datepicker-months', true).first().show();
19857         this.update();
19858         this.place();
19859         
19860         this.fireEvent('show', this, this.date);
19861     },
19862     
19863     hide : function()
19864     {
19865         if(this.isInline) {
19866             return;
19867         }
19868         this.picker().hide();
19869         this.fireEvent('hide', this, this.date);
19870         
19871     },
19872     
19873     onMousedown: function(e)
19874     {
19875         e.stopPropagation();
19876         e.preventDefault();
19877     },
19878     
19879     keyup: function(e)
19880     {
19881         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19882         this.update();
19883     },
19884
19885     fireKey: function(e)
19886     {
19887         if (!this.picker().isVisible()){
19888             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19889                 this.show();
19890             }
19891             return;
19892         }
19893         
19894         var dir;
19895         
19896         switch(e.keyCode){
19897             case 27: // escape
19898                 this.hide();
19899                 e.preventDefault();
19900                 break;
19901             case 37: // left
19902             case 39: // right
19903                 dir = e.keyCode == 37 ? -1 : 1;
19904                 
19905                 this.vIndex = this.vIndex + dir;
19906                 
19907                 if(this.vIndex < 0){
19908                     this.vIndex = 0;
19909                 }
19910                 
19911                 if(this.vIndex > 11){
19912                     this.vIndex = 11;
19913                 }
19914                 
19915                 if(isNaN(this.vIndex)){
19916                     this.vIndex = 0;
19917                 }
19918                 
19919                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19920                 
19921                 break;
19922             case 38: // up
19923             case 40: // down
19924                 
19925                 dir = e.keyCode == 38 ? -1 : 1;
19926                 
19927                 this.vIndex = this.vIndex + dir * 4;
19928                 
19929                 if(this.vIndex < 0){
19930                     this.vIndex = 0;
19931                 }
19932                 
19933                 if(this.vIndex > 11){
19934                     this.vIndex = 11;
19935                 }
19936                 
19937                 if(isNaN(this.vIndex)){
19938                     this.vIndex = 0;
19939                 }
19940                 
19941                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19942                 break;
19943                 
19944             case 13: // enter
19945                 
19946                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19947                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19948                 }
19949                 
19950                 this.hide();
19951                 e.preventDefault();
19952                 break;
19953             case 9: // tab
19954                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19955                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19956                 }
19957                 this.hide();
19958                 break;
19959             case 16: // shift
19960             case 17: // ctrl
19961             case 18: // alt
19962                 break;
19963             default :
19964                 this.hide();
19965                 
19966         }
19967     },
19968     
19969     remove: function() 
19970     {
19971         this.picker().remove();
19972     }
19973    
19974 });
19975
19976 Roo.apply(Roo.bootstrap.MonthField,  {
19977     
19978     content : {
19979         tag: 'tbody',
19980         cn: [
19981         {
19982             tag: 'tr',
19983             cn: [
19984             {
19985                 tag: 'td',
19986                 colspan: '7'
19987             }
19988             ]
19989         }
19990         ]
19991     },
19992     
19993     dates:{
19994         en: {
19995             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19996             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19997         }
19998     }
19999 });
20000
20001 Roo.apply(Roo.bootstrap.MonthField,  {
20002   
20003     template : {
20004         tag: 'div',
20005         cls: 'datepicker dropdown-menu roo-dynamic',
20006         cn: [
20007             {
20008                 tag: 'div',
20009                 cls: 'datepicker-months',
20010                 cn: [
20011                 {
20012                     tag: 'table',
20013                     cls: 'table-condensed',
20014                     cn:[
20015                         Roo.bootstrap.DateField.content
20016                     ]
20017                 }
20018                 ]
20019             }
20020         ]
20021     }
20022 });
20023
20024  
20025
20026  
20027  /*
20028  * - LGPL
20029  *
20030  * CheckBox
20031  * 
20032  */
20033
20034 /**
20035  * @class Roo.bootstrap.CheckBox
20036  * @extends Roo.bootstrap.Input
20037  * Bootstrap CheckBox class
20038  * 
20039  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20040  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20041  * @cfg {String} boxLabel The text that appears beside the checkbox
20042  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20043  * @cfg {Boolean} checked initnal the element
20044  * @cfg {Boolean} inline inline the element (default false)
20045  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20046  * 
20047  * @constructor
20048  * Create a new CheckBox
20049  * @param {Object} config The config object
20050  */
20051
20052 Roo.bootstrap.CheckBox = function(config){
20053     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20054    
20055     this.addEvents({
20056         /**
20057         * @event check
20058         * Fires when the element is checked or unchecked.
20059         * @param {Roo.bootstrap.CheckBox} this This input
20060         * @param {Boolean} checked The new checked value
20061         */
20062        check : true
20063     });
20064     
20065 };
20066
20067 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20068   
20069     inputType: 'checkbox',
20070     inputValue: 1,
20071     valueOff: 0,
20072     boxLabel: false,
20073     checked: false,
20074     weight : false,
20075     inline: false,
20076     
20077     getAutoCreate : function()
20078     {
20079         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20080         
20081         var id = Roo.id();
20082         
20083         var cfg = {};
20084         
20085         cfg.cls = 'form-group ' + this.inputType; //input-group
20086         
20087         if(this.inline){
20088             cfg.cls += ' ' + this.inputType + '-inline';
20089         }
20090         
20091         var input =  {
20092             tag: 'input',
20093             id : id,
20094             type : this.inputType,
20095             value : this.inputValue,
20096             cls : 'roo-' + this.inputType, //'form-box',
20097             placeholder : this.placeholder || ''
20098             
20099         };
20100         
20101         if(this.inputType != 'radio'){
20102             var hidden =  {
20103                 tag: 'input',
20104                 type : 'hidden',
20105                 cls : 'roo-hidden-value',
20106                 value : this.checked ? this.valueOff : this.inputValue
20107             };
20108         }
20109         
20110             
20111         if (this.weight) { // Validity check?
20112             cfg.cls += " " + this.inputType + "-" + this.weight;
20113         }
20114         
20115         if (this.disabled) {
20116             input.disabled=true;
20117         }
20118         
20119         if(this.checked){
20120             input.checked = this.checked;
20121             
20122         }
20123         
20124         
20125         if (this.name) {
20126             
20127             input.name = this.name;
20128             
20129             if(this.inputType != 'radio'){
20130                 hidden.name = this.name;
20131                 input.name = '_hidden_' + this.name;
20132             }
20133         }
20134         
20135         if (this.size) {
20136             input.cls += ' input-' + this.size;
20137         }
20138         
20139         var settings=this;
20140         
20141         ['xs','sm','md','lg'].map(function(size){
20142             if (settings[size]) {
20143                 cfg.cls += ' col-' + size + '-' + settings[size];
20144             }
20145         });
20146         
20147         var inputblock = input;
20148          
20149         if (this.before || this.after) {
20150             
20151             inputblock = {
20152                 cls : 'input-group',
20153                 cn :  [] 
20154             };
20155             
20156             if (this.before) {
20157                 inputblock.cn.push({
20158                     tag :'span',
20159                     cls : 'input-group-addon',
20160                     html : this.before
20161                 });
20162             }
20163             
20164             inputblock.cn.push(input);
20165             
20166             if(this.inputType != 'radio'){
20167                 inputblock.cn.push(hidden);
20168             }
20169             
20170             if (this.after) {
20171                 inputblock.cn.push({
20172                     tag :'span',
20173                     cls : 'input-group-addon',
20174                     html : this.after
20175                 });
20176             }
20177             
20178         }
20179         
20180         if (align ==='left' && this.fieldLabel.length) {
20181 //                Roo.log("left and has label");
20182             cfg.cn = [
20183                 {
20184                     tag: 'label',
20185                     'for' :  id,
20186                     cls : 'control-label',
20187                     html : this.fieldLabel
20188
20189                 },
20190                 {
20191                     cls : "", 
20192                     cn: [
20193                         inputblock
20194                     ]
20195                 }
20196             ];
20197             
20198             if(this.labelWidth > 12){
20199                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20200             }
20201             
20202             if(this.labelWidth < 13 && this.labelmd == 0){
20203                 this.labelmd = this.labelWidth;
20204             }
20205             
20206             if(this.labellg > 0){
20207                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20208                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20209             }
20210             
20211             if(this.labelmd > 0){
20212                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20213                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20214             }
20215             
20216             if(this.labelsm > 0){
20217                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20218                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20219             }
20220             
20221             if(this.labelxs > 0){
20222                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20223                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20224             }
20225             
20226         } else if ( this.fieldLabel.length) {
20227 //                Roo.log(" label");
20228                 cfg.cn = [
20229                    
20230                     {
20231                         tag: this.boxLabel ? 'span' : 'label',
20232                         'for': id,
20233                         cls: 'control-label box-input-label',
20234                         //cls : 'input-group-addon',
20235                         html : this.fieldLabel
20236                         
20237                     },
20238                     
20239                     inputblock
20240                     
20241                 ];
20242
20243         } else {
20244             
20245 //                Roo.log(" no label && no align");
20246                 cfg.cn = [  inputblock ] ;
20247                 
20248                 
20249         }
20250         
20251         if(this.boxLabel){
20252              var boxLabelCfg = {
20253                 tag: 'label',
20254                 //'for': id, // box label is handled by onclick - so no for...
20255                 cls: 'box-label',
20256                 html: this.boxLabel
20257             };
20258             
20259             if(this.tooltip){
20260                 boxLabelCfg.tooltip = this.tooltip;
20261             }
20262              
20263             cfg.cn.push(boxLabelCfg);
20264         }
20265         
20266         if(this.inputType != 'radio'){
20267             cfg.cn.push(hidden);
20268         }
20269         
20270         return cfg;
20271         
20272     },
20273     
20274     /**
20275      * return the real input element.
20276      */
20277     inputEl: function ()
20278     {
20279         return this.el.select('input.roo-' + this.inputType,true).first();
20280     },
20281     hiddenEl: function ()
20282     {
20283         return this.el.select('input.roo-hidden-value',true).first();
20284     },
20285     
20286     labelEl: function()
20287     {
20288         return this.el.select('label.control-label',true).first();
20289     },
20290     /* depricated... */
20291     
20292     label: function()
20293     {
20294         return this.labelEl();
20295     },
20296     
20297     boxLabelEl: function()
20298     {
20299         return this.el.select('label.box-label',true).first();
20300     },
20301     
20302     initEvents : function()
20303     {
20304 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20305         
20306         this.inputEl().on('click', this.onClick,  this);
20307         
20308         if (this.boxLabel) { 
20309             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20310         }
20311         
20312         this.startValue = this.getValue();
20313         
20314         if(this.groupId){
20315             Roo.bootstrap.CheckBox.register(this);
20316         }
20317     },
20318     
20319     onClick : function()
20320     {   
20321         this.setChecked(!this.checked);
20322     },
20323     
20324     setChecked : function(state,suppressEvent)
20325     {
20326         this.startValue = this.getValue();
20327
20328         if(this.inputType == 'radio'){
20329             
20330             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20331                 e.dom.checked = false;
20332             });
20333             
20334             this.inputEl().dom.checked = true;
20335             
20336             this.inputEl().dom.value = this.inputValue;
20337             
20338             if(suppressEvent !== true){
20339                 this.fireEvent('check', this, true);
20340             }
20341             
20342             this.validate();
20343             
20344             return;
20345         }
20346         
20347         this.checked = state;
20348         
20349         this.inputEl().dom.checked = state;
20350         
20351         
20352         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20353         
20354         if(suppressEvent !== true){
20355             this.fireEvent('check', this, state);
20356         }
20357         
20358         this.validate();
20359     },
20360     
20361     getValue : function()
20362     {
20363         if(this.inputType == 'radio'){
20364             return this.getGroupValue();
20365         }
20366         
20367         return this.hiddenEl().dom.value;
20368         
20369     },
20370     
20371     getGroupValue : function()
20372     {
20373         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20374             return '';
20375         }
20376         
20377         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20378     },
20379     
20380     setValue : function(v,suppressEvent)
20381     {
20382         if(this.inputType == 'radio'){
20383             this.setGroupValue(v, suppressEvent);
20384             return;
20385         }
20386         
20387         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20388         
20389         this.validate();
20390     },
20391     
20392     setGroupValue : function(v, suppressEvent)
20393     {
20394         this.startValue = this.getValue();
20395         
20396         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20397             e.dom.checked = false;
20398             
20399             if(e.dom.value == v){
20400                 e.dom.checked = true;
20401             }
20402         });
20403         
20404         if(suppressEvent !== true){
20405             this.fireEvent('check', this, true);
20406         }
20407
20408         this.validate();
20409         
20410         return;
20411     },
20412     
20413     validate : function()
20414     {
20415         if(
20416                 this.disabled || 
20417                 (this.inputType == 'radio' && this.validateRadio()) ||
20418                 (this.inputType == 'checkbox' && this.validateCheckbox())
20419         ){
20420             this.markValid();
20421             return true;
20422         }
20423         
20424         this.markInvalid();
20425         return false;
20426     },
20427     
20428     validateRadio : function()
20429     {
20430         if(this.allowBlank){
20431             return true;
20432         }
20433         
20434         var valid = false;
20435         
20436         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20437             if(!e.dom.checked){
20438                 return;
20439             }
20440             
20441             valid = true;
20442             
20443             return false;
20444         });
20445         
20446         return valid;
20447     },
20448     
20449     validateCheckbox : function()
20450     {
20451         if(!this.groupId){
20452             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20453             //return (this.getValue() == this.inputValue) ? true : false;
20454         }
20455         
20456         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20457         
20458         if(!group){
20459             return false;
20460         }
20461         
20462         var r = false;
20463         
20464         for(var i in group){
20465             if(r){
20466                 break;
20467             }
20468             
20469             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20470         }
20471         
20472         return r;
20473     },
20474     
20475     /**
20476      * Mark this field as valid
20477      */
20478     markValid : function()
20479     {
20480         var _this = this;
20481         
20482         this.fireEvent('valid', this);
20483         
20484         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20485         
20486         if(this.groupId){
20487             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20488         }
20489         
20490         if(label){
20491             label.markValid();
20492         }
20493
20494         if(this.inputType == 'radio'){
20495             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20496                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20497                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20498             });
20499             
20500             return;
20501         }
20502
20503         if(!this.groupId){
20504             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20505             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20506             return;
20507         }
20508         
20509         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20510         
20511         if(!group){
20512             return;
20513         }
20514         
20515         for(var i in group){
20516             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20517             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20518         }
20519     },
20520     
20521      /**
20522      * Mark this field as invalid
20523      * @param {String} msg The validation message
20524      */
20525     markInvalid : function(msg)
20526     {
20527         if(this.allowBlank){
20528             return;
20529         }
20530         
20531         var _this = this;
20532         
20533         this.fireEvent('invalid', this, msg);
20534         
20535         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20536         
20537         if(this.groupId){
20538             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20539         }
20540         
20541         if(label){
20542             label.markInvalid();
20543         }
20544             
20545         if(this.inputType == 'radio'){
20546             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20547                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20548                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20549             });
20550             
20551             return;
20552         }
20553         
20554         if(!this.groupId){
20555             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20556             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20557             return;
20558         }
20559         
20560         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20561         
20562         if(!group){
20563             return;
20564         }
20565         
20566         for(var i in group){
20567             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20568             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20569         }
20570         
20571     },
20572     
20573     clearInvalid : function()
20574     {
20575         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20576         
20577         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20578         
20579         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20580         
20581         if (label) {
20582             label.iconEl.removeClass(label.validClass);
20583             label.iconEl.removeClass(label.invalidClass);
20584         }
20585     },
20586     
20587     disable : function()
20588     {
20589         if(this.inputType != 'radio'){
20590             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20591             return;
20592         }
20593         
20594         var _this = this;
20595         
20596         if(this.rendered){
20597             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20598                 _this.getActionEl().addClass(this.disabledClass);
20599                 e.dom.disabled = true;
20600             });
20601         }
20602         
20603         this.disabled = true;
20604         this.fireEvent("disable", this);
20605         return this;
20606     },
20607
20608     enable : function()
20609     {
20610         if(this.inputType != 'radio'){
20611             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20612             return;
20613         }
20614         
20615         var _this = this;
20616         
20617         if(this.rendered){
20618             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20619                 _this.getActionEl().removeClass(this.disabledClass);
20620                 e.dom.disabled = false;
20621             });
20622         }
20623         
20624         this.disabled = false;
20625         this.fireEvent("enable", this);
20626         return this;
20627     }
20628
20629 });
20630
20631 Roo.apply(Roo.bootstrap.CheckBox, {
20632     
20633     groups: {},
20634     
20635      /**
20636     * register a CheckBox Group
20637     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20638     */
20639     register : function(checkbox)
20640     {
20641         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20642             this.groups[checkbox.groupId] = {};
20643         }
20644         
20645         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20646             return;
20647         }
20648         
20649         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20650         
20651     },
20652     /**
20653     * fetch a CheckBox Group based on the group ID
20654     * @param {string} the group ID
20655     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20656     */
20657     get: function(groupId) {
20658         if (typeof(this.groups[groupId]) == 'undefined') {
20659             return false;
20660         }
20661         
20662         return this.groups[groupId] ;
20663     }
20664     
20665     
20666 });
20667 /*
20668  * - LGPL
20669  *
20670  * RadioItem
20671  * 
20672  */
20673
20674 /**
20675  * @class Roo.bootstrap.Radio
20676  * @extends Roo.bootstrap.Component
20677  * Bootstrap Radio class
20678  * @cfg {String} boxLabel - the label associated
20679  * @cfg {String} value - the value of radio
20680  * 
20681  * @constructor
20682  * Create a new Radio
20683  * @param {Object} config The config object
20684  */
20685 Roo.bootstrap.Radio = function(config){
20686     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20687     
20688 };
20689
20690 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20691     
20692     boxLabel : '',
20693     
20694     value : '',
20695     
20696     getAutoCreate : function()
20697     {
20698         var cfg = {
20699             tag : 'div',
20700             cls : 'form-group radio',
20701             cn : [
20702                 {
20703                     tag : 'label',
20704                     cls : 'box-label',
20705                     html : this.boxLabel
20706                 }
20707             ]
20708         };
20709         
20710         return cfg;
20711     },
20712     
20713     initEvents : function() 
20714     {
20715         this.parent().register(this);
20716         
20717         this.el.on('click', this.onClick, this);
20718         
20719     },
20720     
20721     onClick : function()
20722     {
20723         this.setChecked(true);
20724     },
20725     
20726     setChecked : function(state, suppressEvent)
20727     {
20728         this.parent().setValue(this.value, suppressEvent);
20729         
20730     }
20731     
20732 });
20733  
20734
20735  /*
20736  * - LGPL
20737  *
20738  * Input
20739  * 
20740  */
20741
20742 /**
20743  * @class Roo.bootstrap.SecurePass
20744  * @extends Roo.bootstrap.Input
20745  * Bootstrap SecurePass class
20746  *
20747  * 
20748  * @constructor
20749  * Create a new SecurePass
20750  * @param {Object} config The config object
20751  */
20752  
20753 Roo.bootstrap.SecurePass = function (config) {
20754     // these go here, so the translation tool can replace them..
20755     this.errors = {
20756         PwdEmpty: "Please type a password, and then retype it to confirm.",
20757         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20758         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20759         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20760         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20761         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20762         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20763         TooWeak: "Your password is Too Weak."
20764     },
20765     this.meterLabel = "Password strength:";
20766     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20767     this.meterClass = [
20768         "roo-password-meter-tooweak", 
20769         "roo-password-meter-weak", 
20770         "roo-password-meter-medium", 
20771         "roo-password-meter-strong", 
20772         "roo-password-meter-grey"
20773     ];
20774     
20775     this.errors = {};
20776     
20777     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20778 }
20779
20780 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20781     /**
20782      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20783      * {
20784      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20785      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20786      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20787      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20788      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20789      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20790      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20791      * })
20792      */
20793     // private
20794     
20795     meterWidth: 300,
20796     errorMsg :'',    
20797     errors: false,
20798     imageRoot: '/',
20799     /**
20800      * @cfg {String/Object} Label for the strength meter (defaults to
20801      * 'Password strength:')
20802      */
20803     // private
20804     meterLabel: '',
20805     /**
20806      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20807      * ['Weak', 'Medium', 'Strong'])
20808      */
20809     // private    
20810     pwdStrengths: false,    
20811     // private
20812     strength: 0,
20813     // private
20814     _lastPwd: null,
20815     // private
20816     kCapitalLetter: 0,
20817     kSmallLetter: 1,
20818     kDigit: 2,
20819     kPunctuation: 3,
20820     
20821     insecure: false,
20822     // private
20823     initEvents: function ()
20824     {
20825         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20826
20827         if (this.el.is('input[type=password]') && Roo.isSafari) {
20828             this.el.on('keydown', this.SafariOnKeyDown, this);
20829         }
20830
20831         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20832     },
20833     // private
20834     onRender: function (ct, position)
20835     {
20836         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20837         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20838         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20839
20840         this.trigger.createChild({
20841                    cn: [
20842                     {
20843                     //id: 'PwdMeter',
20844                     tag: 'div',
20845                     cls: 'roo-password-meter-grey col-xs-12',
20846                     style: {
20847                         //width: 0,
20848                         //width: this.meterWidth + 'px'                                                
20849                         }
20850                     },
20851                     {                            
20852                          cls: 'roo-password-meter-text'                          
20853                     }
20854                 ]            
20855         });
20856
20857          
20858         if (this.hideTrigger) {
20859             this.trigger.setDisplayed(false);
20860         }
20861         this.setSize(this.width || '', this.height || '');
20862     },
20863     // private
20864     onDestroy: function ()
20865     {
20866         if (this.trigger) {
20867             this.trigger.removeAllListeners();
20868             this.trigger.remove();
20869         }
20870         if (this.wrap) {
20871             this.wrap.remove();
20872         }
20873         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20874     },
20875     // private
20876     checkStrength: function ()
20877     {
20878         var pwd = this.inputEl().getValue();
20879         if (pwd == this._lastPwd) {
20880             return;
20881         }
20882
20883         var strength;
20884         if (this.ClientSideStrongPassword(pwd)) {
20885             strength = 3;
20886         } else if (this.ClientSideMediumPassword(pwd)) {
20887             strength = 2;
20888         } else if (this.ClientSideWeakPassword(pwd)) {
20889             strength = 1;
20890         } else {
20891             strength = 0;
20892         }
20893         
20894         Roo.log('strength1: ' + strength);
20895         
20896         //var pm = this.trigger.child('div/div/div').dom;
20897         var pm = this.trigger.child('div/div');
20898         pm.removeClass(this.meterClass);
20899         pm.addClass(this.meterClass[strength]);
20900                 
20901         
20902         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20903                 
20904         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20905         
20906         this._lastPwd = pwd;
20907     },
20908     reset: function ()
20909     {
20910         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20911         
20912         this._lastPwd = '';
20913         
20914         var pm = this.trigger.child('div/div');
20915         pm.removeClass(this.meterClass);
20916         pm.addClass('roo-password-meter-grey');        
20917         
20918         
20919         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20920         
20921         pt.innerHTML = '';
20922         this.inputEl().dom.type='password';
20923     },
20924     // private
20925     validateValue: function (value)
20926     {
20927         
20928         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20929             return false;
20930         }
20931         if (value.length == 0) {
20932             if (this.allowBlank) {
20933                 this.clearInvalid();
20934                 return true;
20935             }
20936
20937             this.markInvalid(this.errors.PwdEmpty);
20938             this.errorMsg = this.errors.PwdEmpty;
20939             return false;
20940         }
20941         
20942         if(this.insecure){
20943             return true;
20944         }
20945         
20946         if ('[\x21-\x7e]*'.match(value)) {
20947             this.markInvalid(this.errors.PwdBadChar);
20948             this.errorMsg = this.errors.PwdBadChar;
20949             return false;
20950         }
20951         if (value.length < 6) {
20952             this.markInvalid(this.errors.PwdShort);
20953             this.errorMsg = this.errors.PwdShort;
20954             return false;
20955         }
20956         if (value.length > 16) {
20957             this.markInvalid(this.errors.PwdLong);
20958             this.errorMsg = this.errors.PwdLong;
20959             return false;
20960         }
20961         var strength;
20962         if (this.ClientSideStrongPassword(value)) {
20963             strength = 3;
20964         } else if (this.ClientSideMediumPassword(value)) {
20965             strength = 2;
20966         } else if (this.ClientSideWeakPassword(value)) {
20967             strength = 1;
20968         } else {
20969             strength = 0;
20970         }
20971
20972         
20973         if (strength < 2) {
20974             //this.markInvalid(this.errors.TooWeak);
20975             this.errorMsg = this.errors.TooWeak;
20976             //return false;
20977         }
20978         
20979         
20980         console.log('strength2: ' + strength);
20981         
20982         //var pm = this.trigger.child('div/div/div').dom;
20983         
20984         var pm = this.trigger.child('div/div');
20985         pm.removeClass(this.meterClass);
20986         pm.addClass(this.meterClass[strength]);
20987                 
20988         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20989                 
20990         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20991         
20992         this.errorMsg = ''; 
20993         return true;
20994     },
20995     // private
20996     CharacterSetChecks: function (type)
20997     {
20998         this.type = type;
20999         this.fResult = false;
21000     },
21001     // private
21002     isctype: function (character, type)
21003     {
21004         switch (type) {  
21005             case this.kCapitalLetter:
21006                 if (character >= 'A' && character <= 'Z') {
21007                     return true;
21008                 }
21009                 break;
21010             
21011             case this.kSmallLetter:
21012                 if (character >= 'a' && character <= 'z') {
21013                     return true;
21014                 }
21015                 break;
21016             
21017             case this.kDigit:
21018                 if (character >= '0' && character <= '9') {
21019                     return true;
21020                 }
21021                 break;
21022             
21023             case this.kPunctuation:
21024                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21025                     return true;
21026                 }
21027                 break;
21028             
21029             default:
21030                 return false;
21031         }
21032
21033     },
21034     // private
21035     IsLongEnough: function (pwd, size)
21036     {
21037         return !(pwd == null || isNaN(size) || pwd.length < size);
21038     },
21039     // private
21040     SpansEnoughCharacterSets: function (word, nb)
21041     {
21042         if (!this.IsLongEnough(word, nb))
21043         {
21044             return false;
21045         }
21046
21047         var characterSetChecks = new Array(
21048             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21049             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21050         );
21051         
21052         for (var index = 0; index < word.length; ++index) {
21053             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21054                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21055                     characterSetChecks[nCharSet].fResult = true;
21056                     break;
21057                 }
21058             }
21059         }
21060
21061         var nCharSets = 0;
21062         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21063             if (characterSetChecks[nCharSet].fResult) {
21064                 ++nCharSets;
21065             }
21066         }
21067
21068         if (nCharSets < nb) {
21069             return false;
21070         }
21071         return true;
21072     },
21073     // private
21074     ClientSideStrongPassword: function (pwd)
21075     {
21076         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21077     },
21078     // private
21079     ClientSideMediumPassword: function (pwd)
21080     {
21081         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21082     },
21083     // private
21084     ClientSideWeakPassword: function (pwd)
21085     {
21086         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21087     }
21088           
21089 })//<script type="text/javascript">
21090
21091 /*
21092  * Based  Ext JS Library 1.1.1
21093  * Copyright(c) 2006-2007, Ext JS, LLC.
21094  * LGPL
21095  *
21096  */
21097  
21098 /**
21099  * @class Roo.HtmlEditorCore
21100  * @extends Roo.Component
21101  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21102  *
21103  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21104  */
21105
21106 Roo.HtmlEditorCore = function(config){
21107     
21108     
21109     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21110     
21111     
21112     this.addEvents({
21113         /**
21114          * @event initialize
21115          * Fires when the editor is fully initialized (including the iframe)
21116          * @param {Roo.HtmlEditorCore} this
21117          */
21118         initialize: true,
21119         /**
21120          * @event activate
21121          * Fires when the editor is first receives the focus. Any insertion must wait
21122          * until after this event.
21123          * @param {Roo.HtmlEditorCore} this
21124          */
21125         activate: true,
21126          /**
21127          * @event beforesync
21128          * Fires before the textarea is updated with content from the editor iframe. Return false
21129          * to cancel the sync.
21130          * @param {Roo.HtmlEditorCore} this
21131          * @param {String} html
21132          */
21133         beforesync: true,
21134          /**
21135          * @event beforepush
21136          * Fires before the iframe editor is updated with content from the textarea. Return false
21137          * to cancel the push.
21138          * @param {Roo.HtmlEditorCore} this
21139          * @param {String} html
21140          */
21141         beforepush: true,
21142          /**
21143          * @event sync
21144          * Fires when the textarea is updated with content from the editor iframe.
21145          * @param {Roo.HtmlEditorCore} this
21146          * @param {String} html
21147          */
21148         sync: true,
21149          /**
21150          * @event push
21151          * Fires when the iframe editor is updated with content from the textarea.
21152          * @param {Roo.HtmlEditorCore} this
21153          * @param {String} html
21154          */
21155         push: true,
21156         
21157         /**
21158          * @event editorevent
21159          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21160          * @param {Roo.HtmlEditorCore} this
21161          */
21162         editorevent: true
21163         
21164     });
21165     
21166     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21167     
21168     // defaults : white / black...
21169     this.applyBlacklists();
21170     
21171     
21172     
21173 };
21174
21175
21176 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21177
21178
21179      /**
21180      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21181      */
21182     
21183     owner : false,
21184     
21185      /**
21186      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21187      *                        Roo.resizable.
21188      */
21189     resizable : false,
21190      /**
21191      * @cfg {Number} height (in pixels)
21192      */   
21193     height: 300,
21194    /**
21195      * @cfg {Number} width (in pixels)
21196      */   
21197     width: 500,
21198     
21199     /**
21200      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21201      * 
21202      */
21203     stylesheets: false,
21204     
21205     // id of frame..
21206     frameId: false,
21207     
21208     // private properties
21209     validationEvent : false,
21210     deferHeight: true,
21211     initialized : false,
21212     activated : false,
21213     sourceEditMode : false,
21214     onFocus : Roo.emptyFn,
21215     iframePad:3,
21216     hideMode:'offsets',
21217     
21218     clearUp: true,
21219     
21220     // blacklist + whitelisted elements..
21221     black: false,
21222     white: false,
21223      
21224     
21225
21226     /**
21227      * Protected method that will not generally be called directly. It
21228      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21229      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21230      */
21231     getDocMarkup : function(){
21232         // body styles..
21233         var st = '';
21234         
21235         // inherit styels from page...?? 
21236         if (this.stylesheets === false) {
21237             
21238             Roo.get(document.head).select('style').each(function(node) {
21239                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21240             });
21241             
21242             Roo.get(document.head).select('link').each(function(node) { 
21243                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21244             });
21245             
21246         } else if (!this.stylesheets.length) {
21247                 // simple..
21248                 st = '<style type="text/css">' +
21249                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21250                    '</style>';
21251         } else { 
21252             
21253         }
21254         
21255         st +=  '<style type="text/css">' +
21256             'IMG { cursor: pointer } ' +
21257         '</style>';
21258
21259         
21260         return '<html><head>' + st  +
21261             //<style type="text/css">' +
21262             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21263             //'</style>' +
21264             ' </head><body class="roo-htmleditor-body"></body></html>';
21265     },
21266
21267     // private
21268     onRender : function(ct, position)
21269     {
21270         var _t = this;
21271         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21272         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21273         
21274         
21275         this.el.dom.style.border = '0 none';
21276         this.el.dom.setAttribute('tabIndex', -1);
21277         this.el.addClass('x-hidden hide');
21278         
21279         
21280         
21281         if(Roo.isIE){ // fix IE 1px bogus margin
21282             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21283         }
21284        
21285         
21286         this.frameId = Roo.id();
21287         
21288          
21289         
21290         var iframe = this.owner.wrap.createChild({
21291             tag: 'iframe',
21292             cls: 'form-control', // bootstrap..
21293             id: this.frameId,
21294             name: this.frameId,
21295             frameBorder : 'no',
21296             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21297         }, this.el
21298         );
21299         
21300         
21301         this.iframe = iframe.dom;
21302
21303          this.assignDocWin();
21304         
21305         this.doc.designMode = 'on';
21306        
21307         this.doc.open();
21308         this.doc.write(this.getDocMarkup());
21309         this.doc.close();
21310
21311         
21312         var task = { // must defer to wait for browser to be ready
21313             run : function(){
21314                 //console.log("run task?" + this.doc.readyState);
21315                 this.assignDocWin();
21316                 if(this.doc.body || this.doc.readyState == 'complete'){
21317                     try {
21318                         this.doc.designMode="on";
21319                     } catch (e) {
21320                         return;
21321                     }
21322                     Roo.TaskMgr.stop(task);
21323                     this.initEditor.defer(10, this);
21324                 }
21325             },
21326             interval : 10,
21327             duration: 10000,
21328             scope: this
21329         };
21330         Roo.TaskMgr.start(task);
21331
21332     },
21333
21334     // private
21335     onResize : function(w, h)
21336     {
21337          Roo.log('resize: ' +w + ',' + h );
21338         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21339         if(!this.iframe){
21340             return;
21341         }
21342         if(typeof w == 'number'){
21343             
21344             this.iframe.style.width = w + 'px';
21345         }
21346         if(typeof h == 'number'){
21347             
21348             this.iframe.style.height = h + 'px';
21349             if(this.doc){
21350                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21351             }
21352         }
21353         
21354     },
21355
21356     /**
21357      * Toggles the editor between standard and source edit mode.
21358      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21359      */
21360     toggleSourceEdit : function(sourceEditMode){
21361         
21362         this.sourceEditMode = sourceEditMode === true;
21363         
21364         if(this.sourceEditMode){
21365  
21366             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21367             
21368         }else{
21369             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21370             //this.iframe.className = '';
21371             this.deferFocus();
21372         }
21373         //this.setSize(this.owner.wrap.getSize());
21374         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21375     },
21376
21377     
21378   
21379
21380     /**
21381      * Protected method that will not generally be called directly. If you need/want
21382      * custom HTML cleanup, this is the method you should override.
21383      * @param {String} html The HTML to be cleaned
21384      * return {String} The cleaned HTML
21385      */
21386     cleanHtml : function(html){
21387         html = String(html);
21388         if(html.length > 5){
21389             if(Roo.isSafari){ // strip safari nonsense
21390                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21391             }
21392         }
21393         if(html == '&nbsp;'){
21394             html = '';
21395         }
21396         return html;
21397     },
21398
21399     /**
21400      * HTML Editor -> Textarea
21401      * Protected method that will not generally be called directly. Syncs the contents
21402      * of the editor iframe with the textarea.
21403      */
21404     syncValue : function(){
21405         if(this.initialized){
21406             var bd = (this.doc.body || this.doc.documentElement);
21407             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21408             var html = bd.innerHTML;
21409             if(Roo.isSafari){
21410                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21411                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21412                 if(m && m[1]){
21413                     html = '<div style="'+m[0]+'">' + html + '</div>';
21414                 }
21415             }
21416             html = this.cleanHtml(html);
21417             // fix up the special chars.. normaly like back quotes in word...
21418             // however we do not want to do this with chinese..
21419             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21420                 var cc = b.charCodeAt();
21421                 if (
21422                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21423                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21424                     (cc >= 0xf900 && cc < 0xfb00 )
21425                 ) {
21426                         return b;
21427                 }
21428                 return "&#"+cc+";" 
21429             });
21430             if(this.owner.fireEvent('beforesync', this, html) !== false){
21431                 this.el.dom.value = html;
21432                 this.owner.fireEvent('sync', this, html);
21433             }
21434         }
21435     },
21436
21437     /**
21438      * Protected method that will not generally be called directly. Pushes the value of the textarea
21439      * into the iframe editor.
21440      */
21441     pushValue : function(){
21442         if(this.initialized){
21443             var v = this.el.dom.value.trim();
21444             
21445 //            if(v.length < 1){
21446 //                v = '&#160;';
21447 //            }
21448             
21449             if(this.owner.fireEvent('beforepush', this, v) !== false){
21450                 var d = (this.doc.body || this.doc.documentElement);
21451                 d.innerHTML = v;
21452                 this.cleanUpPaste();
21453                 this.el.dom.value = d.innerHTML;
21454                 this.owner.fireEvent('push', this, v);
21455             }
21456         }
21457     },
21458
21459     // private
21460     deferFocus : function(){
21461         this.focus.defer(10, this);
21462     },
21463
21464     // doc'ed in Field
21465     focus : function(){
21466         if(this.win && !this.sourceEditMode){
21467             this.win.focus();
21468         }else{
21469             this.el.focus();
21470         }
21471     },
21472     
21473     assignDocWin: function()
21474     {
21475         var iframe = this.iframe;
21476         
21477          if(Roo.isIE){
21478             this.doc = iframe.contentWindow.document;
21479             this.win = iframe.contentWindow;
21480         } else {
21481 //            if (!Roo.get(this.frameId)) {
21482 //                return;
21483 //            }
21484 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21485 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21486             
21487             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21488                 return;
21489             }
21490             
21491             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21492             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21493         }
21494     },
21495     
21496     // private
21497     initEditor : function(){
21498         //console.log("INIT EDITOR");
21499         this.assignDocWin();
21500         
21501         
21502         
21503         this.doc.designMode="on";
21504         this.doc.open();
21505         this.doc.write(this.getDocMarkup());
21506         this.doc.close();
21507         
21508         var dbody = (this.doc.body || this.doc.documentElement);
21509         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21510         // this copies styles from the containing element into thsi one..
21511         // not sure why we need all of this..
21512         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21513         
21514         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21515         //ss['background-attachment'] = 'fixed'; // w3c
21516         dbody.bgProperties = 'fixed'; // ie
21517         //Roo.DomHelper.applyStyles(dbody, ss);
21518         Roo.EventManager.on(this.doc, {
21519             //'mousedown': this.onEditorEvent,
21520             'mouseup': this.onEditorEvent,
21521             'dblclick': this.onEditorEvent,
21522             'click': this.onEditorEvent,
21523             'keyup': this.onEditorEvent,
21524             buffer:100,
21525             scope: this
21526         });
21527         if(Roo.isGecko){
21528             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21529         }
21530         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21531             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21532         }
21533         this.initialized = true;
21534
21535         this.owner.fireEvent('initialize', this);
21536         this.pushValue();
21537     },
21538
21539     // private
21540     onDestroy : function(){
21541         
21542         
21543         
21544         if(this.rendered){
21545             
21546             //for (var i =0; i < this.toolbars.length;i++) {
21547             //    // fixme - ask toolbars for heights?
21548             //    this.toolbars[i].onDestroy();
21549            // }
21550             
21551             //this.wrap.dom.innerHTML = '';
21552             //this.wrap.remove();
21553         }
21554     },
21555
21556     // private
21557     onFirstFocus : function(){
21558         
21559         this.assignDocWin();
21560         
21561         
21562         this.activated = true;
21563          
21564     
21565         if(Roo.isGecko){ // prevent silly gecko errors
21566             this.win.focus();
21567             var s = this.win.getSelection();
21568             if(!s.focusNode || s.focusNode.nodeType != 3){
21569                 var r = s.getRangeAt(0);
21570                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21571                 r.collapse(true);
21572                 this.deferFocus();
21573             }
21574             try{
21575                 this.execCmd('useCSS', true);
21576                 this.execCmd('styleWithCSS', false);
21577             }catch(e){}
21578         }
21579         this.owner.fireEvent('activate', this);
21580     },
21581
21582     // private
21583     adjustFont: function(btn){
21584         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21585         //if(Roo.isSafari){ // safari
21586         //    adjust *= 2;
21587        // }
21588         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21589         if(Roo.isSafari){ // safari
21590             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21591             v =  (v < 10) ? 10 : v;
21592             v =  (v > 48) ? 48 : v;
21593             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21594             
21595         }
21596         
21597         
21598         v = Math.max(1, v+adjust);
21599         
21600         this.execCmd('FontSize', v  );
21601     },
21602
21603     onEditorEvent : function(e)
21604     {
21605         this.owner.fireEvent('editorevent', this, e);
21606       //  this.updateToolbar();
21607         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21608     },
21609
21610     insertTag : function(tg)
21611     {
21612         // could be a bit smarter... -> wrap the current selected tRoo..
21613         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21614             
21615             range = this.createRange(this.getSelection());
21616             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21617             wrappingNode.appendChild(range.extractContents());
21618             range.insertNode(wrappingNode);
21619
21620             return;
21621             
21622             
21623             
21624         }
21625         this.execCmd("formatblock",   tg);
21626         
21627     },
21628     
21629     insertText : function(txt)
21630     {
21631         
21632         
21633         var range = this.createRange();
21634         range.deleteContents();
21635                //alert(Sender.getAttribute('label'));
21636                
21637         range.insertNode(this.doc.createTextNode(txt));
21638     } ,
21639     
21640      
21641
21642     /**
21643      * Executes a Midas editor command on the editor document and performs necessary focus and
21644      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21645      * @param {String} cmd The Midas command
21646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21647      */
21648     relayCmd : function(cmd, value){
21649         this.win.focus();
21650         this.execCmd(cmd, value);
21651         this.owner.fireEvent('editorevent', this);
21652         //this.updateToolbar();
21653         this.owner.deferFocus();
21654     },
21655
21656     /**
21657      * Executes a Midas editor command directly on the editor document.
21658      * For visual commands, you should use {@link #relayCmd} instead.
21659      * <b>This should only be called after the editor is initialized.</b>
21660      * @param {String} cmd The Midas command
21661      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21662      */
21663     execCmd : function(cmd, value){
21664         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21665         this.syncValue();
21666     },
21667  
21668  
21669    
21670     /**
21671      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21672      * to insert tRoo.
21673      * @param {String} text | dom node.. 
21674      */
21675     insertAtCursor : function(text)
21676     {
21677         
21678         if(!this.activated){
21679             return;
21680         }
21681         /*
21682         if(Roo.isIE){
21683             this.win.focus();
21684             var r = this.doc.selection.createRange();
21685             if(r){
21686                 r.collapse(true);
21687                 r.pasteHTML(text);
21688                 this.syncValue();
21689                 this.deferFocus();
21690             
21691             }
21692             return;
21693         }
21694         */
21695         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21696             this.win.focus();
21697             
21698             
21699             // from jquery ui (MIT licenced)
21700             var range, node;
21701             var win = this.win;
21702             
21703             if (win.getSelection && win.getSelection().getRangeAt) {
21704                 range = win.getSelection().getRangeAt(0);
21705                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21706                 range.insertNode(node);
21707             } else if (win.document.selection && win.document.selection.createRange) {
21708                 // no firefox support
21709                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21710                 win.document.selection.createRange().pasteHTML(txt);
21711             } else {
21712                 // no firefox support
21713                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21714                 this.execCmd('InsertHTML', txt);
21715             } 
21716             
21717             this.syncValue();
21718             
21719             this.deferFocus();
21720         }
21721     },
21722  // private
21723     mozKeyPress : function(e){
21724         if(e.ctrlKey){
21725             var c = e.getCharCode(), cmd;
21726           
21727             if(c > 0){
21728                 c = String.fromCharCode(c).toLowerCase();
21729                 switch(c){
21730                     case 'b':
21731                         cmd = 'bold';
21732                         break;
21733                     case 'i':
21734                         cmd = 'italic';
21735                         break;
21736                     
21737                     case 'u':
21738                         cmd = 'underline';
21739                         break;
21740                     
21741                     case 'v':
21742                         this.cleanUpPaste.defer(100, this);
21743                         return;
21744                         
21745                 }
21746                 if(cmd){
21747                     this.win.focus();
21748                     this.execCmd(cmd);
21749                     this.deferFocus();
21750                     e.preventDefault();
21751                 }
21752                 
21753             }
21754         }
21755     },
21756
21757     // private
21758     fixKeys : function(){ // load time branching for fastest keydown performance
21759         if(Roo.isIE){
21760             return function(e){
21761                 var k = e.getKey(), r;
21762                 if(k == e.TAB){
21763                     e.stopEvent();
21764                     r = this.doc.selection.createRange();
21765                     if(r){
21766                         r.collapse(true);
21767                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21768                         this.deferFocus();
21769                     }
21770                     return;
21771                 }
21772                 
21773                 if(k == e.ENTER){
21774                     r = this.doc.selection.createRange();
21775                     if(r){
21776                         var target = r.parentElement();
21777                         if(!target || target.tagName.toLowerCase() != 'li'){
21778                             e.stopEvent();
21779                             r.pasteHTML('<br />');
21780                             r.collapse(false);
21781                             r.select();
21782                         }
21783                     }
21784                 }
21785                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21786                     this.cleanUpPaste.defer(100, this);
21787                     return;
21788                 }
21789                 
21790                 
21791             };
21792         }else if(Roo.isOpera){
21793             return function(e){
21794                 var k = e.getKey();
21795                 if(k == e.TAB){
21796                     e.stopEvent();
21797                     this.win.focus();
21798                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21799                     this.deferFocus();
21800                 }
21801                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21802                     this.cleanUpPaste.defer(100, this);
21803                     return;
21804                 }
21805                 
21806             };
21807         }else if(Roo.isSafari){
21808             return function(e){
21809                 var k = e.getKey();
21810                 
21811                 if(k == e.TAB){
21812                     e.stopEvent();
21813                     this.execCmd('InsertText','\t');
21814                     this.deferFocus();
21815                     return;
21816                 }
21817                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21818                     this.cleanUpPaste.defer(100, this);
21819                     return;
21820                 }
21821                 
21822              };
21823         }
21824     }(),
21825     
21826     getAllAncestors: function()
21827     {
21828         var p = this.getSelectedNode();
21829         var a = [];
21830         if (!p) {
21831             a.push(p); // push blank onto stack..
21832             p = this.getParentElement();
21833         }
21834         
21835         
21836         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21837             a.push(p);
21838             p = p.parentNode;
21839         }
21840         a.push(this.doc.body);
21841         return a;
21842     },
21843     lastSel : false,
21844     lastSelNode : false,
21845     
21846     
21847     getSelection : function() 
21848     {
21849         this.assignDocWin();
21850         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21851     },
21852     
21853     getSelectedNode: function() 
21854     {
21855         // this may only work on Gecko!!!
21856         
21857         // should we cache this!!!!
21858         
21859         
21860         
21861          
21862         var range = this.createRange(this.getSelection()).cloneRange();
21863         
21864         if (Roo.isIE) {
21865             var parent = range.parentElement();
21866             while (true) {
21867                 var testRange = range.duplicate();
21868                 testRange.moveToElementText(parent);
21869                 if (testRange.inRange(range)) {
21870                     break;
21871                 }
21872                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21873                     break;
21874                 }
21875                 parent = parent.parentElement;
21876             }
21877             return parent;
21878         }
21879         
21880         // is ancestor a text element.
21881         var ac =  range.commonAncestorContainer;
21882         if (ac.nodeType == 3) {
21883             ac = ac.parentNode;
21884         }
21885         
21886         var ar = ac.childNodes;
21887          
21888         var nodes = [];
21889         var other_nodes = [];
21890         var has_other_nodes = false;
21891         for (var i=0;i<ar.length;i++) {
21892             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21893                 continue;
21894             }
21895             // fullly contained node.
21896             
21897             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21898                 nodes.push(ar[i]);
21899                 continue;
21900             }
21901             
21902             // probably selected..
21903             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21904                 other_nodes.push(ar[i]);
21905                 continue;
21906             }
21907             // outer..
21908             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21909                 continue;
21910             }
21911             
21912             
21913             has_other_nodes = true;
21914         }
21915         if (!nodes.length && other_nodes.length) {
21916             nodes= other_nodes;
21917         }
21918         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21919             return false;
21920         }
21921         
21922         return nodes[0];
21923     },
21924     createRange: function(sel)
21925     {
21926         // this has strange effects when using with 
21927         // top toolbar - not sure if it's a great idea.
21928         //this.editor.contentWindow.focus();
21929         if (typeof sel != "undefined") {
21930             try {
21931                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21932             } catch(e) {
21933                 return this.doc.createRange();
21934             }
21935         } else {
21936             return this.doc.createRange();
21937         }
21938     },
21939     getParentElement: function()
21940     {
21941         
21942         this.assignDocWin();
21943         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21944         
21945         var range = this.createRange(sel);
21946          
21947         try {
21948             var p = range.commonAncestorContainer;
21949             while (p.nodeType == 3) { // text node
21950                 p = p.parentNode;
21951             }
21952             return p;
21953         } catch (e) {
21954             return null;
21955         }
21956     
21957     },
21958     /***
21959      *
21960      * Range intersection.. the hard stuff...
21961      *  '-1' = before
21962      *  '0' = hits..
21963      *  '1' = after.
21964      *         [ -- selected range --- ]
21965      *   [fail]                        [fail]
21966      *
21967      *    basically..
21968      *      if end is before start or  hits it. fail.
21969      *      if start is after end or hits it fail.
21970      *
21971      *   if either hits (but other is outside. - then it's not 
21972      *   
21973      *    
21974      **/
21975     
21976     
21977     // @see http://www.thismuchiknow.co.uk/?p=64.
21978     rangeIntersectsNode : function(range, node)
21979     {
21980         var nodeRange = node.ownerDocument.createRange();
21981         try {
21982             nodeRange.selectNode(node);
21983         } catch (e) {
21984             nodeRange.selectNodeContents(node);
21985         }
21986     
21987         var rangeStartRange = range.cloneRange();
21988         rangeStartRange.collapse(true);
21989     
21990         var rangeEndRange = range.cloneRange();
21991         rangeEndRange.collapse(false);
21992     
21993         var nodeStartRange = nodeRange.cloneRange();
21994         nodeStartRange.collapse(true);
21995     
21996         var nodeEndRange = nodeRange.cloneRange();
21997         nodeEndRange.collapse(false);
21998     
21999         return rangeStartRange.compareBoundaryPoints(
22000                  Range.START_TO_START, nodeEndRange) == -1 &&
22001                rangeEndRange.compareBoundaryPoints(
22002                  Range.START_TO_START, nodeStartRange) == 1;
22003         
22004          
22005     },
22006     rangeCompareNode : function(range, node)
22007     {
22008         var nodeRange = node.ownerDocument.createRange();
22009         try {
22010             nodeRange.selectNode(node);
22011         } catch (e) {
22012             nodeRange.selectNodeContents(node);
22013         }
22014         
22015         
22016         range.collapse(true);
22017     
22018         nodeRange.collapse(true);
22019      
22020         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22021         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22022          
22023         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22024         
22025         var nodeIsBefore   =  ss == 1;
22026         var nodeIsAfter    = ee == -1;
22027         
22028         if (nodeIsBefore && nodeIsAfter) {
22029             return 0; // outer
22030         }
22031         if (!nodeIsBefore && nodeIsAfter) {
22032             return 1; //right trailed.
22033         }
22034         
22035         if (nodeIsBefore && !nodeIsAfter) {
22036             return 2;  // left trailed.
22037         }
22038         // fully contined.
22039         return 3;
22040     },
22041
22042     // private? - in a new class?
22043     cleanUpPaste :  function()
22044     {
22045         // cleans up the whole document..
22046         Roo.log('cleanuppaste');
22047         
22048         this.cleanUpChildren(this.doc.body);
22049         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22050         if (clean != this.doc.body.innerHTML) {
22051             this.doc.body.innerHTML = clean;
22052         }
22053         
22054     },
22055     
22056     cleanWordChars : function(input) {// change the chars to hex code
22057         var he = Roo.HtmlEditorCore;
22058         
22059         var output = input;
22060         Roo.each(he.swapCodes, function(sw) { 
22061             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22062             
22063             output = output.replace(swapper, sw[1]);
22064         });
22065         
22066         return output;
22067     },
22068     
22069     
22070     cleanUpChildren : function (n)
22071     {
22072         if (!n.childNodes.length) {
22073             return;
22074         }
22075         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22076            this.cleanUpChild(n.childNodes[i]);
22077         }
22078     },
22079     
22080     
22081         
22082     
22083     cleanUpChild : function (node)
22084     {
22085         var ed = this;
22086         //console.log(node);
22087         if (node.nodeName == "#text") {
22088             // clean up silly Windows -- stuff?
22089             return; 
22090         }
22091         if (node.nodeName == "#comment") {
22092             node.parentNode.removeChild(node);
22093             // clean up silly Windows -- stuff?
22094             return; 
22095         }
22096         var lcname = node.tagName.toLowerCase();
22097         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22098         // whitelist of tags..
22099         
22100         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22101             // remove node.
22102             node.parentNode.removeChild(node);
22103             return;
22104             
22105         }
22106         
22107         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22108         
22109         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22110         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22111         
22112         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22113         //    remove_keep_children = true;
22114         //}
22115         
22116         if (remove_keep_children) {
22117             this.cleanUpChildren(node);
22118             // inserts everything just before this node...
22119             while (node.childNodes.length) {
22120                 var cn = node.childNodes[0];
22121                 node.removeChild(cn);
22122                 node.parentNode.insertBefore(cn, node);
22123             }
22124             node.parentNode.removeChild(node);
22125             return;
22126         }
22127         
22128         if (!node.attributes || !node.attributes.length) {
22129             this.cleanUpChildren(node);
22130             return;
22131         }
22132         
22133         function cleanAttr(n,v)
22134         {
22135             
22136             if (v.match(/^\./) || v.match(/^\//)) {
22137                 return;
22138             }
22139             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22140                 return;
22141             }
22142             if (v.match(/^#/)) {
22143                 return;
22144             }
22145 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22146             node.removeAttribute(n);
22147             
22148         }
22149         
22150         var cwhite = this.cwhite;
22151         var cblack = this.cblack;
22152             
22153         function cleanStyle(n,v)
22154         {
22155             if (v.match(/expression/)) { //XSS?? should we even bother..
22156                 node.removeAttribute(n);
22157                 return;
22158             }
22159             
22160             var parts = v.split(/;/);
22161             var clean = [];
22162             
22163             Roo.each(parts, function(p) {
22164                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22165                 if (!p.length) {
22166                     return true;
22167                 }
22168                 var l = p.split(':').shift().replace(/\s+/g,'');
22169                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22170                 
22171                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22172 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22173                     //node.removeAttribute(n);
22174                     return true;
22175                 }
22176                 //Roo.log()
22177                 // only allow 'c whitelisted system attributes'
22178                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22179 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22180                     //node.removeAttribute(n);
22181                     return true;
22182                 }
22183                 
22184                 
22185                  
22186                 
22187                 clean.push(p);
22188                 return true;
22189             });
22190             if (clean.length) { 
22191                 node.setAttribute(n, clean.join(';'));
22192             } else {
22193                 node.removeAttribute(n);
22194             }
22195             
22196         }
22197         
22198         
22199         for (var i = node.attributes.length-1; i > -1 ; i--) {
22200             var a = node.attributes[i];
22201             //console.log(a);
22202             
22203             if (a.name.toLowerCase().substr(0,2)=='on')  {
22204                 node.removeAttribute(a.name);
22205                 continue;
22206             }
22207             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22208                 node.removeAttribute(a.name);
22209                 continue;
22210             }
22211             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22212                 cleanAttr(a.name,a.value); // fixme..
22213                 continue;
22214             }
22215             if (a.name == 'style') {
22216                 cleanStyle(a.name,a.value);
22217                 continue;
22218             }
22219             /// clean up MS crap..
22220             // tecnically this should be a list of valid class'es..
22221             
22222             
22223             if (a.name == 'class') {
22224                 if (a.value.match(/^Mso/)) {
22225                     node.className = '';
22226                 }
22227                 
22228                 if (a.value.match(/^body$/)) {
22229                     node.className = '';
22230                 }
22231                 continue;
22232             }
22233             
22234             // style cleanup!?
22235             // class cleanup?
22236             
22237         }
22238         
22239         
22240         this.cleanUpChildren(node);
22241         
22242         
22243     },
22244     
22245     /**
22246      * Clean up MS wordisms...
22247      */
22248     cleanWord : function(node)
22249     {
22250         
22251         
22252         if (!node) {
22253             this.cleanWord(this.doc.body);
22254             return;
22255         }
22256         if (node.nodeName == "#text") {
22257             // clean up silly Windows -- stuff?
22258             return; 
22259         }
22260         if (node.nodeName == "#comment") {
22261             node.parentNode.removeChild(node);
22262             // clean up silly Windows -- stuff?
22263             return; 
22264         }
22265         
22266         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22267             node.parentNode.removeChild(node);
22268             return;
22269         }
22270         
22271         // remove - but keep children..
22272         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22273             while (node.childNodes.length) {
22274                 var cn = node.childNodes[0];
22275                 node.removeChild(cn);
22276                 node.parentNode.insertBefore(cn, node);
22277             }
22278             node.parentNode.removeChild(node);
22279             this.iterateChildren(node, this.cleanWord);
22280             return;
22281         }
22282         // clean styles
22283         if (node.className.length) {
22284             
22285             var cn = node.className.split(/\W+/);
22286             var cna = [];
22287             Roo.each(cn, function(cls) {
22288                 if (cls.match(/Mso[a-zA-Z]+/)) {
22289                     return;
22290                 }
22291                 cna.push(cls);
22292             });
22293             node.className = cna.length ? cna.join(' ') : '';
22294             if (!cna.length) {
22295                 node.removeAttribute("class");
22296             }
22297         }
22298         
22299         if (node.hasAttribute("lang")) {
22300             node.removeAttribute("lang");
22301         }
22302         
22303         if (node.hasAttribute("style")) {
22304             
22305             var styles = node.getAttribute("style").split(";");
22306             var nstyle = [];
22307             Roo.each(styles, function(s) {
22308                 if (!s.match(/:/)) {
22309                     return;
22310                 }
22311                 var kv = s.split(":");
22312                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22313                     return;
22314                 }
22315                 // what ever is left... we allow.
22316                 nstyle.push(s);
22317             });
22318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22319             if (!nstyle.length) {
22320                 node.removeAttribute('style');
22321             }
22322         }
22323         this.iterateChildren(node, this.cleanWord);
22324         
22325         
22326         
22327     },
22328     /**
22329      * iterateChildren of a Node, calling fn each time, using this as the scole..
22330      * @param {DomNode} node node to iterate children of.
22331      * @param {Function} fn method of this class to call on each item.
22332      */
22333     iterateChildren : function(node, fn)
22334     {
22335         if (!node.childNodes.length) {
22336                 return;
22337         }
22338         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22339            fn.call(this, node.childNodes[i])
22340         }
22341     },
22342     
22343     
22344     /**
22345      * cleanTableWidths.
22346      *
22347      * Quite often pasting from word etc.. results in tables with column and widths.
22348      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22349      *
22350      */
22351     cleanTableWidths : function(node)
22352     {
22353          
22354          
22355         if (!node) {
22356             this.cleanTableWidths(this.doc.body);
22357             return;
22358         }
22359         
22360         // ignore list...
22361         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22362             return; 
22363         }
22364         Roo.log(node.tagName);
22365         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22366             this.iterateChildren(node, this.cleanTableWidths);
22367             return;
22368         }
22369         if (node.hasAttribute('width')) {
22370             node.removeAttribute('width');
22371         }
22372         
22373          
22374         if (node.hasAttribute("style")) {
22375             // pretty basic...
22376             
22377             var styles = node.getAttribute("style").split(";");
22378             var nstyle = [];
22379             Roo.each(styles, function(s) {
22380                 if (!s.match(/:/)) {
22381                     return;
22382                 }
22383                 var kv = s.split(":");
22384                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22385                     return;
22386                 }
22387                 // what ever is left... we allow.
22388                 nstyle.push(s);
22389             });
22390             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22391             if (!nstyle.length) {
22392                 node.removeAttribute('style');
22393             }
22394         }
22395         
22396         this.iterateChildren(node, this.cleanTableWidths);
22397         
22398         
22399     },
22400     
22401     
22402     
22403     
22404     domToHTML : function(currentElement, depth, nopadtext) {
22405         
22406         depth = depth || 0;
22407         nopadtext = nopadtext || false;
22408     
22409         if (!currentElement) {
22410             return this.domToHTML(this.doc.body);
22411         }
22412         
22413         //Roo.log(currentElement);
22414         var j;
22415         var allText = false;
22416         var nodeName = currentElement.nodeName;
22417         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22418         
22419         if  (nodeName == '#text') {
22420             
22421             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22422         }
22423         
22424         
22425         var ret = '';
22426         if (nodeName != 'BODY') {
22427              
22428             var i = 0;
22429             // Prints the node tagName, such as <A>, <IMG>, etc
22430             if (tagName) {
22431                 var attr = [];
22432                 for(i = 0; i < currentElement.attributes.length;i++) {
22433                     // quoting?
22434                     var aname = currentElement.attributes.item(i).name;
22435                     if (!currentElement.attributes.item(i).value.length) {
22436                         continue;
22437                     }
22438                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22439                 }
22440                 
22441                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22442             } 
22443             else {
22444                 
22445                 // eack
22446             }
22447         } else {
22448             tagName = false;
22449         }
22450         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22451             return ret;
22452         }
22453         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22454             nopadtext = true;
22455         }
22456         
22457         
22458         // Traverse the tree
22459         i = 0;
22460         var currentElementChild = currentElement.childNodes.item(i);
22461         var allText = true;
22462         var innerHTML  = '';
22463         lastnode = '';
22464         while (currentElementChild) {
22465             // Formatting code (indent the tree so it looks nice on the screen)
22466             var nopad = nopadtext;
22467             if (lastnode == 'SPAN') {
22468                 nopad  = true;
22469             }
22470             // text
22471             if  (currentElementChild.nodeName == '#text') {
22472                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22473                 toadd = nopadtext ? toadd : toadd.trim();
22474                 if (!nopad && toadd.length > 80) {
22475                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22476                 }
22477                 innerHTML  += toadd;
22478                 
22479                 i++;
22480                 currentElementChild = currentElement.childNodes.item(i);
22481                 lastNode = '';
22482                 continue;
22483             }
22484             allText = false;
22485             
22486             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22487                 
22488             // Recursively traverse the tree structure of the child node
22489             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22490             lastnode = currentElementChild.nodeName;
22491             i++;
22492             currentElementChild=currentElement.childNodes.item(i);
22493         }
22494         
22495         ret += innerHTML;
22496         
22497         if (!allText) {
22498                 // The remaining code is mostly for formatting the tree
22499             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22500         }
22501         
22502         
22503         if (tagName) {
22504             ret+= "</"+tagName+">";
22505         }
22506         return ret;
22507         
22508     },
22509         
22510     applyBlacklists : function()
22511     {
22512         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22513         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22514         
22515         this.white = [];
22516         this.black = [];
22517         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22518             if (b.indexOf(tag) > -1) {
22519                 return;
22520             }
22521             this.white.push(tag);
22522             
22523         }, this);
22524         
22525         Roo.each(w, function(tag) {
22526             if (b.indexOf(tag) > -1) {
22527                 return;
22528             }
22529             if (this.white.indexOf(tag) > -1) {
22530                 return;
22531             }
22532             this.white.push(tag);
22533             
22534         }, this);
22535         
22536         
22537         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22538             if (w.indexOf(tag) > -1) {
22539                 return;
22540             }
22541             this.black.push(tag);
22542             
22543         }, this);
22544         
22545         Roo.each(b, function(tag) {
22546             if (w.indexOf(tag) > -1) {
22547                 return;
22548             }
22549             if (this.black.indexOf(tag) > -1) {
22550                 return;
22551             }
22552             this.black.push(tag);
22553             
22554         }, this);
22555         
22556         
22557         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22558         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22559         
22560         this.cwhite = [];
22561         this.cblack = [];
22562         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22563             if (b.indexOf(tag) > -1) {
22564                 return;
22565             }
22566             this.cwhite.push(tag);
22567             
22568         }, this);
22569         
22570         Roo.each(w, function(tag) {
22571             if (b.indexOf(tag) > -1) {
22572                 return;
22573             }
22574             if (this.cwhite.indexOf(tag) > -1) {
22575                 return;
22576             }
22577             this.cwhite.push(tag);
22578             
22579         }, this);
22580         
22581         
22582         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22583             if (w.indexOf(tag) > -1) {
22584                 return;
22585             }
22586             this.cblack.push(tag);
22587             
22588         }, this);
22589         
22590         Roo.each(b, function(tag) {
22591             if (w.indexOf(tag) > -1) {
22592                 return;
22593             }
22594             if (this.cblack.indexOf(tag) > -1) {
22595                 return;
22596             }
22597             this.cblack.push(tag);
22598             
22599         }, this);
22600     },
22601     
22602     setStylesheets : function(stylesheets)
22603     {
22604         if(typeof(stylesheets) == 'string'){
22605             Roo.get(this.iframe.contentDocument.head).createChild({
22606                 tag : 'link',
22607                 rel : 'stylesheet',
22608                 type : 'text/css',
22609                 href : stylesheets
22610             });
22611             
22612             return;
22613         }
22614         var _this = this;
22615      
22616         Roo.each(stylesheets, function(s) {
22617             if(!s.length){
22618                 return;
22619             }
22620             
22621             Roo.get(_this.iframe.contentDocument.head).createChild({
22622                 tag : 'link',
22623                 rel : 'stylesheet',
22624                 type : 'text/css',
22625                 href : s
22626             });
22627         });
22628
22629         
22630     },
22631     
22632     removeStylesheets : function()
22633     {
22634         var _this = this;
22635         
22636         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22637             s.remove();
22638         });
22639     }
22640     
22641     // hide stuff that is not compatible
22642     /**
22643      * @event blur
22644      * @hide
22645      */
22646     /**
22647      * @event change
22648      * @hide
22649      */
22650     /**
22651      * @event focus
22652      * @hide
22653      */
22654     /**
22655      * @event specialkey
22656      * @hide
22657      */
22658     /**
22659      * @cfg {String} fieldClass @hide
22660      */
22661     /**
22662      * @cfg {String} focusClass @hide
22663      */
22664     /**
22665      * @cfg {String} autoCreate @hide
22666      */
22667     /**
22668      * @cfg {String} inputType @hide
22669      */
22670     /**
22671      * @cfg {String} invalidClass @hide
22672      */
22673     /**
22674      * @cfg {String} invalidText @hide
22675      */
22676     /**
22677      * @cfg {String} msgFx @hide
22678      */
22679     /**
22680      * @cfg {String} validateOnBlur @hide
22681      */
22682 });
22683
22684 Roo.HtmlEditorCore.white = [
22685         'area', 'br', 'img', 'input', 'hr', 'wbr',
22686         
22687        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22688        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22689        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22690        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22691        'table',   'ul',         'xmp', 
22692        
22693        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22694       'thead',   'tr', 
22695      
22696       'dir', 'menu', 'ol', 'ul', 'dl',
22697        
22698       'embed',  'object'
22699 ];
22700
22701
22702 Roo.HtmlEditorCore.black = [
22703     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22704         'applet', // 
22705         'base',   'basefont', 'bgsound', 'blink',  'body', 
22706         'frame',  'frameset', 'head',    'html',   'ilayer', 
22707         'iframe', 'layer',  'link',     'meta',    'object',   
22708         'script', 'style' ,'title',  'xml' // clean later..
22709 ];
22710 Roo.HtmlEditorCore.clean = [
22711     'script', 'style', 'title', 'xml'
22712 ];
22713 Roo.HtmlEditorCore.remove = [
22714     'font'
22715 ];
22716 // attributes..
22717
22718 Roo.HtmlEditorCore.ablack = [
22719     'on'
22720 ];
22721     
22722 Roo.HtmlEditorCore.aclean = [ 
22723     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22724 ];
22725
22726 // protocols..
22727 Roo.HtmlEditorCore.pwhite= [
22728         'http',  'https',  'mailto'
22729 ];
22730
22731 // white listed style attributes.
22732 Roo.HtmlEditorCore.cwhite= [
22733       //  'text-align', /// default is to allow most things..
22734       
22735          
22736 //        'font-size'//??
22737 ];
22738
22739 // black listed style attributes.
22740 Roo.HtmlEditorCore.cblack= [
22741       //  'font-size' -- this can be set by the project 
22742 ];
22743
22744
22745 Roo.HtmlEditorCore.swapCodes   =[ 
22746     [    8211, "--" ], 
22747     [    8212, "--" ], 
22748     [    8216,  "'" ],  
22749     [    8217, "'" ],  
22750     [    8220, '"' ],  
22751     [    8221, '"' ],  
22752     [    8226, "*" ],  
22753     [    8230, "..." ]
22754 ]; 
22755
22756     /*
22757  * - LGPL
22758  *
22759  * HtmlEditor
22760  * 
22761  */
22762
22763 /**
22764  * @class Roo.bootstrap.HtmlEditor
22765  * @extends Roo.bootstrap.TextArea
22766  * Bootstrap HtmlEditor class
22767
22768  * @constructor
22769  * Create a new HtmlEditor
22770  * @param {Object} config The config object
22771  */
22772
22773 Roo.bootstrap.HtmlEditor = function(config){
22774     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22775     if (!this.toolbars) {
22776         this.toolbars = [];
22777     }
22778     
22779     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22780     this.addEvents({
22781             /**
22782              * @event initialize
22783              * Fires when the editor is fully initialized (including the iframe)
22784              * @param {HtmlEditor} this
22785              */
22786             initialize: true,
22787             /**
22788              * @event activate
22789              * Fires when the editor is first receives the focus. Any insertion must wait
22790              * until after this event.
22791              * @param {HtmlEditor} this
22792              */
22793             activate: true,
22794              /**
22795              * @event beforesync
22796              * Fires before the textarea is updated with content from the editor iframe. Return false
22797              * to cancel the sync.
22798              * @param {HtmlEditor} this
22799              * @param {String} html
22800              */
22801             beforesync: true,
22802              /**
22803              * @event beforepush
22804              * Fires before the iframe editor is updated with content from the textarea. Return false
22805              * to cancel the push.
22806              * @param {HtmlEditor} this
22807              * @param {String} html
22808              */
22809             beforepush: true,
22810              /**
22811              * @event sync
22812              * Fires when the textarea is updated with content from the editor iframe.
22813              * @param {HtmlEditor} this
22814              * @param {String} html
22815              */
22816             sync: true,
22817              /**
22818              * @event push
22819              * Fires when the iframe editor is updated with content from the textarea.
22820              * @param {HtmlEditor} this
22821              * @param {String} html
22822              */
22823             push: true,
22824              /**
22825              * @event editmodechange
22826              * Fires when the editor switches edit modes
22827              * @param {HtmlEditor} this
22828              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22829              */
22830             editmodechange: true,
22831             /**
22832              * @event editorevent
22833              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22834              * @param {HtmlEditor} this
22835              */
22836             editorevent: true,
22837             /**
22838              * @event firstfocus
22839              * Fires when on first focus - needed by toolbars..
22840              * @param {HtmlEditor} this
22841              */
22842             firstfocus: true,
22843             /**
22844              * @event autosave
22845              * Auto save the htmlEditor value as a file into Events
22846              * @param {HtmlEditor} this
22847              */
22848             autosave: true,
22849             /**
22850              * @event savedpreview
22851              * preview the saved version of htmlEditor
22852              * @param {HtmlEditor} this
22853              */
22854             savedpreview: true
22855         });
22856 };
22857
22858
22859 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22860     
22861     
22862       /**
22863      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22864      */
22865     toolbars : false,
22866     
22867      /**
22868     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22869     */
22870     btns : [],
22871    
22872      /**
22873      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22874      *                        Roo.resizable.
22875      */
22876     resizable : false,
22877      /**
22878      * @cfg {Number} height (in pixels)
22879      */   
22880     height: 300,
22881    /**
22882      * @cfg {Number} width (in pixels)
22883      */   
22884     width: false,
22885     
22886     /**
22887      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22888      * 
22889      */
22890     stylesheets: false,
22891     
22892     // id of frame..
22893     frameId: false,
22894     
22895     // private properties
22896     validationEvent : false,
22897     deferHeight: true,
22898     initialized : false,
22899     activated : false,
22900     
22901     onFocus : Roo.emptyFn,
22902     iframePad:3,
22903     hideMode:'offsets',
22904     
22905     tbContainer : false,
22906     
22907     toolbarContainer :function() {
22908         return this.wrap.select('.x-html-editor-tb',true).first();
22909     },
22910
22911     /**
22912      * Protected method that will not generally be called directly. It
22913      * is called when the editor creates its toolbar. Override this method if you need to
22914      * add custom toolbar buttons.
22915      * @param {HtmlEditor} editor
22916      */
22917     createToolbar : function(){
22918         Roo.log('renewing');
22919         Roo.log("create toolbars");
22920         
22921         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22922         this.toolbars[0].render(this.toolbarContainer());
22923         
22924         return;
22925         
22926 //        if (!editor.toolbars || !editor.toolbars.length) {
22927 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22928 //        }
22929 //        
22930 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22931 //            editor.toolbars[i] = Roo.factory(
22932 //                    typeof(editor.toolbars[i]) == 'string' ?
22933 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22934 //                Roo.bootstrap.HtmlEditor);
22935 //            editor.toolbars[i].init(editor);
22936 //        }
22937     },
22938
22939      
22940     // private
22941     onRender : function(ct, position)
22942     {
22943        // Roo.log("Call onRender: " + this.xtype);
22944         var _t = this;
22945         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22946       
22947         this.wrap = this.inputEl().wrap({
22948             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22949         });
22950         
22951         this.editorcore.onRender(ct, position);
22952          
22953         if (this.resizable) {
22954             this.resizeEl = new Roo.Resizable(this.wrap, {
22955                 pinned : true,
22956                 wrap: true,
22957                 dynamic : true,
22958                 minHeight : this.height,
22959                 height: this.height,
22960                 handles : this.resizable,
22961                 width: this.width,
22962                 listeners : {
22963                     resize : function(r, w, h) {
22964                         _t.onResize(w,h); // -something
22965                     }
22966                 }
22967             });
22968             
22969         }
22970         this.createToolbar(this);
22971        
22972         
22973         if(!this.width && this.resizable){
22974             this.setSize(this.wrap.getSize());
22975         }
22976         if (this.resizeEl) {
22977             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22978             // should trigger onReize..
22979         }
22980         
22981     },
22982
22983     // private
22984     onResize : function(w, h)
22985     {
22986         Roo.log('resize: ' +w + ',' + h );
22987         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22988         var ew = false;
22989         var eh = false;
22990         
22991         if(this.inputEl() ){
22992             if(typeof w == 'number'){
22993                 var aw = w - this.wrap.getFrameWidth('lr');
22994                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22995                 ew = aw;
22996             }
22997             if(typeof h == 'number'){
22998                  var tbh = -11;  // fixme it needs to tool bar size!
22999                 for (var i =0; i < this.toolbars.length;i++) {
23000                     // fixme - ask toolbars for heights?
23001                     tbh += this.toolbars[i].el.getHeight();
23002                     //if (this.toolbars[i].footer) {
23003                     //    tbh += this.toolbars[i].footer.el.getHeight();
23004                     //}
23005                 }
23006               
23007                 
23008                 
23009                 
23010                 
23011                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23012                 ah -= 5; // knock a few pixes off for look..
23013                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23014                 var eh = ah;
23015             }
23016         }
23017         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23018         this.editorcore.onResize(ew,eh);
23019         
23020     },
23021
23022     /**
23023      * Toggles the editor between standard and source edit mode.
23024      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23025      */
23026     toggleSourceEdit : function(sourceEditMode)
23027     {
23028         this.editorcore.toggleSourceEdit(sourceEditMode);
23029         
23030         if(this.editorcore.sourceEditMode){
23031             Roo.log('editor - showing textarea');
23032             
23033 //            Roo.log('in');
23034 //            Roo.log(this.syncValue());
23035             this.syncValue();
23036             this.inputEl().removeClass(['hide', 'x-hidden']);
23037             this.inputEl().dom.removeAttribute('tabIndex');
23038             this.inputEl().focus();
23039         }else{
23040             Roo.log('editor - hiding textarea');
23041 //            Roo.log('out')
23042 //            Roo.log(this.pushValue()); 
23043             this.pushValue();
23044             
23045             this.inputEl().addClass(['hide', 'x-hidden']);
23046             this.inputEl().dom.setAttribute('tabIndex', -1);
23047             //this.deferFocus();
23048         }
23049          
23050         if(this.resizable){
23051             this.setSize(this.wrap.getSize());
23052         }
23053         
23054         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23055     },
23056  
23057     // private (for BoxComponent)
23058     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23059
23060     // private (for BoxComponent)
23061     getResizeEl : function(){
23062         return this.wrap;
23063     },
23064
23065     // private (for BoxComponent)
23066     getPositionEl : function(){
23067         return this.wrap;
23068     },
23069
23070     // private
23071     initEvents : function(){
23072         this.originalValue = this.getValue();
23073     },
23074
23075 //    /**
23076 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23077 //     * @method
23078 //     */
23079 //    markInvalid : Roo.emptyFn,
23080 //    /**
23081 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23082 //     * @method
23083 //     */
23084 //    clearInvalid : Roo.emptyFn,
23085
23086     setValue : function(v){
23087         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23088         this.editorcore.pushValue();
23089     },
23090
23091      
23092     // private
23093     deferFocus : function(){
23094         this.focus.defer(10, this);
23095     },
23096
23097     // doc'ed in Field
23098     focus : function(){
23099         this.editorcore.focus();
23100         
23101     },
23102       
23103
23104     // private
23105     onDestroy : function(){
23106         
23107         
23108         
23109         if(this.rendered){
23110             
23111             for (var i =0; i < this.toolbars.length;i++) {
23112                 // fixme - ask toolbars for heights?
23113                 this.toolbars[i].onDestroy();
23114             }
23115             
23116             this.wrap.dom.innerHTML = '';
23117             this.wrap.remove();
23118         }
23119     },
23120
23121     // private
23122     onFirstFocus : function(){
23123         //Roo.log("onFirstFocus");
23124         this.editorcore.onFirstFocus();
23125          for (var i =0; i < this.toolbars.length;i++) {
23126             this.toolbars[i].onFirstFocus();
23127         }
23128         
23129     },
23130     
23131     // private
23132     syncValue : function()
23133     {   
23134         this.editorcore.syncValue();
23135     },
23136     
23137     pushValue : function()
23138     {   
23139         this.editorcore.pushValue();
23140     }
23141      
23142     
23143     // hide stuff that is not compatible
23144     /**
23145      * @event blur
23146      * @hide
23147      */
23148     /**
23149      * @event change
23150      * @hide
23151      */
23152     /**
23153      * @event focus
23154      * @hide
23155      */
23156     /**
23157      * @event specialkey
23158      * @hide
23159      */
23160     /**
23161      * @cfg {String} fieldClass @hide
23162      */
23163     /**
23164      * @cfg {String} focusClass @hide
23165      */
23166     /**
23167      * @cfg {String} autoCreate @hide
23168      */
23169     /**
23170      * @cfg {String} inputType @hide
23171      */
23172     /**
23173      * @cfg {String} invalidClass @hide
23174      */
23175     /**
23176      * @cfg {String} invalidText @hide
23177      */
23178     /**
23179      * @cfg {String} msgFx @hide
23180      */
23181     /**
23182      * @cfg {String} validateOnBlur @hide
23183      */
23184 });
23185  
23186     
23187    
23188    
23189    
23190       
23191 Roo.namespace('Roo.bootstrap.htmleditor');
23192 /**
23193  * @class Roo.bootstrap.HtmlEditorToolbar1
23194  * Basic Toolbar
23195  * 
23196  * Usage:
23197  *
23198  new Roo.bootstrap.HtmlEditor({
23199     ....
23200     toolbars : [
23201         new Roo.bootstrap.HtmlEditorToolbar1({
23202             disable : { fonts: 1 , format: 1, ..., ... , ...],
23203             btns : [ .... ]
23204         })
23205     }
23206      
23207  * 
23208  * @cfg {Object} disable List of elements to disable..
23209  * @cfg {Array} btns List of additional buttons.
23210  * 
23211  * 
23212  * NEEDS Extra CSS? 
23213  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23214  */
23215  
23216 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23217 {
23218     
23219     Roo.apply(this, config);
23220     
23221     // default disabled, based on 'good practice'..
23222     this.disable = this.disable || {};
23223     Roo.applyIf(this.disable, {
23224         fontSize : true,
23225         colors : true,
23226         specialElements : true
23227     });
23228     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23229     
23230     this.editor = config.editor;
23231     this.editorcore = config.editor.editorcore;
23232     
23233     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23234     
23235     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23236     // dont call parent... till later.
23237 }
23238 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23239      
23240     bar : true,
23241     
23242     editor : false,
23243     editorcore : false,
23244     
23245     
23246     formats : [
23247         "p" ,  
23248         "h1","h2","h3","h4","h5","h6", 
23249         "pre", "code", 
23250         "abbr", "acronym", "address", "cite", "samp", "var",
23251         'div','span'
23252     ],
23253     
23254     onRender : function(ct, position)
23255     {
23256        // Roo.log("Call onRender: " + this.xtype);
23257         
23258        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23259        Roo.log(this.el);
23260        this.el.dom.style.marginBottom = '0';
23261        var _this = this;
23262        var editorcore = this.editorcore;
23263        var editor= this.editor;
23264        
23265        var children = [];
23266        var btn = function(id,cmd , toggle, handler, html){
23267        
23268             var  event = toggle ? 'toggle' : 'click';
23269        
23270             var a = {
23271                 size : 'sm',
23272                 xtype: 'Button',
23273                 xns: Roo.bootstrap,
23274                 glyphicon : id,
23275                 cmd : id || cmd,
23276                 enableToggle:toggle !== false,
23277                 html : html || '',
23278                 pressed : toggle ? false : null,
23279                 listeners : {}
23280             };
23281             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23282                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23283             };
23284             children.push(a);
23285             return a;
23286        }
23287        
23288     //    var cb_box = function...
23289         
23290         var style = {
23291                 xtype: 'Button',
23292                 size : 'sm',
23293                 xns: Roo.bootstrap,
23294                 glyphicon : 'font',
23295                 //html : 'submit'
23296                 menu : {
23297                     xtype: 'Menu',
23298                     xns: Roo.bootstrap,
23299                     items:  []
23300                 }
23301         };
23302         Roo.each(this.formats, function(f) {
23303             style.menu.items.push({
23304                 xtype :'MenuItem',
23305                 xns: Roo.bootstrap,
23306                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23307                 tagname : f,
23308                 listeners : {
23309                     click : function()
23310                     {
23311                         editorcore.insertTag(this.tagname);
23312                         editor.focus();
23313                     }
23314                 }
23315                 
23316             });
23317         });
23318         children.push(style);   
23319         
23320         btn('bold',false,true);
23321         btn('italic',false,true);
23322         btn('align-left', 'justifyleft',true);
23323         btn('align-center', 'justifycenter',true);
23324         btn('align-right' , 'justifyright',true);
23325         btn('link', false, false, function(btn) {
23326             //Roo.log("create link?");
23327             var url = prompt(this.createLinkText, this.defaultLinkValue);
23328             if(url && url != 'http:/'+'/'){
23329                 this.editorcore.relayCmd('createlink', url);
23330             }
23331         }),
23332         btn('list','insertunorderedlist',true);
23333         btn('pencil', false,true, function(btn){
23334                 Roo.log(this);
23335                 this.toggleSourceEdit(btn.pressed);
23336         });
23337         
23338         if (this.editor.btns.length > 0) {
23339             for (var i = 0; i<this.editor.btns.length; i++) {
23340                 children.push(this.editor.btns[i]);
23341             }
23342         }
23343         
23344         /*
23345         var cog = {
23346                 xtype: 'Button',
23347                 size : 'sm',
23348                 xns: Roo.bootstrap,
23349                 glyphicon : 'cog',
23350                 //html : 'submit'
23351                 menu : {
23352                     xtype: 'Menu',
23353                     xns: Roo.bootstrap,
23354                     items:  []
23355                 }
23356         };
23357         
23358         cog.menu.items.push({
23359             xtype :'MenuItem',
23360             xns: Roo.bootstrap,
23361             html : Clean styles,
23362             tagname : f,
23363             listeners : {
23364                 click : function()
23365                 {
23366                     editorcore.insertTag(this.tagname);
23367                     editor.focus();
23368                 }
23369             }
23370             
23371         });
23372        */
23373         
23374          
23375        this.xtype = 'NavSimplebar';
23376         
23377         for(var i=0;i< children.length;i++) {
23378             
23379             this.buttons.add(this.addxtypeChild(children[i]));
23380             
23381         }
23382         
23383         editor.on('editorevent', this.updateToolbar, this);
23384     },
23385     onBtnClick : function(id)
23386     {
23387        this.editorcore.relayCmd(id);
23388        this.editorcore.focus();
23389     },
23390     
23391     /**
23392      * Protected method that will not generally be called directly. It triggers
23393      * a toolbar update by reading the markup state of the current selection in the editor.
23394      */
23395     updateToolbar: function(){
23396
23397         if(!this.editorcore.activated){
23398             this.editor.onFirstFocus(); // is this neeed?
23399             return;
23400         }
23401
23402         var btns = this.buttons; 
23403         var doc = this.editorcore.doc;
23404         btns.get('bold').setActive(doc.queryCommandState('bold'));
23405         btns.get('italic').setActive(doc.queryCommandState('italic'));
23406         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23407         
23408         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23409         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23410         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23411         
23412         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23413         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23414          /*
23415         
23416         var ans = this.editorcore.getAllAncestors();
23417         if (this.formatCombo) {
23418             
23419             
23420             var store = this.formatCombo.store;
23421             this.formatCombo.setValue("");
23422             for (var i =0; i < ans.length;i++) {
23423                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23424                     // select it..
23425                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23426                     break;
23427                 }
23428             }
23429         }
23430         
23431         
23432         
23433         // hides menus... - so this cant be on a menu...
23434         Roo.bootstrap.MenuMgr.hideAll();
23435         */
23436         Roo.bootstrap.MenuMgr.hideAll();
23437         //this.editorsyncValue();
23438     },
23439     onFirstFocus: function() {
23440         this.buttons.each(function(item){
23441            item.enable();
23442         });
23443     },
23444     toggleSourceEdit : function(sourceEditMode){
23445         
23446           
23447         if(sourceEditMode){
23448             Roo.log("disabling buttons");
23449            this.buttons.each( function(item){
23450                 if(item.cmd != 'pencil'){
23451                     item.disable();
23452                 }
23453             });
23454           
23455         }else{
23456             Roo.log("enabling buttons");
23457             if(this.editorcore.initialized){
23458                 this.buttons.each( function(item){
23459                     item.enable();
23460                 });
23461             }
23462             
23463         }
23464         Roo.log("calling toggole on editor");
23465         // tell the editor that it's been pressed..
23466         this.editor.toggleSourceEdit(sourceEditMode);
23467        
23468     }
23469 });
23470
23471
23472
23473
23474
23475 /**
23476  * @class Roo.bootstrap.Table.AbstractSelectionModel
23477  * @extends Roo.util.Observable
23478  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23479  * implemented by descendant classes.  This class should not be directly instantiated.
23480  * @constructor
23481  */
23482 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23483     this.locked = false;
23484     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23485 };
23486
23487
23488 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23489     /** @ignore Called by the grid automatically. Do not call directly. */
23490     init : function(grid){
23491         this.grid = grid;
23492         this.initEvents();
23493     },
23494
23495     /**
23496      * Locks the selections.
23497      */
23498     lock : function(){
23499         this.locked = true;
23500     },
23501
23502     /**
23503      * Unlocks the selections.
23504      */
23505     unlock : function(){
23506         this.locked = false;
23507     },
23508
23509     /**
23510      * Returns true if the selections are locked.
23511      * @return {Boolean}
23512      */
23513     isLocked : function(){
23514         return this.locked;
23515     }
23516 });
23517 /**
23518  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23519  * @class Roo.bootstrap.Table.RowSelectionModel
23520  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23521  * It supports multiple selections and keyboard selection/navigation. 
23522  * @constructor
23523  * @param {Object} config
23524  */
23525
23526 Roo.bootstrap.Table.RowSelectionModel = function(config){
23527     Roo.apply(this, config);
23528     this.selections = new Roo.util.MixedCollection(false, function(o){
23529         return o.id;
23530     });
23531
23532     this.last = false;
23533     this.lastActive = false;
23534
23535     this.addEvents({
23536         /**
23537              * @event selectionchange
23538              * Fires when the selection changes
23539              * @param {SelectionModel} this
23540              */
23541             "selectionchange" : true,
23542         /**
23543              * @event afterselectionchange
23544              * Fires after the selection changes (eg. by key press or clicking)
23545              * @param {SelectionModel} this
23546              */
23547             "afterselectionchange" : true,
23548         /**
23549              * @event beforerowselect
23550              * Fires when a row is selected being selected, return false to cancel.
23551              * @param {SelectionModel} this
23552              * @param {Number} rowIndex The selected index
23553              * @param {Boolean} keepExisting False if other selections will be cleared
23554              */
23555             "beforerowselect" : true,
23556         /**
23557              * @event rowselect
23558              * Fires when a row is selected.
23559              * @param {SelectionModel} this
23560              * @param {Number} rowIndex The selected index
23561              * @param {Roo.data.Record} r The record
23562              */
23563             "rowselect" : true,
23564         /**
23565              * @event rowdeselect
23566              * Fires when a row is deselected.
23567              * @param {SelectionModel} this
23568              * @param {Number} rowIndex The selected index
23569              */
23570         "rowdeselect" : true
23571     });
23572     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23573     this.locked = false;
23574  };
23575
23576 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23577     /**
23578      * @cfg {Boolean} singleSelect
23579      * True to allow selection of only one row at a time (defaults to false)
23580      */
23581     singleSelect : false,
23582
23583     // private
23584     initEvents : function()
23585     {
23586
23587         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23588         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23589         //}else{ // allow click to work like normal
23590          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23591         //}
23592         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23593         this.grid.on("rowclick", this.handleMouseDown, this);
23594         
23595         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23596             "up" : function(e){
23597                 if(!e.shiftKey){
23598                     this.selectPrevious(e.shiftKey);
23599                 }else if(this.last !== false && this.lastActive !== false){
23600                     var last = this.last;
23601                     this.selectRange(this.last,  this.lastActive-1);
23602                     this.grid.getView().focusRow(this.lastActive);
23603                     if(last !== false){
23604                         this.last = last;
23605                     }
23606                 }else{
23607                     this.selectFirstRow();
23608                 }
23609                 this.fireEvent("afterselectionchange", this);
23610             },
23611             "down" : function(e){
23612                 if(!e.shiftKey){
23613                     this.selectNext(e.shiftKey);
23614                 }else if(this.last !== false && this.lastActive !== false){
23615                     var last = this.last;
23616                     this.selectRange(this.last,  this.lastActive+1);
23617                     this.grid.getView().focusRow(this.lastActive);
23618                     if(last !== false){
23619                         this.last = last;
23620                     }
23621                 }else{
23622                     this.selectFirstRow();
23623                 }
23624                 this.fireEvent("afterselectionchange", this);
23625             },
23626             scope: this
23627         });
23628         this.grid.store.on('load', function(){
23629             this.selections.clear();
23630         },this);
23631         /*
23632         var view = this.grid.view;
23633         view.on("refresh", this.onRefresh, this);
23634         view.on("rowupdated", this.onRowUpdated, this);
23635         view.on("rowremoved", this.onRemove, this);
23636         */
23637     },
23638
23639     // private
23640     onRefresh : function()
23641     {
23642         var ds = this.grid.store, i, v = this.grid.view;
23643         var s = this.selections;
23644         s.each(function(r){
23645             if((i = ds.indexOfId(r.id)) != -1){
23646                 v.onRowSelect(i);
23647             }else{
23648                 s.remove(r);
23649             }
23650         });
23651     },
23652
23653     // private
23654     onRemove : function(v, index, r){
23655         this.selections.remove(r);
23656     },
23657
23658     // private
23659     onRowUpdated : function(v, index, r){
23660         if(this.isSelected(r)){
23661             v.onRowSelect(index);
23662         }
23663     },
23664
23665     /**
23666      * Select records.
23667      * @param {Array} records The records to select
23668      * @param {Boolean} keepExisting (optional) True to keep existing selections
23669      */
23670     selectRecords : function(records, keepExisting)
23671     {
23672         if(!keepExisting){
23673             this.clearSelections();
23674         }
23675             var ds = this.grid.store;
23676         for(var i = 0, len = records.length; i < len; i++){
23677             this.selectRow(ds.indexOf(records[i]), true);
23678         }
23679     },
23680
23681     /**
23682      * Gets the number of selected rows.
23683      * @return {Number}
23684      */
23685     getCount : function(){
23686         return this.selections.length;
23687     },
23688
23689     /**
23690      * Selects the first row in the grid.
23691      */
23692     selectFirstRow : function(){
23693         this.selectRow(0);
23694     },
23695
23696     /**
23697      * Select the last row.
23698      * @param {Boolean} keepExisting (optional) True to keep existing selections
23699      */
23700     selectLastRow : function(keepExisting){
23701         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23702         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23703     },
23704
23705     /**
23706      * Selects the row immediately following the last selected row.
23707      * @param {Boolean} keepExisting (optional) True to keep existing selections
23708      */
23709     selectNext : function(keepExisting)
23710     {
23711             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23712             this.selectRow(this.last+1, keepExisting);
23713             this.grid.getView().focusRow(this.last);
23714         }
23715     },
23716
23717     /**
23718      * Selects the row that precedes the last selected row.
23719      * @param {Boolean} keepExisting (optional) True to keep existing selections
23720      */
23721     selectPrevious : function(keepExisting){
23722         if(this.last){
23723             this.selectRow(this.last-1, keepExisting);
23724             this.grid.getView().focusRow(this.last);
23725         }
23726     },
23727
23728     /**
23729      * Returns the selected records
23730      * @return {Array} Array of selected records
23731      */
23732     getSelections : function(){
23733         return [].concat(this.selections.items);
23734     },
23735
23736     /**
23737      * Returns the first selected record.
23738      * @return {Record}
23739      */
23740     getSelected : function(){
23741         return this.selections.itemAt(0);
23742     },
23743
23744
23745     /**
23746      * Clears all selections.
23747      */
23748     clearSelections : function(fast)
23749     {
23750         if(this.locked) {
23751             return;
23752         }
23753         if(fast !== true){
23754                 var ds = this.grid.store;
23755             var s = this.selections;
23756             s.each(function(r){
23757                 this.deselectRow(ds.indexOfId(r.id));
23758             }, this);
23759             s.clear();
23760         }else{
23761             this.selections.clear();
23762         }
23763         this.last = false;
23764     },
23765
23766
23767     /**
23768      * Selects all rows.
23769      */
23770     selectAll : function(){
23771         if(this.locked) {
23772             return;
23773         }
23774         this.selections.clear();
23775         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23776             this.selectRow(i, true);
23777         }
23778     },
23779
23780     /**
23781      * Returns True if there is a selection.
23782      * @return {Boolean}
23783      */
23784     hasSelection : function(){
23785         return this.selections.length > 0;
23786     },
23787
23788     /**
23789      * Returns True if the specified row is selected.
23790      * @param {Number/Record} record The record or index of the record to check
23791      * @return {Boolean}
23792      */
23793     isSelected : function(index){
23794             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23795         return (r && this.selections.key(r.id) ? true : false);
23796     },
23797
23798     /**
23799      * Returns True if the specified record id is selected.
23800      * @param {String} id The id of record to check
23801      * @return {Boolean}
23802      */
23803     isIdSelected : function(id){
23804         return (this.selections.key(id) ? true : false);
23805     },
23806
23807
23808     // private
23809     handleMouseDBClick : function(e, t){
23810         
23811     },
23812     // private
23813     handleMouseDown : function(e, t)
23814     {
23815             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23816         if(this.isLocked() || rowIndex < 0 ){
23817             return;
23818         };
23819         if(e.shiftKey && this.last !== false){
23820             var last = this.last;
23821             this.selectRange(last, rowIndex, e.ctrlKey);
23822             this.last = last; // reset the last
23823             t.focus();
23824     
23825         }else{
23826             var isSelected = this.isSelected(rowIndex);
23827             //Roo.log("select row:" + rowIndex);
23828             if(isSelected){
23829                 this.deselectRow(rowIndex);
23830             } else {
23831                         this.selectRow(rowIndex, true);
23832             }
23833     
23834             /*
23835                 if(e.button !== 0 && isSelected){
23836                 alert('rowIndex 2: ' + rowIndex);
23837                     view.focusRow(rowIndex);
23838                 }else if(e.ctrlKey && isSelected){
23839                     this.deselectRow(rowIndex);
23840                 }else if(!isSelected){
23841                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23842                     view.focusRow(rowIndex);
23843                 }
23844             */
23845         }
23846         this.fireEvent("afterselectionchange", this);
23847     },
23848     // private
23849     handleDragableRowClick :  function(grid, rowIndex, e) 
23850     {
23851         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23852             this.selectRow(rowIndex, false);
23853             grid.view.focusRow(rowIndex);
23854              this.fireEvent("afterselectionchange", this);
23855         }
23856     },
23857     
23858     /**
23859      * Selects multiple rows.
23860      * @param {Array} rows Array of the indexes of the row to select
23861      * @param {Boolean} keepExisting (optional) True to keep existing selections
23862      */
23863     selectRows : function(rows, keepExisting){
23864         if(!keepExisting){
23865             this.clearSelections();
23866         }
23867         for(var i = 0, len = rows.length; i < len; i++){
23868             this.selectRow(rows[i], true);
23869         }
23870     },
23871
23872     /**
23873      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23874      * @param {Number} startRow The index of the first row in the range
23875      * @param {Number} endRow The index of the last row in the range
23876      * @param {Boolean} keepExisting (optional) True to retain existing selections
23877      */
23878     selectRange : function(startRow, endRow, keepExisting){
23879         if(this.locked) {
23880             return;
23881         }
23882         if(!keepExisting){
23883             this.clearSelections();
23884         }
23885         if(startRow <= endRow){
23886             for(var i = startRow; i <= endRow; i++){
23887                 this.selectRow(i, true);
23888             }
23889         }else{
23890             for(var i = startRow; i >= endRow; i--){
23891                 this.selectRow(i, true);
23892             }
23893         }
23894     },
23895
23896     /**
23897      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23898      * @param {Number} startRow The index of the first row in the range
23899      * @param {Number} endRow The index of the last row in the range
23900      */
23901     deselectRange : function(startRow, endRow, preventViewNotify){
23902         if(this.locked) {
23903             return;
23904         }
23905         for(var i = startRow; i <= endRow; i++){
23906             this.deselectRow(i, preventViewNotify);
23907         }
23908     },
23909
23910     /**
23911      * Selects a row.
23912      * @param {Number} row The index of the row to select
23913      * @param {Boolean} keepExisting (optional) True to keep existing selections
23914      */
23915     selectRow : function(index, keepExisting, preventViewNotify)
23916     {
23917             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23918             return;
23919         }
23920         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23921             if(!keepExisting || this.singleSelect){
23922                 this.clearSelections();
23923             }
23924             
23925             var r = this.grid.store.getAt(index);
23926             //console.log('selectRow - record id :' + r.id);
23927             
23928             this.selections.add(r);
23929             this.last = this.lastActive = index;
23930             if(!preventViewNotify){
23931                 var proxy = new Roo.Element(
23932                                 this.grid.getRowDom(index)
23933                 );
23934                 proxy.addClass('bg-info info');
23935             }
23936             this.fireEvent("rowselect", this, index, r);
23937             this.fireEvent("selectionchange", this);
23938         }
23939     },
23940
23941     /**
23942      * Deselects a row.
23943      * @param {Number} row The index of the row to deselect
23944      */
23945     deselectRow : function(index, preventViewNotify)
23946     {
23947         if(this.locked) {
23948             return;
23949         }
23950         if(this.last == index){
23951             this.last = false;
23952         }
23953         if(this.lastActive == index){
23954             this.lastActive = false;
23955         }
23956         
23957         var r = this.grid.store.getAt(index);
23958         if (!r) {
23959             return;
23960         }
23961         
23962         this.selections.remove(r);
23963         //.console.log('deselectRow - record id :' + r.id);
23964         if(!preventViewNotify){
23965         
23966             var proxy = new Roo.Element(
23967                 this.grid.getRowDom(index)
23968             );
23969             proxy.removeClass('bg-info info');
23970         }
23971         this.fireEvent("rowdeselect", this, index);
23972         this.fireEvent("selectionchange", this);
23973     },
23974
23975     // private
23976     restoreLast : function(){
23977         if(this._last){
23978             this.last = this._last;
23979         }
23980     },
23981
23982     // private
23983     acceptsNav : function(row, col, cm){
23984         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23985     },
23986
23987     // private
23988     onEditorKey : function(field, e){
23989         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23990         if(k == e.TAB){
23991             e.stopEvent();
23992             ed.completeEdit();
23993             if(e.shiftKey){
23994                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23995             }else{
23996                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23997             }
23998         }else if(k == e.ENTER && !e.ctrlKey){
23999             e.stopEvent();
24000             ed.completeEdit();
24001             if(e.shiftKey){
24002                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24003             }else{
24004                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24005             }
24006         }else if(k == e.ESC){
24007             ed.cancelEdit();
24008         }
24009         if(newCell){
24010             g.startEditing(newCell[0], newCell[1]);
24011         }
24012     }
24013 });
24014 /*
24015  * Based on:
24016  * Ext JS Library 1.1.1
24017  * Copyright(c) 2006-2007, Ext JS, LLC.
24018  *
24019  * Originally Released Under LGPL - original licence link has changed is not relivant.
24020  *
24021  * Fork - LGPL
24022  * <script type="text/javascript">
24023  */
24024  
24025 /**
24026  * @class Roo.bootstrap.PagingToolbar
24027  * @extends Roo.bootstrap.NavSimplebar
24028  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24029  * @constructor
24030  * Create a new PagingToolbar
24031  * @param {Object} config The config object
24032  * @param {Roo.data.Store} store
24033  */
24034 Roo.bootstrap.PagingToolbar = function(config)
24035 {
24036     // old args format still supported... - xtype is prefered..
24037         // created from xtype...
24038     
24039     this.ds = config.dataSource;
24040     
24041     if (config.store && !this.ds) {
24042         this.store= Roo.factory(config.store, Roo.data);
24043         this.ds = this.store;
24044         this.ds.xmodule = this.xmodule || false;
24045     }
24046     
24047     this.toolbarItems = [];
24048     if (config.items) {
24049         this.toolbarItems = config.items;
24050     }
24051     
24052     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24053     
24054     this.cursor = 0;
24055     
24056     if (this.ds) { 
24057         this.bind(this.ds);
24058     }
24059     
24060     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24061     
24062 };
24063
24064 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24065     /**
24066      * @cfg {Roo.data.Store} dataSource
24067      * The underlying data store providing the paged data
24068      */
24069     /**
24070      * @cfg {String/HTMLElement/Element} container
24071      * container The id or element that will contain the toolbar
24072      */
24073     /**
24074      * @cfg {Boolean} displayInfo
24075      * True to display the displayMsg (defaults to false)
24076      */
24077     /**
24078      * @cfg {Number} pageSize
24079      * The number of records to display per page (defaults to 20)
24080      */
24081     pageSize: 20,
24082     /**
24083      * @cfg {String} displayMsg
24084      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24085      */
24086     displayMsg : 'Displaying {0} - {1} of {2}',
24087     /**
24088      * @cfg {String} emptyMsg
24089      * The message to display when no records are found (defaults to "No data to display")
24090      */
24091     emptyMsg : 'No data to display',
24092     /**
24093      * Customizable piece of the default paging text (defaults to "Page")
24094      * @type String
24095      */
24096     beforePageText : "Page",
24097     /**
24098      * Customizable piece of the default paging text (defaults to "of %0")
24099      * @type String
24100      */
24101     afterPageText : "of {0}",
24102     /**
24103      * Customizable piece of the default paging text (defaults to "First Page")
24104      * @type String
24105      */
24106     firstText : "First Page",
24107     /**
24108      * Customizable piece of the default paging text (defaults to "Previous Page")
24109      * @type String
24110      */
24111     prevText : "Previous Page",
24112     /**
24113      * Customizable piece of the default paging text (defaults to "Next Page")
24114      * @type String
24115      */
24116     nextText : "Next Page",
24117     /**
24118      * Customizable piece of the default paging text (defaults to "Last Page")
24119      * @type String
24120      */
24121     lastText : "Last Page",
24122     /**
24123      * Customizable piece of the default paging text (defaults to "Refresh")
24124      * @type String
24125      */
24126     refreshText : "Refresh",
24127
24128     buttons : false,
24129     // private
24130     onRender : function(ct, position) 
24131     {
24132         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24133         this.navgroup.parentId = this.id;
24134         this.navgroup.onRender(this.el, null);
24135         // add the buttons to the navgroup
24136         
24137         if(this.displayInfo){
24138             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24139             this.displayEl = this.el.select('.x-paging-info', true).first();
24140 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24141 //            this.displayEl = navel.el.select('span',true).first();
24142         }
24143         
24144         var _this = this;
24145         
24146         if(this.buttons){
24147             Roo.each(_this.buttons, function(e){ // this might need to use render????
24148                Roo.factory(e).onRender(_this.el, null);
24149             });
24150         }
24151             
24152         Roo.each(_this.toolbarItems, function(e) {
24153             _this.navgroup.addItem(e);
24154         });
24155         
24156         
24157         this.first = this.navgroup.addItem({
24158             tooltip: this.firstText,
24159             cls: "prev",
24160             icon : 'fa fa-backward',
24161             disabled: true,
24162             preventDefault: true,
24163             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24164         });
24165         
24166         this.prev =  this.navgroup.addItem({
24167             tooltip: this.prevText,
24168             cls: "prev",
24169             icon : 'fa fa-step-backward',
24170             disabled: true,
24171             preventDefault: true,
24172             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24173         });
24174     //this.addSeparator();
24175         
24176         
24177         var field = this.navgroup.addItem( {
24178             tagtype : 'span',
24179             cls : 'x-paging-position',
24180             
24181             html : this.beforePageText  +
24182                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24183                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24184          } ); //?? escaped?
24185         
24186         this.field = field.el.select('input', true).first();
24187         this.field.on("keydown", this.onPagingKeydown, this);
24188         this.field.on("focus", function(){this.dom.select();});
24189     
24190     
24191         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24192         //this.field.setHeight(18);
24193         //this.addSeparator();
24194         this.next = this.navgroup.addItem({
24195             tooltip: this.nextText,
24196             cls: "next",
24197             html : ' <i class="fa fa-step-forward">',
24198             disabled: true,
24199             preventDefault: true,
24200             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24201         });
24202         this.last = this.navgroup.addItem({
24203             tooltip: this.lastText,
24204             icon : 'fa fa-forward',
24205             cls: "next",
24206             disabled: true,
24207             preventDefault: true,
24208             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24209         });
24210     //this.addSeparator();
24211         this.loading = this.navgroup.addItem({
24212             tooltip: this.refreshText,
24213             icon: 'fa fa-refresh',
24214             preventDefault: true,
24215             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24216         });
24217         
24218     },
24219
24220     // private
24221     updateInfo : function(){
24222         if(this.displayEl){
24223             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24224             var msg = count == 0 ?
24225                 this.emptyMsg :
24226                 String.format(
24227                     this.displayMsg,
24228                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24229                 );
24230             this.displayEl.update(msg);
24231         }
24232     },
24233
24234     // private
24235     onLoad : function(ds, r, o)
24236     {
24237         this.cursor = o.params ? o.params.start : 0;
24238         var d = this.getPageData(),
24239             ap = d.activePage,
24240             ps = d.pages;
24241         
24242         
24243         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24244         this.field.dom.value = ap;
24245         this.first.setDisabled(ap == 1);
24246         this.prev.setDisabled(ap == 1);
24247         this.next.setDisabled(ap == ps);
24248         this.last.setDisabled(ap == ps);
24249         this.loading.enable();
24250         this.updateInfo();
24251     },
24252
24253     // private
24254     getPageData : function(){
24255         var total = this.ds.getTotalCount();
24256         return {
24257             total : total,
24258             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24259             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24260         };
24261     },
24262
24263     // private
24264     onLoadError : function(){
24265         this.loading.enable();
24266     },
24267
24268     // private
24269     onPagingKeydown : function(e){
24270         var k = e.getKey();
24271         var d = this.getPageData();
24272         if(k == e.RETURN){
24273             var v = this.field.dom.value, pageNum;
24274             if(!v || isNaN(pageNum = parseInt(v, 10))){
24275                 this.field.dom.value = d.activePage;
24276                 return;
24277             }
24278             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24279             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24280             e.stopEvent();
24281         }
24282         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))
24283         {
24284           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24285           this.field.dom.value = pageNum;
24286           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24287           e.stopEvent();
24288         }
24289         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24290         {
24291           var v = this.field.dom.value, pageNum; 
24292           var increment = (e.shiftKey) ? 10 : 1;
24293           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24294                 increment *= -1;
24295           }
24296           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24297             this.field.dom.value = d.activePage;
24298             return;
24299           }
24300           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24301           {
24302             this.field.dom.value = parseInt(v, 10) + increment;
24303             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24304             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24305           }
24306           e.stopEvent();
24307         }
24308     },
24309
24310     // private
24311     beforeLoad : function(){
24312         if(this.loading){
24313             this.loading.disable();
24314         }
24315     },
24316
24317     // private
24318     onClick : function(which){
24319         
24320         var ds = this.ds;
24321         if (!ds) {
24322             return;
24323         }
24324         
24325         switch(which){
24326             case "first":
24327                 ds.load({params:{start: 0, limit: this.pageSize}});
24328             break;
24329             case "prev":
24330                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24331             break;
24332             case "next":
24333                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24334             break;
24335             case "last":
24336                 var total = ds.getTotalCount();
24337                 var extra = total % this.pageSize;
24338                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24339                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24340             break;
24341             case "refresh":
24342                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24343             break;
24344         }
24345     },
24346
24347     /**
24348      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24349      * @param {Roo.data.Store} store The data store to unbind
24350      */
24351     unbind : function(ds){
24352         ds.un("beforeload", this.beforeLoad, this);
24353         ds.un("load", this.onLoad, this);
24354         ds.un("loadexception", this.onLoadError, this);
24355         ds.un("remove", this.updateInfo, this);
24356         ds.un("add", this.updateInfo, this);
24357         this.ds = undefined;
24358     },
24359
24360     /**
24361      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24362      * @param {Roo.data.Store} store The data store to bind
24363      */
24364     bind : function(ds){
24365         ds.on("beforeload", this.beforeLoad, this);
24366         ds.on("load", this.onLoad, this);
24367         ds.on("loadexception", this.onLoadError, this);
24368         ds.on("remove", this.updateInfo, this);
24369         ds.on("add", this.updateInfo, this);
24370         this.ds = ds;
24371     }
24372 });/*
24373  * - LGPL
24374  *
24375  * element
24376  * 
24377  */
24378
24379 /**
24380  * @class Roo.bootstrap.MessageBar
24381  * @extends Roo.bootstrap.Component
24382  * Bootstrap MessageBar class
24383  * @cfg {String} html contents of the MessageBar
24384  * @cfg {String} weight (info | success | warning | danger) default info
24385  * @cfg {String} beforeClass insert the bar before the given class
24386  * @cfg {Boolean} closable (true | false) default false
24387  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24388  * 
24389  * @constructor
24390  * Create a new Element
24391  * @param {Object} config The config object
24392  */
24393
24394 Roo.bootstrap.MessageBar = function(config){
24395     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24396 };
24397
24398 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24399     
24400     html: '',
24401     weight: 'info',
24402     closable: false,
24403     fixed: false,
24404     beforeClass: 'bootstrap-sticky-wrap',
24405     
24406     getAutoCreate : function(){
24407         
24408         var cfg = {
24409             tag: 'div',
24410             cls: 'alert alert-dismissable alert-' + this.weight,
24411             cn: [
24412                 {
24413                     tag: 'span',
24414                     cls: 'message',
24415                     html: this.html || ''
24416                 }
24417             ]
24418         };
24419         
24420         if(this.fixed){
24421             cfg.cls += ' alert-messages-fixed';
24422         }
24423         
24424         if(this.closable){
24425             cfg.cn.push({
24426                 tag: 'button',
24427                 cls: 'close',
24428                 html: 'x'
24429             });
24430         }
24431         
24432         return cfg;
24433     },
24434     
24435     onRender : function(ct, position)
24436     {
24437         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24438         
24439         if(!this.el){
24440             var cfg = Roo.apply({},  this.getAutoCreate());
24441             cfg.id = Roo.id();
24442             
24443             if (this.cls) {
24444                 cfg.cls += ' ' + this.cls;
24445             }
24446             if (this.style) {
24447                 cfg.style = this.style;
24448             }
24449             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24450             
24451             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24452         }
24453         
24454         this.el.select('>button.close').on('click', this.hide, this);
24455         
24456     },
24457     
24458     show : function()
24459     {
24460         if (!this.rendered) {
24461             this.render();
24462         }
24463         
24464         this.el.show();
24465         
24466         this.fireEvent('show', this);
24467         
24468     },
24469     
24470     hide : function()
24471     {
24472         if (!this.rendered) {
24473             this.render();
24474         }
24475         
24476         this.el.hide();
24477         
24478         this.fireEvent('hide', this);
24479     },
24480     
24481     update : function()
24482     {
24483 //        var e = this.el.dom.firstChild;
24484 //        
24485 //        if(this.closable){
24486 //            e = e.nextSibling;
24487 //        }
24488 //        
24489 //        e.data = this.html || '';
24490
24491         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24492     }
24493    
24494 });
24495
24496  
24497
24498      /*
24499  * - LGPL
24500  *
24501  * Graph
24502  * 
24503  */
24504
24505
24506 /**
24507  * @class Roo.bootstrap.Graph
24508  * @extends Roo.bootstrap.Component
24509  * Bootstrap Graph class
24510 > Prameters
24511  -sm {number} sm 4
24512  -md {number} md 5
24513  @cfg {String} graphtype  bar | vbar | pie
24514  @cfg {number} g_x coodinator | centre x (pie)
24515  @cfg {number} g_y coodinator | centre y (pie)
24516  @cfg {number} g_r radius (pie)
24517  @cfg {number} g_height height of the chart (respected by all elements in the set)
24518  @cfg {number} g_width width of the chart (respected by all elements in the set)
24519  @cfg {Object} title The title of the chart
24520     
24521  -{Array}  values
24522  -opts (object) options for the chart 
24523      o {
24524      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24525      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24526      o vgutter (number)
24527      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.
24528      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24529      o to
24530      o stretch (boolean)
24531      o }
24532  -opts (object) options for the pie
24533      o{
24534      o cut
24535      o startAngle (number)
24536      o endAngle (number)
24537      } 
24538  *
24539  * @constructor
24540  * Create a new Input
24541  * @param {Object} config The config object
24542  */
24543
24544 Roo.bootstrap.Graph = function(config){
24545     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24546     
24547     this.addEvents({
24548         // img events
24549         /**
24550          * @event click
24551          * The img click event for the img.
24552          * @param {Roo.EventObject} e
24553          */
24554         "click" : true
24555     });
24556 };
24557
24558 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24559     
24560     sm: 4,
24561     md: 5,
24562     graphtype: 'bar',
24563     g_height: 250,
24564     g_width: 400,
24565     g_x: 50,
24566     g_y: 50,
24567     g_r: 30,
24568     opts:{
24569         //g_colors: this.colors,
24570         g_type: 'soft',
24571         g_gutter: '20%'
24572
24573     },
24574     title : false,
24575
24576     getAutoCreate : function(){
24577         
24578         var cfg = {
24579             tag: 'div',
24580             html : null
24581         };
24582         
24583         
24584         return  cfg;
24585     },
24586
24587     onRender : function(ct,position){
24588         
24589         
24590         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24591         
24592         if (typeof(Raphael) == 'undefined') {
24593             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24594             return;
24595         }
24596         
24597         this.raphael = Raphael(this.el.dom);
24598         
24599                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24600                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24601                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24602                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24603                 /*
24604                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24605                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24606                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24607                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24608                 
24609                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24610                 r.barchart(330, 10, 300, 220, data1);
24611                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24612                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24613                 */
24614                 
24615                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24616                 // r.barchart(30, 30, 560, 250,  xdata, {
24617                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24618                 //     axis : "0 0 1 1",
24619                 //     axisxlabels :  xdata
24620                 //     //yvalues : cols,
24621                    
24622                 // });
24623 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24624 //        
24625 //        this.load(null,xdata,{
24626 //                axis : "0 0 1 1",
24627 //                axisxlabels :  xdata
24628 //                });
24629
24630     },
24631
24632     load : function(graphtype,xdata,opts)
24633     {
24634         this.raphael.clear();
24635         if(!graphtype) {
24636             graphtype = this.graphtype;
24637         }
24638         if(!opts){
24639             opts = this.opts;
24640         }
24641         var r = this.raphael,
24642             fin = function () {
24643                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24644             },
24645             fout = function () {
24646                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24647             },
24648             pfin = function() {
24649                 this.sector.stop();
24650                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24651
24652                 if (this.label) {
24653                     this.label[0].stop();
24654                     this.label[0].attr({ r: 7.5 });
24655                     this.label[1].attr({ "font-weight": 800 });
24656                 }
24657             },
24658             pfout = function() {
24659                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24660
24661                 if (this.label) {
24662                     this.label[0].animate({ r: 5 }, 500, "bounce");
24663                     this.label[1].attr({ "font-weight": 400 });
24664                 }
24665             };
24666
24667         switch(graphtype){
24668             case 'bar':
24669                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24670                 break;
24671             case 'hbar':
24672                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24673                 break;
24674             case 'pie':
24675 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24676 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24677 //            
24678                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24679                 
24680                 break;
24681
24682         }
24683         
24684         if(this.title){
24685             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24686         }
24687         
24688     },
24689     
24690     setTitle: function(o)
24691     {
24692         this.title = o;
24693     },
24694     
24695     initEvents: function() {
24696         
24697         if(!this.href){
24698             this.el.on('click', this.onClick, this);
24699         }
24700     },
24701     
24702     onClick : function(e)
24703     {
24704         Roo.log('img onclick');
24705         this.fireEvent('click', this, e);
24706     }
24707    
24708 });
24709
24710  
24711 /*
24712  * - LGPL
24713  *
24714  * numberBox
24715  * 
24716  */
24717 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24718
24719 /**
24720  * @class Roo.bootstrap.dash.NumberBox
24721  * @extends Roo.bootstrap.Component
24722  * Bootstrap NumberBox class
24723  * @cfg {String} headline Box headline
24724  * @cfg {String} content Box content
24725  * @cfg {String} icon Box icon
24726  * @cfg {String} footer Footer text
24727  * @cfg {String} fhref Footer href
24728  * 
24729  * @constructor
24730  * Create a new NumberBox
24731  * @param {Object} config The config object
24732  */
24733
24734
24735 Roo.bootstrap.dash.NumberBox = function(config){
24736     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24737     
24738 };
24739
24740 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24741     
24742     headline : '',
24743     content : '',
24744     icon : '',
24745     footer : '',
24746     fhref : '',
24747     ficon : '',
24748     
24749     getAutoCreate : function(){
24750         
24751         var cfg = {
24752             tag : 'div',
24753             cls : 'small-box ',
24754             cn : [
24755                 {
24756                     tag : 'div',
24757                     cls : 'inner',
24758                     cn :[
24759                         {
24760                             tag : 'h3',
24761                             cls : 'roo-headline',
24762                             html : this.headline
24763                         },
24764                         {
24765                             tag : 'p',
24766                             cls : 'roo-content',
24767                             html : this.content
24768                         }
24769                     ]
24770                 }
24771             ]
24772         };
24773         
24774         if(this.icon){
24775             cfg.cn.push({
24776                 tag : 'div',
24777                 cls : 'icon',
24778                 cn :[
24779                     {
24780                         tag : 'i',
24781                         cls : 'ion ' + this.icon
24782                     }
24783                 ]
24784             });
24785         }
24786         
24787         if(this.footer){
24788             var footer = {
24789                 tag : 'a',
24790                 cls : 'small-box-footer',
24791                 href : this.fhref || '#',
24792                 html : this.footer
24793             };
24794             
24795             cfg.cn.push(footer);
24796             
24797         }
24798         
24799         return  cfg;
24800     },
24801
24802     onRender : function(ct,position){
24803         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24804
24805
24806        
24807                 
24808     },
24809
24810     setHeadline: function (value)
24811     {
24812         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24813     },
24814     
24815     setFooter: function (value, href)
24816     {
24817         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24818         
24819         if(href){
24820             this.el.select('a.small-box-footer',true).first().attr('href', href);
24821         }
24822         
24823     },
24824
24825     setContent: function (value)
24826     {
24827         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24828     },
24829
24830     initEvents: function() 
24831     {   
24832         
24833     }
24834     
24835 });
24836
24837  
24838 /*
24839  * - LGPL
24840  *
24841  * TabBox
24842  * 
24843  */
24844 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24845
24846 /**
24847  * @class Roo.bootstrap.dash.TabBox
24848  * @extends Roo.bootstrap.Component
24849  * Bootstrap TabBox class
24850  * @cfg {String} title Title of the TabBox
24851  * @cfg {String} icon Icon of the TabBox
24852  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24853  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24854  * 
24855  * @constructor
24856  * Create a new TabBox
24857  * @param {Object} config The config object
24858  */
24859
24860
24861 Roo.bootstrap.dash.TabBox = function(config){
24862     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24863     this.addEvents({
24864         // raw events
24865         /**
24866          * @event addpane
24867          * When a pane is added
24868          * @param {Roo.bootstrap.dash.TabPane} pane
24869          */
24870         "addpane" : true,
24871         /**
24872          * @event activatepane
24873          * When a pane is activated
24874          * @param {Roo.bootstrap.dash.TabPane} pane
24875          */
24876         "activatepane" : true
24877         
24878          
24879     });
24880     
24881     this.panes = [];
24882 };
24883
24884 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24885
24886     title : '',
24887     icon : false,
24888     showtabs : true,
24889     tabScrollable : false,
24890     
24891     getChildContainer : function()
24892     {
24893         return this.el.select('.tab-content', true).first();
24894     },
24895     
24896     getAutoCreate : function(){
24897         
24898         var header = {
24899             tag: 'li',
24900             cls: 'pull-left header',
24901             html: this.title,
24902             cn : []
24903         };
24904         
24905         if(this.icon){
24906             header.cn.push({
24907                 tag: 'i',
24908                 cls: 'fa ' + this.icon
24909             });
24910         }
24911         
24912         var h = {
24913             tag: 'ul',
24914             cls: 'nav nav-tabs pull-right',
24915             cn: [
24916                 header
24917             ]
24918         };
24919         
24920         if(this.tabScrollable){
24921             h = {
24922                 tag: 'div',
24923                 cls: 'tab-header',
24924                 cn: [
24925                     {
24926                         tag: 'ul',
24927                         cls: 'nav nav-tabs pull-right',
24928                         cn: [
24929                             header
24930                         ]
24931                     }
24932                 ]
24933             };
24934         }
24935         
24936         var cfg = {
24937             tag: 'div',
24938             cls: 'nav-tabs-custom',
24939             cn: [
24940                 h,
24941                 {
24942                     tag: 'div',
24943                     cls: 'tab-content no-padding',
24944                     cn: []
24945                 }
24946             ]
24947         };
24948
24949         return  cfg;
24950     },
24951     initEvents : function()
24952     {
24953         //Roo.log('add add pane handler');
24954         this.on('addpane', this.onAddPane, this);
24955     },
24956      /**
24957      * Updates the box title
24958      * @param {String} html to set the title to.
24959      */
24960     setTitle : function(value)
24961     {
24962         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24963     },
24964     onAddPane : function(pane)
24965     {
24966         this.panes.push(pane);
24967         //Roo.log('addpane');
24968         //Roo.log(pane);
24969         // tabs are rendere left to right..
24970         if(!this.showtabs){
24971             return;
24972         }
24973         
24974         var ctr = this.el.select('.nav-tabs', true).first();
24975          
24976          
24977         var existing = ctr.select('.nav-tab',true);
24978         var qty = existing.getCount();;
24979         
24980         
24981         var tab = ctr.createChild({
24982             tag : 'li',
24983             cls : 'nav-tab' + (qty ? '' : ' active'),
24984             cn : [
24985                 {
24986                     tag : 'a',
24987                     href:'#',
24988                     html : pane.title
24989                 }
24990             ]
24991         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24992         pane.tab = tab;
24993         
24994         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24995         if (!qty) {
24996             pane.el.addClass('active');
24997         }
24998         
24999                 
25000     },
25001     onTabClick : function(ev,un,ob,pane)
25002     {
25003         //Roo.log('tab - prev default');
25004         ev.preventDefault();
25005         
25006         
25007         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25008         pane.tab.addClass('active');
25009         //Roo.log(pane.title);
25010         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25011         // technically we should have a deactivate event.. but maybe add later.
25012         // and it should not de-activate the selected tab...
25013         this.fireEvent('activatepane', pane);
25014         pane.el.addClass('active');
25015         pane.fireEvent('activate');
25016         
25017         
25018     },
25019     
25020     getActivePane : function()
25021     {
25022         var r = false;
25023         Roo.each(this.panes, function(p) {
25024             if(p.el.hasClass('active')){
25025                 r = p;
25026                 return false;
25027             }
25028             
25029             return;
25030         });
25031         
25032         return r;
25033     }
25034     
25035     
25036 });
25037
25038  
25039 /*
25040  * - LGPL
25041  *
25042  * Tab pane
25043  * 
25044  */
25045 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25046 /**
25047  * @class Roo.bootstrap.TabPane
25048  * @extends Roo.bootstrap.Component
25049  * Bootstrap TabPane class
25050  * @cfg {Boolean} active (false | true) Default false
25051  * @cfg {String} title title of panel
25052
25053  * 
25054  * @constructor
25055  * Create a new TabPane
25056  * @param {Object} config The config object
25057  */
25058
25059 Roo.bootstrap.dash.TabPane = function(config){
25060     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25061     
25062     this.addEvents({
25063         // raw events
25064         /**
25065          * @event activate
25066          * When a pane is activated
25067          * @param {Roo.bootstrap.dash.TabPane} pane
25068          */
25069         "activate" : true
25070          
25071     });
25072 };
25073
25074 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25075     
25076     active : false,
25077     title : '',
25078     
25079     // the tabBox that this is attached to.
25080     tab : false,
25081      
25082     getAutoCreate : function() 
25083     {
25084         var cfg = {
25085             tag: 'div',
25086             cls: 'tab-pane'
25087         };
25088         
25089         if(this.active){
25090             cfg.cls += ' active';
25091         }
25092         
25093         return cfg;
25094     },
25095     initEvents  : function()
25096     {
25097         //Roo.log('trigger add pane handler');
25098         this.parent().fireEvent('addpane', this)
25099     },
25100     
25101      /**
25102      * Updates the tab title 
25103      * @param {String} html to set the title to.
25104      */
25105     setTitle: function(str)
25106     {
25107         if (!this.tab) {
25108             return;
25109         }
25110         this.title = str;
25111         this.tab.select('a', true).first().dom.innerHTML = str;
25112         
25113     }
25114     
25115     
25116     
25117 });
25118
25119  
25120
25121
25122  /*
25123  * - LGPL
25124  *
25125  * menu
25126  * 
25127  */
25128 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25129
25130 /**
25131  * @class Roo.bootstrap.menu.Menu
25132  * @extends Roo.bootstrap.Component
25133  * Bootstrap Menu class - container for Menu
25134  * @cfg {String} html Text of the menu
25135  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25136  * @cfg {String} icon Font awesome icon
25137  * @cfg {String} pos Menu align to (top | bottom) default bottom
25138  * 
25139  * 
25140  * @constructor
25141  * Create a new Menu
25142  * @param {Object} config The config object
25143  */
25144
25145
25146 Roo.bootstrap.menu.Menu = function(config){
25147     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25148     
25149     this.addEvents({
25150         /**
25151          * @event beforeshow
25152          * Fires before this menu is displayed
25153          * @param {Roo.bootstrap.menu.Menu} this
25154          */
25155         beforeshow : true,
25156         /**
25157          * @event beforehide
25158          * Fires before this menu is hidden
25159          * @param {Roo.bootstrap.menu.Menu} this
25160          */
25161         beforehide : true,
25162         /**
25163          * @event show
25164          * Fires after this menu is displayed
25165          * @param {Roo.bootstrap.menu.Menu} this
25166          */
25167         show : true,
25168         /**
25169          * @event hide
25170          * Fires after this menu is hidden
25171          * @param {Roo.bootstrap.menu.Menu} this
25172          */
25173         hide : true,
25174         /**
25175          * @event click
25176          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25177          * @param {Roo.bootstrap.menu.Menu} this
25178          * @param {Roo.EventObject} e
25179          */
25180         click : true
25181     });
25182     
25183 };
25184
25185 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25186     
25187     submenu : false,
25188     html : '',
25189     weight : 'default',
25190     icon : false,
25191     pos : 'bottom',
25192     
25193     
25194     getChildContainer : function() {
25195         if(this.isSubMenu){
25196             return this.el;
25197         }
25198         
25199         return this.el.select('ul.dropdown-menu', true).first();  
25200     },
25201     
25202     getAutoCreate : function()
25203     {
25204         var text = [
25205             {
25206                 tag : 'span',
25207                 cls : 'roo-menu-text',
25208                 html : this.html
25209             }
25210         ];
25211         
25212         if(this.icon){
25213             text.unshift({
25214                 tag : 'i',
25215                 cls : 'fa ' + this.icon
25216             })
25217         }
25218         
25219         
25220         var cfg = {
25221             tag : 'div',
25222             cls : 'btn-group',
25223             cn : [
25224                 {
25225                     tag : 'button',
25226                     cls : 'dropdown-button btn btn-' + this.weight,
25227                     cn : text
25228                 },
25229                 {
25230                     tag : 'button',
25231                     cls : 'dropdown-toggle btn btn-' + this.weight,
25232                     cn : [
25233                         {
25234                             tag : 'span',
25235                             cls : 'caret'
25236                         }
25237                     ]
25238                 },
25239                 {
25240                     tag : 'ul',
25241                     cls : 'dropdown-menu'
25242                 }
25243             ]
25244             
25245         };
25246         
25247         if(this.pos == 'top'){
25248             cfg.cls += ' dropup';
25249         }
25250         
25251         if(this.isSubMenu){
25252             cfg = {
25253                 tag : 'ul',
25254                 cls : 'dropdown-menu'
25255             }
25256         }
25257         
25258         return cfg;
25259     },
25260     
25261     onRender : function(ct, position)
25262     {
25263         this.isSubMenu = ct.hasClass('dropdown-submenu');
25264         
25265         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25266     },
25267     
25268     initEvents : function() 
25269     {
25270         if(this.isSubMenu){
25271             return;
25272         }
25273         
25274         this.hidden = true;
25275         
25276         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25277         this.triggerEl.on('click', this.onTriggerPress, this);
25278         
25279         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25280         this.buttonEl.on('click', this.onClick, this);
25281         
25282     },
25283     
25284     list : function()
25285     {
25286         if(this.isSubMenu){
25287             return this.el;
25288         }
25289         
25290         return this.el.select('ul.dropdown-menu', true).first();
25291     },
25292     
25293     onClick : function(e)
25294     {
25295         this.fireEvent("click", this, e);
25296     },
25297     
25298     onTriggerPress  : function(e)
25299     {   
25300         if (this.isVisible()) {
25301             this.hide();
25302         } else {
25303             this.show();
25304         }
25305     },
25306     
25307     isVisible : function(){
25308         return !this.hidden;
25309     },
25310     
25311     show : function()
25312     {
25313         this.fireEvent("beforeshow", this);
25314         
25315         this.hidden = false;
25316         this.el.addClass('open');
25317         
25318         Roo.get(document).on("mouseup", this.onMouseUp, this);
25319         
25320         this.fireEvent("show", this);
25321         
25322         
25323     },
25324     
25325     hide : function()
25326     {
25327         this.fireEvent("beforehide", this);
25328         
25329         this.hidden = true;
25330         this.el.removeClass('open');
25331         
25332         Roo.get(document).un("mouseup", this.onMouseUp);
25333         
25334         this.fireEvent("hide", this);
25335     },
25336     
25337     onMouseUp : function()
25338     {
25339         this.hide();
25340     }
25341     
25342 });
25343
25344  
25345  /*
25346  * - LGPL
25347  *
25348  * menu item
25349  * 
25350  */
25351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25352
25353 /**
25354  * @class Roo.bootstrap.menu.Item
25355  * @extends Roo.bootstrap.Component
25356  * Bootstrap MenuItem class
25357  * @cfg {Boolean} submenu (true | false) default false
25358  * @cfg {String} html text of the item
25359  * @cfg {String} href the link
25360  * @cfg {Boolean} disable (true | false) default false
25361  * @cfg {Boolean} preventDefault (true | false) default true
25362  * @cfg {String} icon Font awesome icon
25363  * @cfg {String} pos Submenu align to (left | right) default right 
25364  * 
25365  * 
25366  * @constructor
25367  * Create a new Item
25368  * @param {Object} config The config object
25369  */
25370
25371
25372 Roo.bootstrap.menu.Item = function(config){
25373     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25374     this.addEvents({
25375         /**
25376          * @event mouseover
25377          * Fires when the mouse is hovering over this menu
25378          * @param {Roo.bootstrap.menu.Item} this
25379          * @param {Roo.EventObject} e
25380          */
25381         mouseover : true,
25382         /**
25383          * @event mouseout
25384          * Fires when the mouse exits this menu
25385          * @param {Roo.bootstrap.menu.Item} this
25386          * @param {Roo.EventObject} e
25387          */
25388         mouseout : true,
25389         // raw events
25390         /**
25391          * @event click
25392          * The raw click event for the entire grid.
25393          * @param {Roo.EventObject} e
25394          */
25395         click : true
25396     });
25397 };
25398
25399 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25400     
25401     submenu : false,
25402     href : '',
25403     html : '',
25404     preventDefault: true,
25405     disable : false,
25406     icon : false,
25407     pos : 'right',
25408     
25409     getAutoCreate : function()
25410     {
25411         var text = [
25412             {
25413                 tag : 'span',
25414                 cls : 'roo-menu-item-text',
25415                 html : this.html
25416             }
25417         ];
25418         
25419         if(this.icon){
25420             text.unshift({
25421                 tag : 'i',
25422                 cls : 'fa ' + this.icon
25423             })
25424         }
25425         
25426         var cfg = {
25427             tag : 'li',
25428             cn : [
25429                 {
25430                     tag : 'a',
25431                     href : this.href || '#',
25432                     cn : text
25433                 }
25434             ]
25435         };
25436         
25437         if(this.disable){
25438             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25439         }
25440         
25441         if(this.submenu){
25442             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25443             
25444             if(this.pos == 'left'){
25445                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25446             }
25447         }
25448         
25449         return cfg;
25450     },
25451     
25452     initEvents : function() 
25453     {
25454         this.el.on('mouseover', this.onMouseOver, this);
25455         this.el.on('mouseout', this.onMouseOut, this);
25456         
25457         this.el.select('a', true).first().on('click', this.onClick, this);
25458         
25459     },
25460     
25461     onClick : function(e)
25462     {
25463         if(this.preventDefault){
25464             e.preventDefault();
25465         }
25466         
25467         this.fireEvent("click", this, e);
25468     },
25469     
25470     onMouseOver : function(e)
25471     {
25472         if(this.submenu && this.pos == 'left'){
25473             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25474         }
25475         
25476         this.fireEvent("mouseover", this, e);
25477     },
25478     
25479     onMouseOut : function(e)
25480     {
25481         this.fireEvent("mouseout", this, e);
25482     }
25483 });
25484
25485  
25486
25487  /*
25488  * - LGPL
25489  *
25490  * menu separator
25491  * 
25492  */
25493 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25494
25495 /**
25496  * @class Roo.bootstrap.menu.Separator
25497  * @extends Roo.bootstrap.Component
25498  * Bootstrap Separator class
25499  * 
25500  * @constructor
25501  * Create a new Separator
25502  * @param {Object} config The config object
25503  */
25504
25505
25506 Roo.bootstrap.menu.Separator = function(config){
25507     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25508 };
25509
25510 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25511     
25512     getAutoCreate : function(){
25513         var cfg = {
25514             tag : 'li',
25515             cls: 'divider'
25516         };
25517         
25518         return cfg;
25519     }
25520    
25521 });
25522
25523  
25524
25525  /*
25526  * - LGPL
25527  *
25528  * Tooltip
25529  * 
25530  */
25531
25532 /**
25533  * @class Roo.bootstrap.Tooltip
25534  * Bootstrap Tooltip class
25535  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25536  * to determine which dom element triggers the tooltip.
25537  * 
25538  * It needs to add support for additional attributes like tooltip-position
25539  * 
25540  * @constructor
25541  * Create a new Toolti
25542  * @param {Object} config The config object
25543  */
25544
25545 Roo.bootstrap.Tooltip = function(config){
25546     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25547     
25548     this.alignment = Roo.bootstrap.Tooltip.alignment;
25549     
25550     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25551         this.alignment = config.alignment;
25552     }
25553     
25554 };
25555
25556 Roo.apply(Roo.bootstrap.Tooltip, {
25557     /**
25558      * @function init initialize tooltip monitoring.
25559      * @static
25560      */
25561     currentEl : false,
25562     currentTip : false,
25563     currentRegion : false,
25564     
25565     //  init : delay?
25566     
25567     init : function()
25568     {
25569         Roo.get(document).on('mouseover', this.enter ,this);
25570         Roo.get(document).on('mouseout', this.leave, this);
25571          
25572         
25573         this.currentTip = new Roo.bootstrap.Tooltip();
25574     },
25575     
25576     enter : function(ev)
25577     {
25578         var dom = ev.getTarget();
25579         
25580         //Roo.log(['enter',dom]);
25581         var el = Roo.fly(dom);
25582         if (this.currentEl) {
25583             //Roo.log(dom);
25584             //Roo.log(this.currentEl);
25585             //Roo.log(this.currentEl.contains(dom));
25586             if (this.currentEl == el) {
25587                 return;
25588             }
25589             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25590                 return;
25591             }
25592
25593         }
25594         
25595         if (this.currentTip.el) {
25596             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25597         }    
25598         //Roo.log(ev);
25599         
25600         if(!el || el.dom == document){
25601             return;
25602         }
25603         
25604         var bindEl = el;
25605         
25606         // you can not look for children, as if el is the body.. then everythign is the child..
25607         if (!el.attr('tooltip')) { //
25608             if (!el.select("[tooltip]").elements.length) {
25609                 return;
25610             }
25611             // is the mouse over this child...?
25612             bindEl = el.select("[tooltip]").first();
25613             var xy = ev.getXY();
25614             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25615                 //Roo.log("not in region.");
25616                 return;
25617             }
25618             //Roo.log("child element over..");
25619             
25620         }
25621         this.currentEl = bindEl;
25622         this.currentTip.bind(bindEl);
25623         this.currentRegion = Roo.lib.Region.getRegion(dom);
25624         this.currentTip.enter();
25625         
25626     },
25627     leave : function(ev)
25628     {
25629         var dom = ev.getTarget();
25630         //Roo.log(['leave',dom]);
25631         if (!this.currentEl) {
25632             return;
25633         }
25634         
25635         
25636         if (dom != this.currentEl.dom) {
25637             return;
25638         }
25639         var xy = ev.getXY();
25640         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25641             return;
25642         }
25643         // only activate leave if mouse cursor is outside... bounding box..
25644         
25645         
25646         
25647         
25648         if (this.currentTip) {
25649             this.currentTip.leave();
25650         }
25651         //Roo.log('clear currentEl');
25652         this.currentEl = false;
25653         
25654         
25655     },
25656     alignment : {
25657         'left' : ['r-l', [-2,0], 'right'],
25658         'right' : ['l-r', [2,0], 'left'],
25659         'bottom' : ['t-b', [0,2], 'top'],
25660         'top' : [ 'b-t', [0,-2], 'bottom']
25661     }
25662     
25663 });
25664
25665
25666 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25667     
25668     
25669     bindEl : false,
25670     
25671     delay : null, // can be { show : 300 , hide: 500}
25672     
25673     timeout : null,
25674     
25675     hoverState : null, //???
25676     
25677     placement : 'bottom', 
25678     
25679     alignment : false,
25680     
25681     getAutoCreate : function(){
25682     
25683         var cfg = {
25684            cls : 'tooltip',
25685            role : 'tooltip',
25686            cn : [
25687                 {
25688                     cls : 'tooltip-arrow'
25689                 },
25690                 {
25691                     cls : 'tooltip-inner'
25692                 }
25693            ]
25694         };
25695         
25696         return cfg;
25697     },
25698     bind : function(el)
25699     {
25700         this.bindEl = el;
25701     },
25702       
25703     
25704     enter : function () {
25705        
25706         if (this.timeout != null) {
25707             clearTimeout(this.timeout);
25708         }
25709         
25710         this.hoverState = 'in';
25711          //Roo.log("enter - show");
25712         if (!this.delay || !this.delay.show) {
25713             this.show();
25714             return;
25715         }
25716         var _t = this;
25717         this.timeout = setTimeout(function () {
25718             if (_t.hoverState == 'in') {
25719                 _t.show();
25720             }
25721         }, this.delay.show);
25722     },
25723     leave : function()
25724     {
25725         clearTimeout(this.timeout);
25726     
25727         this.hoverState = 'out';
25728          if (!this.delay || !this.delay.hide) {
25729             this.hide();
25730             return;
25731         }
25732        
25733         var _t = this;
25734         this.timeout = setTimeout(function () {
25735             //Roo.log("leave - timeout");
25736             
25737             if (_t.hoverState == 'out') {
25738                 _t.hide();
25739                 Roo.bootstrap.Tooltip.currentEl = false;
25740             }
25741         }, delay);
25742     },
25743     
25744     show : function (msg)
25745     {
25746         if (!this.el) {
25747             this.render(document.body);
25748         }
25749         // set content.
25750         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25751         
25752         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25753         
25754         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25755         
25756         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25757         
25758         var placement = typeof this.placement == 'function' ?
25759             this.placement.call(this, this.el, on_el) :
25760             this.placement;
25761             
25762         var autoToken = /\s?auto?\s?/i;
25763         var autoPlace = autoToken.test(placement);
25764         if (autoPlace) {
25765             placement = placement.replace(autoToken, '') || 'top';
25766         }
25767         
25768         //this.el.detach()
25769         //this.el.setXY([0,0]);
25770         this.el.show();
25771         //this.el.dom.style.display='block';
25772         
25773         //this.el.appendTo(on_el);
25774         
25775         var p = this.getPosition();
25776         var box = this.el.getBox();
25777         
25778         if (autoPlace) {
25779             // fixme..
25780         }
25781         
25782         var align = this.alignment[placement];
25783         
25784         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25785         
25786         if(placement == 'top' || placement == 'bottom'){
25787             if(xy[0] < 0){
25788                 placement = 'right';
25789             }
25790             
25791             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25792                 placement = 'left';
25793             }
25794             
25795             var scroll = Roo.select('body', true).first().getScroll();
25796             
25797             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25798                 placement = 'top';
25799             }
25800             
25801         }
25802         
25803         this.el.alignTo(this.bindEl, align[0],align[1]);
25804         //var arrow = this.el.select('.arrow',true).first();
25805         //arrow.set(align[2], 
25806         
25807         this.el.addClass(placement);
25808         
25809         this.el.addClass('in fade');
25810         
25811         this.hoverState = null;
25812         
25813         if (this.el.hasClass('fade')) {
25814             // fade it?
25815         }
25816         
25817     },
25818     hide : function()
25819     {
25820          
25821         if (!this.el) {
25822             return;
25823         }
25824         //this.el.setXY([0,0]);
25825         this.el.removeClass('in');
25826         //this.el.hide();
25827         
25828     }
25829     
25830 });
25831  
25832
25833  /*
25834  * - LGPL
25835  *
25836  * Location Picker
25837  * 
25838  */
25839
25840 /**
25841  * @class Roo.bootstrap.LocationPicker
25842  * @extends Roo.bootstrap.Component
25843  * Bootstrap LocationPicker class
25844  * @cfg {Number} latitude Position when init default 0
25845  * @cfg {Number} longitude Position when init default 0
25846  * @cfg {Number} zoom default 15
25847  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25848  * @cfg {Boolean} mapTypeControl default false
25849  * @cfg {Boolean} disableDoubleClickZoom default false
25850  * @cfg {Boolean} scrollwheel default true
25851  * @cfg {Boolean} streetViewControl default false
25852  * @cfg {Number} radius default 0
25853  * @cfg {String} locationName
25854  * @cfg {Boolean} draggable default true
25855  * @cfg {Boolean} enableAutocomplete default false
25856  * @cfg {Boolean} enableReverseGeocode default true
25857  * @cfg {String} markerTitle
25858  * 
25859  * @constructor
25860  * Create a new LocationPicker
25861  * @param {Object} config The config object
25862  */
25863
25864
25865 Roo.bootstrap.LocationPicker = function(config){
25866     
25867     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25868     
25869     this.addEvents({
25870         /**
25871          * @event initial
25872          * Fires when the picker initialized.
25873          * @param {Roo.bootstrap.LocationPicker} this
25874          * @param {Google Location} location
25875          */
25876         initial : true,
25877         /**
25878          * @event positionchanged
25879          * Fires when the picker position changed.
25880          * @param {Roo.bootstrap.LocationPicker} this
25881          * @param {Google Location} location
25882          */
25883         positionchanged : true,
25884         /**
25885          * @event resize
25886          * Fires when the map resize.
25887          * @param {Roo.bootstrap.LocationPicker} this
25888          */
25889         resize : true,
25890         /**
25891          * @event show
25892          * Fires when the map show.
25893          * @param {Roo.bootstrap.LocationPicker} this
25894          */
25895         show : true,
25896         /**
25897          * @event hide
25898          * Fires when the map hide.
25899          * @param {Roo.bootstrap.LocationPicker} this
25900          */
25901         hide : true,
25902         /**
25903          * @event mapClick
25904          * Fires when click the map.
25905          * @param {Roo.bootstrap.LocationPicker} this
25906          * @param {Map event} e
25907          */
25908         mapClick : true,
25909         /**
25910          * @event mapRightClick
25911          * Fires when right click the map.
25912          * @param {Roo.bootstrap.LocationPicker} this
25913          * @param {Map event} e
25914          */
25915         mapRightClick : true,
25916         /**
25917          * @event markerClick
25918          * Fires when click the marker.
25919          * @param {Roo.bootstrap.LocationPicker} this
25920          * @param {Map event} e
25921          */
25922         markerClick : true,
25923         /**
25924          * @event markerRightClick
25925          * Fires when right click the marker.
25926          * @param {Roo.bootstrap.LocationPicker} this
25927          * @param {Map event} e
25928          */
25929         markerRightClick : true,
25930         /**
25931          * @event OverlayViewDraw
25932          * Fires when OverlayView Draw
25933          * @param {Roo.bootstrap.LocationPicker} this
25934          */
25935         OverlayViewDraw : true,
25936         /**
25937          * @event OverlayViewOnAdd
25938          * Fires when OverlayView Draw
25939          * @param {Roo.bootstrap.LocationPicker} this
25940          */
25941         OverlayViewOnAdd : true,
25942         /**
25943          * @event OverlayViewOnRemove
25944          * Fires when OverlayView Draw
25945          * @param {Roo.bootstrap.LocationPicker} this
25946          */
25947         OverlayViewOnRemove : true,
25948         /**
25949          * @event OverlayViewShow
25950          * Fires when OverlayView Draw
25951          * @param {Roo.bootstrap.LocationPicker} this
25952          * @param {Pixel} cpx
25953          */
25954         OverlayViewShow : true,
25955         /**
25956          * @event OverlayViewHide
25957          * Fires when OverlayView Draw
25958          * @param {Roo.bootstrap.LocationPicker} this
25959          */
25960         OverlayViewHide : true,
25961         /**
25962          * @event loadexception
25963          * Fires when load google lib failed.
25964          * @param {Roo.bootstrap.LocationPicker} this
25965          */
25966         loadexception : true
25967     });
25968         
25969 };
25970
25971 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25972     
25973     gMapContext: false,
25974     
25975     latitude: 0,
25976     longitude: 0,
25977     zoom: 15,
25978     mapTypeId: false,
25979     mapTypeControl: false,
25980     disableDoubleClickZoom: false,
25981     scrollwheel: true,
25982     streetViewControl: false,
25983     radius: 0,
25984     locationName: '',
25985     draggable: true,
25986     enableAutocomplete: false,
25987     enableReverseGeocode: true,
25988     markerTitle: '',
25989     
25990     getAutoCreate: function()
25991     {
25992
25993         var cfg = {
25994             tag: 'div',
25995             cls: 'roo-location-picker'
25996         };
25997         
25998         return cfg
25999     },
26000     
26001     initEvents: function(ct, position)
26002     {       
26003         if(!this.el.getWidth() || this.isApplied()){
26004             return;
26005         }
26006         
26007         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26008         
26009         this.initial();
26010     },
26011     
26012     initial: function()
26013     {
26014         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26015             this.fireEvent('loadexception', this);
26016             return;
26017         }
26018         
26019         if(!this.mapTypeId){
26020             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26021         }
26022         
26023         this.gMapContext = this.GMapContext();
26024         
26025         this.initOverlayView();
26026         
26027         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26028         
26029         var _this = this;
26030                 
26031         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26032             _this.setPosition(_this.gMapContext.marker.position);
26033         });
26034         
26035         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26036             _this.fireEvent('mapClick', this, event);
26037             
26038         });
26039
26040         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26041             _this.fireEvent('mapRightClick', this, event);
26042             
26043         });
26044         
26045         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26046             _this.fireEvent('markerClick', this, event);
26047             
26048         });
26049
26050         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26051             _this.fireEvent('markerRightClick', this, event);
26052             
26053         });
26054         
26055         this.setPosition(this.gMapContext.location);
26056         
26057         this.fireEvent('initial', this, this.gMapContext.location);
26058     },
26059     
26060     initOverlayView: function()
26061     {
26062         var _this = this;
26063         
26064         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26065             
26066             draw: function()
26067             {
26068                 _this.fireEvent('OverlayViewDraw', _this);
26069             },
26070             
26071             onAdd: function()
26072             {
26073                 _this.fireEvent('OverlayViewOnAdd', _this);
26074             },
26075             
26076             onRemove: function()
26077             {
26078                 _this.fireEvent('OverlayViewOnRemove', _this);
26079             },
26080             
26081             show: function(cpx)
26082             {
26083                 _this.fireEvent('OverlayViewShow', _this, cpx);
26084             },
26085             
26086             hide: function()
26087             {
26088                 _this.fireEvent('OverlayViewHide', _this);
26089             }
26090             
26091         });
26092     },
26093     
26094     fromLatLngToContainerPixel: function(event)
26095     {
26096         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26097     },
26098     
26099     isApplied: function() 
26100     {
26101         return this.getGmapContext() == false ? false : true;
26102     },
26103     
26104     getGmapContext: function() 
26105     {
26106         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26107     },
26108     
26109     GMapContext: function() 
26110     {
26111         var position = new google.maps.LatLng(this.latitude, this.longitude);
26112         
26113         var _map = new google.maps.Map(this.el.dom, {
26114             center: position,
26115             zoom: this.zoom,
26116             mapTypeId: this.mapTypeId,
26117             mapTypeControl: this.mapTypeControl,
26118             disableDoubleClickZoom: this.disableDoubleClickZoom,
26119             scrollwheel: this.scrollwheel,
26120             streetViewControl: this.streetViewControl,
26121             locationName: this.locationName,
26122             draggable: this.draggable,
26123             enableAutocomplete: this.enableAutocomplete,
26124             enableReverseGeocode: this.enableReverseGeocode
26125         });
26126         
26127         var _marker = new google.maps.Marker({
26128             position: position,
26129             map: _map,
26130             title: this.markerTitle,
26131             draggable: this.draggable
26132         });
26133         
26134         return {
26135             map: _map,
26136             marker: _marker,
26137             circle: null,
26138             location: position,
26139             radius: this.radius,
26140             locationName: this.locationName,
26141             addressComponents: {
26142                 formatted_address: null,
26143                 addressLine1: null,
26144                 addressLine2: null,
26145                 streetName: null,
26146                 streetNumber: null,
26147                 city: null,
26148                 district: null,
26149                 state: null,
26150                 stateOrProvince: null
26151             },
26152             settings: this,
26153             domContainer: this.el.dom,
26154             geodecoder: new google.maps.Geocoder()
26155         };
26156     },
26157     
26158     drawCircle: function(center, radius, options) 
26159     {
26160         if (this.gMapContext.circle != null) {
26161             this.gMapContext.circle.setMap(null);
26162         }
26163         if (radius > 0) {
26164             radius *= 1;
26165             options = Roo.apply({}, options, {
26166                 strokeColor: "#0000FF",
26167                 strokeOpacity: .35,
26168                 strokeWeight: 2,
26169                 fillColor: "#0000FF",
26170                 fillOpacity: .2
26171             });
26172             
26173             options.map = this.gMapContext.map;
26174             options.radius = radius;
26175             options.center = center;
26176             this.gMapContext.circle = new google.maps.Circle(options);
26177             return this.gMapContext.circle;
26178         }
26179         
26180         return null;
26181     },
26182     
26183     setPosition: function(location) 
26184     {
26185         this.gMapContext.location = location;
26186         this.gMapContext.marker.setPosition(location);
26187         this.gMapContext.map.panTo(location);
26188         this.drawCircle(location, this.gMapContext.radius, {});
26189         
26190         var _this = this;
26191         
26192         if (this.gMapContext.settings.enableReverseGeocode) {
26193             this.gMapContext.geodecoder.geocode({
26194                 latLng: this.gMapContext.location
26195             }, function(results, status) {
26196                 
26197                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26198                     _this.gMapContext.locationName = results[0].formatted_address;
26199                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26200                     
26201                     _this.fireEvent('positionchanged', this, location);
26202                 }
26203             });
26204             
26205             return;
26206         }
26207         
26208         this.fireEvent('positionchanged', this, location);
26209     },
26210     
26211     resize: function()
26212     {
26213         google.maps.event.trigger(this.gMapContext.map, "resize");
26214         
26215         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26216         
26217         this.fireEvent('resize', this);
26218     },
26219     
26220     setPositionByLatLng: function(latitude, longitude)
26221     {
26222         this.setPosition(new google.maps.LatLng(latitude, longitude));
26223     },
26224     
26225     getCurrentPosition: function() 
26226     {
26227         return {
26228             latitude: this.gMapContext.location.lat(),
26229             longitude: this.gMapContext.location.lng()
26230         };
26231     },
26232     
26233     getAddressName: function() 
26234     {
26235         return this.gMapContext.locationName;
26236     },
26237     
26238     getAddressComponents: function() 
26239     {
26240         return this.gMapContext.addressComponents;
26241     },
26242     
26243     address_component_from_google_geocode: function(address_components) 
26244     {
26245         var result = {};
26246         
26247         for (var i = 0; i < address_components.length; i++) {
26248             var component = address_components[i];
26249             if (component.types.indexOf("postal_code") >= 0) {
26250                 result.postalCode = component.short_name;
26251             } else if (component.types.indexOf("street_number") >= 0) {
26252                 result.streetNumber = component.short_name;
26253             } else if (component.types.indexOf("route") >= 0) {
26254                 result.streetName = component.short_name;
26255             } else if (component.types.indexOf("neighborhood") >= 0) {
26256                 result.city = component.short_name;
26257             } else if (component.types.indexOf("locality") >= 0) {
26258                 result.city = component.short_name;
26259             } else if (component.types.indexOf("sublocality") >= 0) {
26260                 result.district = component.short_name;
26261             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26262                 result.stateOrProvince = component.short_name;
26263             } else if (component.types.indexOf("country") >= 0) {
26264                 result.country = component.short_name;
26265             }
26266         }
26267         
26268         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26269         result.addressLine2 = "";
26270         return result;
26271     },
26272     
26273     setZoomLevel: function(zoom)
26274     {
26275         this.gMapContext.map.setZoom(zoom);
26276     },
26277     
26278     show: function()
26279     {
26280         if(!this.el){
26281             return;
26282         }
26283         
26284         this.el.show();
26285         
26286         this.resize();
26287         
26288         this.fireEvent('show', this);
26289     },
26290     
26291     hide: function()
26292     {
26293         if(!this.el){
26294             return;
26295         }
26296         
26297         this.el.hide();
26298         
26299         this.fireEvent('hide', this);
26300     }
26301     
26302 });
26303
26304 Roo.apply(Roo.bootstrap.LocationPicker, {
26305     
26306     OverlayView : function(map, options)
26307     {
26308         options = options || {};
26309         
26310         this.setMap(map);
26311     }
26312     
26313     
26314 });/*
26315  * - LGPL
26316  *
26317  * Alert
26318  * 
26319  */
26320
26321 /**
26322  * @class Roo.bootstrap.Alert
26323  * @extends Roo.bootstrap.Component
26324  * Bootstrap Alert class
26325  * @cfg {String} title The title of alert
26326  * @cfg {String} html The content of alert
26327  * @cfg {String} weight (  success | info | warning | danger )
26328  * @cfg {String} faicon font-awesomeicon
26329  * 
26330  * @constructor
26331  * Create a new alert
26332  * @param {Object} config The config object
26333  */
26334
26335
26336 Roo.bootstrap.Alert = function(config){
26337     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26338     
26339 };
26340
26341 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26342     
26343     title: '',
26344     html: '',
26345     weight: false,
26346     faicon: false,
26347     
26348     getAutoCreate : function()
26349     {
26350         
26351         var cfg = {
26352             tag : 'div',
26353             cls : 'alert',
26354             cn : [
26355                 {
26356                     tag : 'i',
26357                     cls : 'roo-alert-icon'
26358                     
26359                 },
26360                 {
26361                     tag : 'b',
26362                     cls : 'roo-alert-title',
26363                     html : this.title
26364                 },
26365                 {
26366                     tag : 'span',
26367                     cls : 'roo-alert-text',
26368                     html : this.html
26369                 }
26370             ]
26371         };
26372         
26373         if(this.faicon){
26374             cfg.cn[0].cls += ' fa ' + this.faicon;
26375         }
26376         
26377         if(this.weight){
26378             cfg.cls += ' alert-' + this.weight;
26379         }
26380         
26381         return cfg;
26382     },
26383     
26384     initEvents: function() 
26385     {
26386         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26387     },
26388     
26389     setTitle : function(str)
26390     {
26391         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26392     },
26393     
26394     setText : function(str)
26395     {
26396         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26397     },
26398     
26399     setWeight : function(weight)
26400     {
26401         if(this.weight){
26402             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26403         }
26404         
26405         this.weight = weight;
26406         
26407         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26408     },
26409     
26410     setIcon : function(icon)
26411     {
26412         if(this.faicon){
26413             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26414         }
26415         
26416         this.faicon = icon;
26417         
26418         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26419     },
26420     
26421     hide: function() 
26422     {
26423         this.el.hide();   
26424     },
26425     
26426     show: function() 
26427     {  
26428         this.el.show();   
26429     }
26430     
26431 });
26432
26433  
26434 /*
26435 * Licence: LGPL
26436 */
26437
26438 /**
26439  * @class Roo.bootstrap.UploadCropbox
26440  * @extends Roo.bootstrap.Component
26441  * Bootstrap UploadCropbox class
26442  * @cfg {String} emptyText show when image has been loaded
26443  * @cfg {String} rotateNotify show when image too small to rotate
26444  * @cfg {Number} errorTimeout default 3000
26445  * @cfg {Number} minWidth default 300
26446  * @cfg {Number} minHeight default 300
26447  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26448  * @cfg {Boolean} isDocument (true|false) default false
26449  * @cfg {String} url action url
26450  * @cfg {String} paramName default 'imageUpload'
26451  * @cfg {String} method default POST
26452  * @cfg {Boolean} loadMask (true|false) default true
26453  * @cfg {Boolean} loadingText default 'Loading...'
26454  * 
26455  * @constructor
26456  * Create a new UploadCropbox
26457  * @param {Object} config The config object
26458  */
26459
26460 Roo.bootstrap.UploadCropbox = function(config){
26461     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26462     
26463     this.addEvents({
26464         /**
26465          * @event beforeselectfile
26466          * Fire before select file
26467          * @param {Roo.bootstrap.UploadCropbox} this
26468          */
26469         "beforeselectfile" : true,
26470         /**
26471          * @event initial
26472          * Fire after initEvent
26473          * @param {Roo.bootstrap.UploadCropbox} this
26474          */
26475         "initial" : true,
26476         /**
26477          * @event crop
26478          * Fire after initEvent
26479          * @param {Roo.bootstrap.UploadCropbox} this
26480          * @param {String} data
26481          */
26482         "crop" : true,
26483         /**
26484          * @event prepare
26485          * Fire when preparing the file data
26486          * @param {Roo.bootstrap.UploadCropbox} this
26487          * @param {Object} file
26488          */
26489         "prepare" : true,
26490         /**
26491          * @event exception
26492          * Fire when get exception
26493          * @param {Roo.bootstrap.UploadCropbox} this
26494          * @param {XMLHttpRequest} xhr
26495          */
26496         "exception" : true,
26497         /**
26498          * @event beforeloadcanvas
26499          * Fire before load the canvas
26500          * @param {Roo.bootstrap.UploadCropbox} this
26501          * @param {String} src
26502          */
26503         "beforeloadcanvas" : true,
26504         /**
26505          * @event trash
26506          * Fire when trash image
26507          * @param {Roo.bootstrap.UploadCropbox} this
26508          */
26509         "trash" : true,
26510         /**
26511          * @event download
26512          * Fire when download the image
26513          * @param {Roo.bootstrap.UploadCropbox} this
26514          */
26515         "download" : true,
26516         /**
26517          * @event footerbuttonclick
26518          * Fire when footerbuttonclick
26519          * @param {Roo.bootstrap.UploadCropbox} this
26520          * @param {String} type
26521          */
26522         "footerbuttonclick" : true,
26523         /**
26524          * @event resize
26525          * Fire when resize
26526          * @param {Roo.bootstrap.UploadCropbox} this
26527          */
26528         "resize" : true,
26529         /**
26530          * @event rotate
26531          * Fire when rotate the image
26532          * @param {Roo.bootstrap.UploadCropbox} this
26533          * @param {String} pos
26534          */
26535         "rotate" : true,
26536         /**
26537          * @event inspect
26538          * Fire when inspect the file
26539          * @param {Roo.bootstrap.UploadCropbox} this
26540          * @param {Object} file
26541          */
26542         "inspect" : true,
26543         /**
26544          * @event upload
26545          * Fire when xhr upload the file
26546          * @param {Roo.bootstrap.UploadCropbox} this
26547          * @param {Object} data
26548          */
26549         "upload" : true,
26550         /**
26551          * @event arrange
26552          * Fire when arrange the file data
26553          * @param {Roo.bootstrap.UploadCropbox} this
26554          * @param {Object} formData
26555          */
26556         "arrange" : true
26557     });
26558     
26559     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26560 };
26561
26562 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26563     
26564     emptyText : 'Click to upload image',
26565     rotateNotify : 'Image is too small to rotate',
26566     errorTimeout : 3000,
26567     scale : 0,
26568     baseScale : 1,
26569     rotate : 0,
26570     dragable : false,
26571     pinching : false,
26572     mouseX : 0,
26573     mouseY : 0,
26574     cropData : false,
26575     minWidth : 300,
26576     minHeight : 300,
26577     file : false,
26578     exif : {},
26579     baseRotate : 1,
26580     cropType : 'image/jpeg',
26581     buttons : false,
26582     canvasLoaded : false,
26583     isDocument : false,
26584     method : 'POST',
26585     paramName : 'imageUpload',
26586     loadMask : true,
26587     loadingText : 'Loading...',
26588     maskEl : false,
26589     
26590     getAutoCreate : function()
26591     {
26592         var cfg = {
26593             tag : 'div',
26594             cls : 'roo-upload-cropbox',
26595             cn : [
26596                 {
26597                     tag : 'input',
26598                     cls : 'roo-upload-cropbox-selector',
26599                     type : 'file'
26600                 },
26601                 {
26602                     tag : 'div',
26603                     cls : 'roo-upload-cropbox-body',
26604                     style : 'cursor:pointer',
26605                     cn : [
26606                         {
26607                             tag : 'div',
26608                             cls : 'roo-upload-cropbox-preview'
26609                         },
26610                         {
26611                             tag : 'div',
26612                             cls : 'roo-upload-cropbox-thumb'
26613                         },
26614                         {
26615                             tag : 'div',
26616                             cls : 'roo-upload-cropbox-empty-notify',
26617                             html : this.emptyText
26618                         },
26619                         {
26620                             tag : 'div',
26621                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26622                             html : this.rotateNotify
26623                         }
26624                     ]
26625                 },
26626                 {
26627                     tag : 'div',
26628                     cls : 'roo-upload-cropbox-footer',
26629                     cn : {
26630                         tag : 'div',
26631                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26632                         cn : []
26633                     }
26634                 }
26635             ]
26636         };
26637         
26638         return cfg;
26639     },
26640     
26641     onRender : function(ct, position)
26642     {
26643         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26644         
26645         if (this.buttons.length) {
26646             
26647             Roo.each(this.buttons, function(bb) {
26648                 
26649                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26650                 
26651                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26652                 
26653             }, this);
26654         }
26655         
26656         if(this.loadMask){
26657             this.maskEl = this.el;
26658         }
26659     },
26660     
26661     initEvents : function()
26662     {
26663         this.urlAPI = (window.createObjectURL && window) || 
26664                                 (window.URL && URL.revokeObjectURL && URL) || 
26665                                 (window.webkitURL && webkitURL);
26666                         
26667         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26668         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26669         
26670         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26671         this.selectorEl.hide();
26672         
26673         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26674         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26675         
26676         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26677         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26678         this.thumbEl.hide();
26679         
26680         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26681         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682         
26683         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26684         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26685         this.errorEl.hide();
26686         
26687         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26688         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26689         this.footerEl.hide();
26690         
26691         this.setThumbBoxSize();
26692         
26693         this.bind();
26694         
26695         this.resize();
26696         
26697         this.fireEvent('initial', this);
26698     },
26699
26700     bind : function()
26701     {
26702         var _this = this;
26703         
26704         window.addEventListener("resize", function() { _this.resize(); } );
26705         
26706         this.bodyEl.on('click', this.beforeSelectFile, this);
26707         
26708         if(Roo.isTouch){
26709             this.bodyEl.on('touchstart', this.onTouchStart, this);
26710             this.bodyEl.on('touchmove', this.onTouchMove, this);
26711             this.bodyEl.on('touchend', this.onTouchEnd, this);
26712         }
26713         
26714         if(!Roo.isTouch){
26715             this.bodyEl.on('mousedown', this.onMouseDown, this);
26716             this.bodyEl.on('mousemove', this.onMouseMove, this);
26717             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26718             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26719             Roo.get(document).on('mouseup', this.onMouseUp, this);
26720         }
26721         
26722         this.selectorEl.on('change', this.onFileSelected, this);
26723     },
26724     
26725     reset : function()
26726     {    
26727         this.scale = 0;
26728         this.baseScale = 1;
26729         this.rotate = 0;
26730         this.baseRotate = 1;
26731         this.dragable = false;
26732         this.pinching = false;
26733         this.mouseX = 0;
26734         this.mouseY = 0;
26735         this.cropData = false;
26736         this.notifyEl.dom.innerHTML = this.emptyText;
26737         
26738         this.selectorEl.dom.value = '';
26739         
26740     },
26741     
26742     resize : function()
26743     {
26744         if(this.fireEvent('resize', this) != false){
26745             this.setThumbBoxPosition();
26746             this.setCanvasPosition();
26747         }
26748     },
26749     
26750     onFooterButtonClick : function(e, el, o, type)
26751     {
26752         switch (type) {
26753             case 'rotate-left' :
26754                 this.onRotateLeft(e);
26755                 break;
26756             case 'rotate-right' :
26757                 this.onRotateRight(e);
26758                 break;
26759             case 'picture' :
26760                 this.beforeSelectFile(e);
26761                 break;
26762             case 'trash' :
26763                 this.trash(e);
26764                 break;
26765             case 'crop' :
26766                 this.crop(e);
26767                 break;
26768             case 'download' :
26769                 this.download(e);
26770                 break;
26771             default :
26772                 break;
26773         }
26774         
26775         this.fireEvent('footerbuttonclick', this, type);
26776     },
26777     
26778     beforeSelectFile : function(e)
26779     {
26780         e.preventDefault();
26781         
26782         if(this.fireEvent('beforeselectfile', this) != false){
26783             this.selectorEl.dom.click();
26784         }
26785     },
26786     
26787     onFileSelected : function(e)
26788     {
26789         e.preventDefault();
26790         
26791         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26792             return;
26793         }
26794         
26795         var file = this.selectorEl.dom.files[0];
26796         
26797         if(this.fireEvent('inspect', this, file) != false){
26798             this.prepare(file);
26799         }
26800         
26801     },
26802     
26803     trash : function(e)
26804     {
26805         this.fireEvent('trash', this);
26806     },
26807     
26808     download : function(e)
26809     {
26810         this.fireEvent('download', this);
26811     },
26812     
26813     loadCanvas : function(src)
26814     {   
26815         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26816             
26817             this.reset();
26818             
26819             this.imageEl = document.createElement('img');
26820             
26821             var _this = this;
26822             
26823             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26824             
26825             this.imageEl.src = src;
26826         }
26827     },
26828     
26829     onLoadCanvas : function()
26830     {   
26831         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26832         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26833         
26834         this.bodyEl.un('click', this.beforeSelectFile, this);
26835         
26836         this.notifyEl.hide();
26837         this.thumbEl.show();
26838         this.footerEl.show();
26839         
26840         this.baseRotateLevel();
26841         
26842         if(this.isDocument){
26843             this.setThumbBoxSize();
26844         }
26845         
26846         this.setThumbBoxPosition();
26847         
26848         this.baseScaleLevel();
26849         
26850         this.draw();
26851         
26852         this.resize();
26853         
26854         this.canvasLoaded = true;
26855         
26856         if(this.loadMask){
26857             this.maskEl.unmask();
26858         }
26859         
26860     },
26861     
26862     setCanvasPosition : function()
26863     {   
26864         if(!this.canvasEl){
26865             return;
26866         }
26867         
26868         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26869         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26870         
26871         this.previewEl.setLeft(pw);
26872         this.previewEl.setTop(ph);
26873         
26874     },
26875     
26876     onMouseDown : function(e)
26877     {   
26878         e.stopEvent();
26879         
26880         this.dragable = true;
26881         this.pinching = false;
26882         
26883         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26884             this.dragable = false;
26885             return;
26886         }
26887         
26888         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26889         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26890         
26891     },
26892     
26893     onMouseMove : function(e)
26894     {   
26895         e.stopEvent();
26896         
26897         if(!this.canvasLoaded){
26898             return;
26899         }
26900         
26901         if (!this.dragable){
26902             return;
26903         }
26904         
26905         var minX = Math.ceil(this.thumbEl.getLeft(true));
26906         var minY = Math.ceil(this.thumbEl.getTop(true));
26907         
26908         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26909         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26910         
26911         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26912         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26913         
26914         x = x - this.mouseX;
26915         y = y - this.mouseY;
26916         
26917         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26918         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26919         
26920         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26921         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26922         
26923         this.previewEl.setLeft(bgX);
26924         this.previewEl.setTop(bgY);
26925         
26926         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26927         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26928     },
26929     
26930     onMouseUp : function(e)
26931     {   
26932         e.stopEvent();
26933         
26934         this.dragable = false;
26935     },
26936     
26937     onMouseWheel : function(e)
26938     {   
26939         e.stopEvent();
26940         
26941         this.startScale = this.scale;
26942         
26943         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26944         
26945         if(!this.zoomable()){
26946             this.scale = this.startScale;
26947             return;
26948         }
26949         
26950         this.draw();
26951         
26952         return;
26953     },
26954     
26955     zoomable : function()
26956     {
26957         var minScale = this.thumbEl.getWidth() / this.minWidth;
26958         
26959         if(this.minWidth < this.minHeight){
26960             minScale = this.thumbEl.getHeight() / this.minHeight;
26961         }
26962         
26963         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26964         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26965         
26966         if(
26967                 this.isDocument &&
26968                 (this.rotate == 0 || this.rotate == 180) && 
26969                 (
26970                     width > this.imageEl.OriginWidth || 
26971                     height > this.imageEl.OriginHeight ||
26972                     (width < this.minWidth && height < this.minHeight)
26973                 )
26974         ){
26975             return false;
26976         }
26977         
26978         if(
26979                 this.isDocument &&
26980                 (this.rotate == 90 || this.rotate == 270) && 
26981                 (
26982                     width > this.imageEl.OriginWidth || 
26983                     height > this.imageEl.OriginHeight ||
26984                     (width < this.minHeight && height < this.minWidth)
26985                 )
26986         ){
26987             return false;
26988         }
26989         
26990         if(
26991                 !this.isDocument &&
26992                 (this.rotate == 0 || this.rotate == 180) && 
26993                 (
26994                     width < this.minWidth || 
26995                     width > this.imageEl.OriginWidth || 
26996                     height < this.minHeight || 
26997                     height > this.imageEl.OriginHeight
26998                 )
26999         ){
27000             return false;
27001         }
27002         
27003         if(
27004                 !this.isDocument &&
27005                 (this.rotate == 90 || this.rotate == 270) && 
27006                 (
27007                     width < this.minHeight || 
27008                     width > this.imageEl.OriginWidth || 
27009                     height < this.minWidth || 
27010                     height > this.imageEl.OriginHeight
27011                 )
27012         ){
27013             return false;
27014         }
27015         
27016         return true;
27017         
27018     },
27019     
27020     onRotateLeft : function(e)
27021     {   
27022         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27023             
27024             var minScale = this.thumbEl.getWidth() / this.minWidth;
27025             
27026             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27027             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27028             
27029             this.startScale = this.scale;
27030             
27031             while (this.getScaleLevel() < minScale){
27032             
27033                 this.scale = this.scale + 1;
27034                 
27035                 if(!this.zoomable()){
27036                     break;
27037                 }
27038                 
27039                 if(
27040                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27041                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27042                 ){
27043                     continue;
27044                 }
27045                 
27046                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27047
27048                 this.draw();
27049                 
27050                 return;
27051             }
27052             
27053             this.scale = this.startScale;
27054             
27055             this.onRotateFail();
27056             
27057             return false;
27058         }
27059         
27060         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27061
27062         if(this.isDocument){
27063             this.setThumbBoxSize();
27064             this.setThumbBoxPosition();
27065             this.setCanvasPosition();
27066         }
27067         
27068         this.draw();
27069         
27070         this.fireEvent('rotate', this, 'left');
27071         
27072     },
27073     
27074     onRotateRight : function(e)
27075     {
27076         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27077             
27078             var minScale = this.thumbEl.getWidth() / this.minWidth;
27079         
27080             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27081             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27082             
27083             this.startScale = this.scale;
27084             
27085             while (this.getScaleLevel() < minScale){
27086             
27087                 this.scale = this.scale + 1;
27088                 
27089                 if(!this.zoomable()){
27090                     break;
27091                 }
27092                 
27093                 if(
27094                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27095                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27096                 ){
27097                     continue;
27098                 }
27099                 
27100                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27101
27102                 this.draw();
27103                 
27104                 return;
27105             }
27106             
27107             this.scale = this.startScale;
27108             
27109             this.onRotateFail();
27110             
27111             return false;
27112         }
27113         
27114         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27115
27116         if(this.isDocument){
27117             this.setThumbBoxSize();
27118             this.setThumbBoxPosition();
27119             this.setCanvasPosition();
27120         }
27121         
27122         this.draw();
27123         
27124         this.fireEvent('rotate', this, 'right');
27125     },
27126     
27127     onRotateFail : function()
27128     {
27129         this.errorEl.show(true);
27130         
27131         var _this = this;
27132         
27133         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27134     },
27135     
27136     draw : function()
27137     {
27138         this.previewEl.dom.innerHTML = '';
27139         
27140         var canvasEl = document.createElement("canvas");
27141         
27142         var contextEl = canvasEl.getContext("2d");
27143         
27144         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27145         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27146         var center = this.imageEl.OriginWidth / 2;
27147         
27148         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27149             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27150             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27151             center = this.imageEl.OriginHeight / 2;
27152         }
27153         
27154         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27155         
27156         contextEl.translate(center, center);
27157         contextEl.rotate(this.rotate * Math.PI / 180);
27158
27159         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27160         
27161         this.canvasEl = document.createElement("canvas");
27162         
27163         this.contextEl = this.canvasEl.getContext("2d");
27164         
27165         switch (this.rotate) {
27166             case 0 :
27167                 
27168                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27169                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27170                 
27171                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27172                 
27173                 break;
27174             case 90 : 
27175                 
27176                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27177                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27178                 
27179                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27180                     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);
27181                     break;
27182                 }
27183                 
27184                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27185                 
27186                 break;
27187             case 180 :
27188                 
27189                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27190                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27191                 
27192                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27193                     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);
27194                     break;
27195                 }
27196                 
27197                 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);
27198                 
27199                 break;
27200             case 270 :
27201                 
27202                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27203                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27204         
27205                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27206                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27207                     break;
27208                 }
27209                 
27210                 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);
27211                 
27212                 break;
27213             default : 
27214                 break;
27215         }
27216         
27217         this.previewEl.appendChild(this.canvasEl);
27218         
27219         this.setCanvasPosition();
27220     },
27221     
27222     crop : function()
27223     {
27224         if(!this.canvasLoaded){
27225             return;
27226         }
27227         
27228         var imageCanvas = document.createElement("canvas");
27229         
27230         var imageContext = imageCanvas.getContext("2d");
27231         
27232         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27233         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27234         
27235         var center = imageCanvas.width / 2;
27236         
27237         imageContext.translate(center, center);
27238         
27239         imageContext.rotate(this.rotate * Math.PI / 180);
27240         
27241         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27242         
27243         var canvas = document.createElement("canvas");
27244         
27245         var context = canvas.getContext("2d");
27246                 
27247         canvas.width = this.minWidth;
27248         canvas.height = this.minHeight;
27249
27250         switch (this.rotate) {
27251             case 0 :
27252                 
27253                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27254                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27255                 
27256                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27257                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27258                 
27259                 var targetWidth = this.minWidth - 2 * x;
27260                 var targetHeight = this.minHeight - 2 * y;
27261                 
27262                 var scale = 1;
27263                 
27264                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27265                     scale = targetWidth / width;
27266                 }
27267                 
27268                 if(x > 0 && y == 0){
27269                     scale = targetHeight / height;
27270                 }
27271                 
27272                 if(x > 0 && y > 0){
27273                     scale = targetWidth / width;
27274                     
27275                     if(width < height){
27276                         scale = targetHeight / height;
27277                     }
27278                 }
27279                 
27280                 context.scale(scale, scale);
27281                 
27282                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27283                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27284
27285                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27286                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27287
27288                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27289                 
27290                 break;
27291             case 90 : 
27292                 
27293                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27294                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27295                 
27296                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27297                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27298                 
27299                 var targetWidth = this.minWidth - 2 * x;
27300                 var targetHeight = this.minHeight - 2 * y;
27301                 
27302                 var scale = 1;
27303                 
27304                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27305                     scale = targetWidth / width;
27306                 }
27307                 
27308                 if(x > 0 && y == 0){
27309                     scale = targetHeight / height;
27310                 }
27311                 
27312                 if(x > 0 && y > 0){
27313                     scale = targetWidth / width;
27314                     
27315                     if(width < height){
27316                         scale = targetHeight / height;
27317                     }
27318                 }
27319                 
27320                 context.scale(scale, scale);
27321                 
27322                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27323                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27324
27325                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27326                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27327                 
27328                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27329                 
27330                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27331                 
27332                 break;
27333             case 180 :
27334                 
27335                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27336                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27337                 
27338                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27339                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27340                 
27341                 var targetWidth = this.minWidth - 2 * x;
27342                 var targetHeight = this.minHeight - 2 * y;
27343                 
27344                 var scale = 1;
27345                 
27346                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27347                     scale = targetWidth / width;
27348                 }
27349                 
27350                 if(x > 0 && y == 0){
27351                     scale = targetHeight / height;
27352                 }
27353                 
27354                 if(x > 0 && y > 0){
27355                     scale = targetWidth / width;
27356                     
27357                     if(width < height){
27358                         scale = targetHeight / height;
27359                     }
27360                 }
27361                 
27362                 context.scale(scale, scale);
27363                 
27364                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27365                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27366
27367                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27368                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27369
27370                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27371                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27372                 
27373                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27374                 
27375                 break;
27376             case 270 :
27377                 
27378                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27379                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27380                 
27381                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27382                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27383                 
27384                 var targetWidth = this.minWidth - 2 * x;
27385                 var targetHeight = this.minHeight - 2 * y;
27386                 
27387                 var scale = 1;
27388                 
27389                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27390                     scale = targetWidth / width;
27391                 }
27392                 
27393                 if(x > 0 && y == 0){
27394                     scale = targetHeight / height;
27395                 }
27396                 
27397                 if(x > 0 && y > 0){
27398                     scale = targetWidth / width;
27399                     
27400                     if(width < height){
27401                         scale = targetHeight / height;
27402                     }
27403                 }
27404                 
27405                 context.scale(scale, scale);
27406                 
27407                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27408                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27409
27410                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27411                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27412                 
27413                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27414                 
27415                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27416                 
27417                 break;
27418             default : 
27419                 break;
27420         }
27421         
27422         this.cropData = canvas.toDataURL(this.cropType);
27423         
27424         if(this.fireEvent('crop', this, this.cropData) !== false){
27425             this.process(this.file, this.cropData);
27426         }
27427         
27428         return;
27429         
27430     },
27431     
27432     setThumbBoxSize : function()
27433     {
27434         var width, height;
27435         
27436         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27437             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27438             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27439             
27440             this.minWidth = width;
27441             this.minHeight = height;
27442             
27443             if(this.rotate == 90 || this.rotate == 270){
27444                 this.minWidth = height;
27445                 this.minHeight = width;
27446             }
27447         }
27448         
27449         height = 300;
27450         width = Math.ceil(this.minWidth * height / this.minHeight);
27451         
27452         if(this.minWidth > this.minHeight){
27453             width = 300;
27454             height = Math.ceil(this.minHeight * width / this.minWidth);
27455         }
27456         
27457         this.thumbEl.setStyle({
27458             width : width + 'px',
27459             height : height + 'px'
27460         });
27461
27462         return;
27463             
27464     },
27465     
27466     setThumbBoxPosition : function()
27467     {
27468         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27469         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27470         
27471         this.thumbEl.setLeft(x);
27472         this.thumbEl.setTop(y);
27473         
27474     },
27475     
27476     baseRotateLevel : function()
27477     {
27478         this.baseRotate = 1;
27479         
27480         if(
27481                 typeof(this.exif) != 'undefined' &&
27482                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27483                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27484         ){
27485             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27486         }
27487         
27488         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27489         
27490     },
27491     
27492     baseScaleLevel : function()
27493     {
27494         var width, height;
27495         
27496         if(this.isDocument){
27497             
27498             if(this.baseRotate == 6 || this.baseRotate == 8){
27499             
27500                 height = this.thumbEl.getHeight();
27501                 this.baseScale = height / this.imageEl.OriginWidth;
27502
27503                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27504                     width = this.thumbEl.getWidth();
27505                     this.baseScale = width / this.imageEl.OriginHeight;
27506                 }
27507
27508                 return;
27509             }
27510
27511             height = this.thumbEl.getHeight();
27512             this.baseScale = height / this.imageEl.OriginHeight;
27513
27514             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27515                 width = this.thumbEl.getWidth();
27516                 this.baseScale = width / this.imageEl.OriginWidth;
27517             }
27518
27519             return;
27520         }
27521         
27522         if(this.baseRotate == 6 || this.baseRotate == 8){
27523             
27524             width = this.thumbEl.getHeight();
27525             this.baseScale = width / this.imageEl.OriginHeight;
27526             
27527             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27528                 height = this.thumbEl.getWidth();
27529                 this.baseScale = height / this.imageEl.OriginHeight;
27530             }
27531             
27532             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27533                 height = this.thumbEl.getWidth();
27534                 this.baseScale = height / this.imageEl.OriginHeight;
27535                 
27536                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27537                     width = this.thumbEl.getHeight();
27538                     this.baseScale = width / this.imageEl.OriginWidth;
27539                 }
27540             }
27541             
27542             return;
27543         }
27544         
27545         width = this.thumbEl.getWidth();
27546         this.baseScale = width / this.imageEl.OriginWidth;
27547         
27548         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27549             height = this.thumbEl.getHeight();
27550             this.baseScale = height / this.imageEl.OriginHeight;
27551         }
27552         
27553         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27554             
27555             height = this.thumbEl.getHeight();
27556             this.baseScale = height / this.imageEl.OriginHeight;
27557             
27558             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27559                 width = this.thumbEl.getWidth();
27560                 this.baseScale = width / this.imageEl.OriginWidth;
27561             }
27562             
27563         }
27564         
27565         return;
27566     },
27567     
27568     getScaleLevel : function()
27569     {
27570         return this.baseScale * Math.pow(1.1, this.scale);
27571     },
27572     
27573     onTouchStart : function(e)
27574     {
27575         if(!this.canvasLoaded){
27576             this.beforeSelectFile(e);
27577             return;
27578         }
27579         
27580         var touches = e.browserEvent.touches;
27581         
27582         if(!touches){
27583             return;
27584         }
27585         
27586         if(touches.length == 1){
27587             this.onMouseDown(e);
27588             return;
27589         }
27590         
27591         if(touches.length != 2){
27592             return;
27593         }
27594         
27595         var coords = [];
27596         
27597         for(var i = 0, finger; finger = touches[i]; i++){
27598             coords.push(finger.pageX, finger.pageY);
27599         }
27600         
27601         var x = Math.pow(coords[0] - coords[2], 2);
27602         var y = Math.pow(coords[1] - coords[3], 2);
27603         
27604         this.startDistance = Math.sqrt(x + y);
27605         
27606         this.startScale = this.scale;
27607         
27608         this.pinching = true;
27609         this.dragable = false;
27610         
27611     },
27612     
27613     onTouchMove : function(e)
27614     {
27615         if(!this.pinching && !this.dragable){
27616             return;
27617         }
27618         
27619         var touches = e.browserEvent.touches;
27620         
27621         if(!touches){
27622             return;
27623         }
27624         
27625         if(this.dragable){
27626             this.onMouseMove(e);
27627             return;
27628         }
27629         
27630         var coords = [];
27631         
27632         for(var i = 0, finger; finger = touches[i]; i++){
27633             coords.push(finger.pageX, finger.pageY);
27634         }
27635         
27636         var x = Math.pow(coords[0] - coords[2], 2);
27637         var y = Math.pow(coords[1] - coords[3], 2);
27638         
27639         this.endDistance = Math.sqrt(x + y);
27640         
27641         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27642         
27643         if(!this.zoomable()){
27644             this.scale = this.startScale;
27645             return;
27646         }
27647         
27648         this.draw();
27649         
27650     },
27651     
27652     onTouchEnd : function(e)
27653     {
27654         this.pinching = false;
27655         this.dragable = false;
27656         
27657     },
27658     
27659     process : function(file, crop)
27660     {
27661         if(this.loadMask){
27662             this.maskEl.mask(this.loadingText);
27663         }
27664         
27665         this.xhr = new XMLHttpRequest();
27666         
27667         file.xhr = this.xhr;
27668
27669         this.xhr.open(this.method, this.url, true);
27670         
27671         var headers = {
27672             "Accept": "application/json",
27673             "Cache-Control": "no-cache",
27674             "X-Requested-With": "XMLHttpRequest"
27675         };
27676         
27677         for (var headerName in headers) {
27678             var headerValue = headers[headerName];
27679             if (headerValue) {
27680                 this.xhr.setRequestHeader(headerName, headerValue);
27681             }
27682         }
27683         
27684         var _this = this;
27685         
27686         this.xhr.onload = function()
27687         {
27688             _this.xhrOnLoad(_this.xhr);
27689         }
27690         
27691         this.xhr.onerror = function()
27692         {
27693             _this.xhrOnError(_this.xhr);
27694         }
27695         
27696         var formData = new FormData();
27697
27698         formData.append('returnHTML', 'NO');
27699         
27700         if(crop){
27701             formData.append('crop', crop);
27702         }
27703         
27704         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27705             formData.append(this.paramName, file, file.name);
27706         }
27707         
27708         if(typeof(file.filename) != 'undefined'){
27709             formData.append('filename', file.filename);
27710         }
27711         
27712         if(typeof(file.mimetype) != 'undefined'){
27713             formData.append('mimetype', file.mimetype);
27714         }
27715         
27716         if(this.fireEvent('arrange', this, formData) != false){
27717             this.xhr.send(formData);
27718         };
27719     },
27720     
27721     xhrOnLoad : function(xhr)
27722     {
27723         if(this.loadMask){
27724             this.maskEl.unmask();
27725         }
27726         
27727         if (xhr.readyState !== 4) {
27728             this.fireEvent('exception', this, xhr);
27729             return;
27730         }
27731
27732         var response = Roo.decode(xhr.responseText);
27733         
27734         if(!response.success){
27735             this.fireEvent('exception', this, xhr);
27736             return;
27737         }
27738         
27739         var response = Roo.decode(xhr.responseText);
27740         
27741         this.fireEvent('upload', this, response);
27742         
27743     },
27744     
27745     xhrOnError : function()
27746     {
27747         if(this.loadMask){
27748             this.maskEl.unmask();
27749         }
27750         
27751         Roo.log('xhr on error');
27752         
27753         var response = Roo.decode(xhr.responseText);
27754           
27755         Roo.log(response);
27756         
27757     },
27758     
27759     prepare : function(file)
27760     {   
27761         if(this.loadMask){
27762             this.maskEl.mask(this.loadingText);
27763         }
27764         
27765         this.file = false;
27766         this.exif = {};
27767         
27768         if(typeof(file) === 'string'){
27769             this.loadCanvas(file);
27770             return;
27771         }
27772         
27773         if(!file || !this.urlAPI){
27774             return;
27775         }
27776         
27777         this.file = file;
27778         this.cropType = file.type;
27779         
27780         var _this = this;
27781         
27782         if(this.fireEvent('prepare', this, this.file) != false){
27783             
27784             var reader = new FileReader();
27785             
27786             reader.onload = function (e) {
27787                 if (e.target.error) {
27788                     Roo.log(e.target.error);
27789                     return;
27790                 }
27791                 
27792                 var buffer = e.target.result,
27793                     dataView = new DataView(buffer),
27794                     offset = 2,
27795                     maxOffset = dataView.byteLength - 4,
27796                     markerBytes,
27797                     markerLength;
27798                 
27799                 if (dataView.getUint16(0) === 0xffd8) {
27800                     while (offset < maxOffset) {
27801                         markerBytes = dataView.getUint16(offset);
27802                         
27803                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27804                             markerLength = dataView.getUint16(offset + 2) + 2;
27805                             if (offset + markerLength > dataView.byteLength) {
27806                                 Roo.log('Invalid meta data: Invalid segment size.');
27807                                 break;
27808                             }
27809                             
27810                             if(markerBytes == 0xffe1){
27811                                 _this.parseExifData(
27812                                     dataView,
27813                                     offset,
27814                                     markerLength
27815                                 );
27816                             }
27817                             
27818                             offset += markerLength;
27819                             
27820                             continue;
27821                         }
27822                         
27823                         break;
27824                     }
27825                     
27826                 }
27827                 
27828                 var url = _this.urlAPI.createObjectURL(_this.file);
27829                 
27830                 _this.loadCanvas(url);
27831                 
27832                 return;
27833             }
27834             
27835             reader.readAsArrayBuffer(this.file);
27836             
27837         }
27838         
27839     },
27840     
27841     parseExifData : function(dataView, offset, length)
27842     {
27843         var tiffOffset = offset + 10,
27844             littleEndian,
27845             dirOffset;
27846     
27847         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27848             // No Exif data, might be XMP data instead
27849             return;
27850         }
27851         
27852         // Check for the ASCII code for "Exif" (0x45786966):
27853         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27854             // No Exif data, might be XMP data instead
27855             return;
27856         }
27857         if (tiffOffset + 8 > dataView.byteLength) {
27858             Roo.log('Invalid Exif data: Invalid segment size.');
27859             return;
27860         }
27861         // Check for the two null bytes:
27862         if (dataView.getUint16(offset + 8) !== 0x0000) {
27863             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27864             return;
27865         }
27866         // Check the byte alignment:
27867         switch (dataView.getUint16(tiffOffset)) {
27868         case 0x4949:
27869             littleEndian = true;
27870             break;
27871         case 0x4D4D:
27872             littleEndian = false;
27873             break;
27874         default:
27875             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27876             return;
27877         }
27878         // Check for the TIFF tag marker (0x002A):
27879         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27880             Roo.log('Invalid Exif data: Missing TIFF marker.');
27881             return;
27882         }
27883         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27884         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27885         
27886         this.parseExifTags(
27887             dataView,
27888             tiffOffset,
27889             tiffOffset + dirOffset,
27890             littleEndian
27891         );
27892     },
27893     
27894     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27895     {
27896         var tagsNumber,
27897             dirEndOffset,
27898             i;
27899         if (dirOffset + 6 > dataView.byteLength) {
27900             Roo.log('Invalid Exif data: Invalid directory offset.');
27901             return;
27902         }
27903         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27904         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27905         if (dirEndOffset + 4 > dataView.byteLength) {
27906             Roo.log('Invalid Exif data: Invalid directory size.');
27907             return;
27908         }
27909         for (i = 0; i < tagsNumber; i += 1) {
27910             this.parseExifTag(
27911                 dataView,
27912                 tiffOffset,
27913                 dirOffset + 2 + 12 * i, // tag offset
27914                 littleEndian
27915             );
27916         }
27917         // Return the offset to the next directory:
27918         return dataView.getUint32(dirEndOffset, littleEndian);
27919     },
27920     
27921     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27922     {
27923         var tag = dataView.getUint16(offset, littleEndian);
27924         
27925         this.exif[tag] = this.getExifValue(
27926             dataView,
27927             tiffOffset,
27928             offset,
27929             dataView.getUint16(offset + 2, littleEndian), // tag type
27930             dataView.getUint32(offset + 4, littleEndian), // tag length
27931             littleEndian
27932         );
27933     },
27934     
27935     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27936     {
27937         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27938             tagSize,
27939             dataOffset,
27940             values,
27941             i,
27942             str,
27943             c;
27944     
27945         if (!tagType) {
27946             Roo.log('Invalid Exif data: Invalid tag type.');
27947             return;
27948         }
27949         
27950         tagSize = tagType.size * length;
27951         // Determine if the value is contained in the dataOffset bytes,
27952         // or if the value at the dataOffset is a pointer to the actual data:
27953         dataOffset = tagSize > 4 ?
27954                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27955         if (dataOffset + tagSize > dataView.byteLength) {
27956             Roo.log('Invalid Exif data: Invalid data offset.');
27957             return;
27958         }
27959         if (length === 1) {
27960             return tagType.getValue(dataView, dataOffset, littleEndian);
27961         }
27962         values = [];
27963         for (i = 0; i < length; i += 1) {
27964             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27965         }
27966         
27967         if (tagType.ascii) {
27968             str = '';
27969             // Concatenate the chars:
27970             for (i = 0; i < values.length; i += 1) {
27971                 c = values[i];
27972                 // Ignore the terminating NULL byte(s):
27973                 if (c === '\u0000') {
27974                     break;
27975                 }
27976                 str += c;
27977             }
27978             return str;
27979         }
27980         return values;
27981     }
27982     
27983 });
27984
27985 Roo.apply(Roo.bootstrap.UploadCropbox, {
27986     tags : {
27987         'Orientation': 0x0112
27988     },
27989     
27990     Orientation: {
27991             1: 0, //'top-left',
27992 //            2: 'top-right',
27993             3: 180, //'bottom-right',
27994 //            4: 'bottom-left',
27995 //            5: 'left-top',
27996             6: 90, //'right-top',
27997 //            7: 'right-bottom',
27998             8: 270 //'left-bottom'
27999     },
28000     
28001     exifTagTypes : {
28002         // byte, 8-bit unsigned int:
28003         1: {
28004             getValue: function (dataView, dataOffset) {
28005                 return dataView.getUint8(dataOffset);
28006             },
28007             size: 1
28008         },
28009         // ascii, 8-bit byte:
28010         2: {
28011             getValue: function (dataView, dataOffset) {
28012                 return String.fromCharCode(dataView.getUint8(dataOffset));
28013             },
28014             size: 1,
28015             ascii: true
28016         },
28017         // short, 16 bit int:
28018         3: {
28019             getValue: function (dataView, dataOffset, littleEndian) {
28020                 return dataView.getUint16(dataOffset, littleEndian);
28021             },
28022             size: 2
28023         },
28024         // long, 32 bit int:
28025         4: {
28026             getValue: function (dataView, dataOffset, littleEndian) {
28027                 return dataView.getUint32(dataOffset, littleEndian);
28028             },
28029             size: 4
28030         },
28031         // rational = two long values, first is numerator, second is denominator:
28032         5: {
28033             getValue: function (dataView, dataOffset, littleEndian) {
28034                 return dataView.getUint32(dataOffset, littleEndian) /
28035                     dataView.getUint32(dataOffset + 4, littleEndian);
28036             },
28037             size: 8
28038         },
28039         // slong, 32 bit signed int:
28040         9: {
28041             getValue: function (dataView, dataOffset, littleEndian) {
28042                 return dataView.getInt32(dataOffset, littleEndian);
28043             },
28044             size: 4
28045         },
28046         // srational, two slongs, first is numerator, second is denominator:
28047         10: {
28048             getValue: function (dataView, dataOffset, littleEndian) {
28049                 return dataView.getInt32(dataOffset, littleEndian) /
28050                     dataView.getInt32(dataOffset + 4, littleEndian);
28051             },
28052             size: 8
28053         }
28054     },
28055     
28056     footer : {
28057         STANDARD : [
28058             {
28059                 tag : 'div',
28060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28061                 action : 'rotate-left',
28062                 cn : [
28063                     {
28064                         tag : 'button',
28065                         cls : 'btn btn-default',
28066                         html : '<i class="fa fa-undo"></i>'
28067                     }
28068                 ]
28069             },
28070             {
28071                 tag : 'div',
28072                 cls : 'btn-group roo-upload-cropbox-picture',
28073                 action : 'picture',
28074                 cn : [
28075                     {
28076                         tag : 'button',
28077                         cls : 'btn btn-default',
28078                         html : '<i class="fa fa-picture-o"></i>'
28079                     }
28080                 ]
28081             },
28082             {
28083                 tag : 'div',
28084                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28085                 action : 'rotate-right',
28086                 cn : [
28087                     {
28088                         tag : 'button',
28089                         cls : 'btn btn-default',
28090                         html : '<i class="fa fa-repeat"></i>'
28091                     }
28092                 ]
28093             }
28094         ],
28095         DOCUMENT : [
28096             {
28097                 tag : 'div',
28098                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28099                 action : 'rotate-left',
28100                 cn : [
28101                     {
28102                         tag : 'button',
28103                         cls : 'btn btn-default',
28104                         html : '<i class="fa fa-undo"></i>'
28105                     }
28106                 ]
28107             },
28108             {
28109                 tag : 'div',
28110                 cls : 'btn-group roo-upload-cropbox-download',
28111                 action : 'download',
28112                 cn : [
28113                     {
28114                         tag : 'button',
28115                         cls : 'btn btn-default',
28116                         html : '<i class="fa fa-download"></i>'
28117                     }
28118                 ]
28119             },
28120             {
28121                 tag : 'div',
28122                 cls : 'btn-group roo-upload-cropbox-crop',
28123                 action : 'crop',
28124                 cn : [
28125                     {
28126                         tag : 'button',
28127                         cls : 'btn btn-default',
28128                         html : '<i class="fa fa-crop"></i>'
28129                     }
28130                 ]
28131             },
28132             {
28133                 tag : 'div',
28134                 cls : 'btn-group roo-upload-cropbox-trash',
28135                 action : 'trash',
28136                 cn : [
28137                     {
28138                         tag : 'button',
28139                         cls : 'btn btn-default',
28140                         html : '<i class="fa fa-trash"></i>'
28141                     }
28142                 ]
28143             },
28144             {
28145                 tag : 'div',
28146                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28147                 action : 'rotate-right',
28148                 cn : [
28149                     {
28150                         tag : 'button',
28151                         cls : 'btn btn-default',
28152                         html : '<i class="fa fa-repeat"></i>'
28153                     }
28154                 ]
28155             }
28156         ],
28157         ROTATOR : [
28158             {
28159                 tag : 'div',
28160                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28161                 action : 'rotate-left',
28162                 cn : [
28163                     {
28164                         tag : 'button',
28165                         cls : 'btn btn-default',
28166                         html : '<i class="fa fa-undo"></i>'
28167                     }
28168                 ]
28169             },
28170             {
28171                 tag : 'div',
28172                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28173                 action : 'rotate-right',
28174                 cn : [
28175                     {
28176                         tag : 'button',
28177                         cls : 'btn btn-default',
28178                         html : '<i class="fa fa-repeat"></i>'
28179                     }
28180                 ]
28181             }
28182         ]
28183     }
28184 });
28185
28186 /*
28187 * Licence: LGPL
28188 */
28189
28190 /**
28191  * @class Roo.bootstrap.DocumentManager
28192  * @extends Roo.bootstrap.Component
28193  * Bootstrap DocumentManager class
28194  * @cfg {String} paramName default 'imageUpload'
28195  * @cfg {String} toolTipName default 'filename'
28196  * @cfg {String} method default POST
28197  * @cfg {String} url action url
28198  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28199  * @cfg {Boolean} multiple multiple upload default true
28200  * @cfg {Number} thumbSize default 300
28201  * @cfg {String} fieldLabel
28202  * @cfg {Number} labelWidth default 4
28203  * @cfg {String} labelAlign (left|top) default left
28204  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28205 * @cfg {Number} labellg set the width of label (1-12)
28206  * @cfg {Number} labelmd set the width of label (1-12)
28207  * @cfg {Number} labelsm set the width of label (1-12)
28208  * @cfg {Number} labelxs set the width of label (1-12)
28209  * 
28210  * @constructor
28211  * Create a new DocumentManager
28212  * @param {Object} config The config object
28213  */
28214
28215 Roo.bootstrap.DocumentManager = function(config){
28216     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28217     
28218     this.files = [];
28219     this.delegates = [];
28220     
28221     this.addEvents({
28222         /**
28223          * @event initial
28224          * Fire when initial the DocumentManager
28225          * @param {Roo.bootstrap.DocumentManager} this
28226          */
28227         "initial" : true,
28228         /**
28229          * @event inspect
28230          * inspect selected file
28231          * @param {Roo.bootstrap.DocumentManager} this
28232          * @param {File} file
28233          */
28234         "inspect" : true,
28235         /**
28236          * @event exception
28237          * Fire when xhr load exception
28238          * @param {Roo.bootstrap.DocumentManager} this
28239          * @param {XMLHttpRequest} xhr
28240          */
28241         "exception" : true,
28242         /**
28243          * @event afterupload
28244          * Fire when xhr load exception
28245          * @param {Roo.bootstrap.DocumentManager} this
28246          * @param {XMLHttpRequest} xhr
28247          */
28248         "afterupload" : true,
28249         /**
28250          * @event prepare
28251          * prepare the form data
28252          * @param {Roo.bootstrap.DocumentManager} this
28253          * @param {Object} formData
28254          */
28255         "prepare" : true,
28256         /**
28257          * @event remove
28258          * Fire when remove the file
28259          * @param {Roo.bootstrap.DocumentManager} this
28260          * @param {Object} file
28261          */
28262         "remove" : true,
28263         /**
28264          * @event refresh
28265          * Fire after refresh the file
28266          * @param {Roo.bootstrap.DocumentManager} this
28267          */
28268         "refresh" : true,
28269         /**
28270          * @event click
28271          * Fire after click the image
28272          * @param {Roo.bootstrap.DocumentManager} this
28273          * @param {Object} file
28274          */
28275         "click" : true,
28276         /**
28277          * @event edit
28278          * Fire when upload a image and editable set to true
28279          * @param {Roo.bootstrap.DocumentManager} this
28280          * @param {Object} file
28281          */
28282         "edit" : true,
28283         /**
28284          * @event beforeselectfile
28285          * Fire before select file
28286          * @param {Roo.bootstrap.DocumentManager} this
28287          */
28288         "beforeselectfile" : true,
28289         /**
28290          * @event process
28291          * Fire before process file
28292          * @param {Roo.bootstrap.DocumentManager} this
28293          * @param {Object} file
28294          */
28295         "process" : true
28296         
28297     });
28298 };
28299
28300 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28301     
28302     boxes : 0,
28303     inputName : '',
28304     thumbSize : 300,
28305     multiple : true,
28306     files : false,
28307     method : 'POST',
28308     url : '',
28309     paramName : 'imageUpload',
28310     toolTipName : 'filename',
28311     fieldLabel : '',
28312     labelWidth : 4,
28313     labelAlign : 'left',
28314     editable : true,
28315     delegates : false,
28316     xhr : false, 
28317     
28318     labellg : 0,
28319     labelmd : 0,
28320     labelsm : 0,
28321     labelxs : 0,
28322     
28323     getAutoCreate : function()
28324     {   
28325         var managerWidget = {
28326             tag : 'div',
28327             cls : 'roo-document-manager',
28328             cn : [
28329                 {
28330                     tag : 'input',
28331                     cls : 'roo-document-manager-selector',
28332                     type : 'file'
28333                 },
28334                 {
28335                     tag : 'div',
28336                     cls : 'roo-document-manager-uploader',
28337                     cn : [
28338                         {
28339                             tag : 'div',
28340                             cls : 'roo-document-manager-upload-btn',
28341                             html : '<i class="fa fa-plus"></i>'
28342                         }
28343                     ]
28344                     
28345                 }
28346             ]
28347         };
28348         
28349         var content = [
28350             {
28351                 tag : 'div',
28352                 cls : 'column col-md-12',
28353                 cn : managerWidget
28354             }
28355         ];
28356         
28357         if(this.fieldLabel.length){
28358             
28359             content = [
28360                 {
28361                     tag : 'div',
28362                     cls : 'column col-md-12',
28363                     html : this.fieldLabel
28364                 },
28365                 {
28366                     tag : 'div',
28367                     cls : 'column col-md-12',
28368                     cn : managerWidget
28369                 }
28370             ];
28371
28372             if(this.labelAlign == 'left'){
28373                 content = [
28374                     {
28375                         tag : 'div',
28376                         cls : 'column',
28377                         html : this.fieldLabel
28378                     },
28379                     {
28380                         tag : 'div',
28381                         cls : 'column',
28382                         cn : managerWidget
28383                     }
28384                 ];
28385                 
28386                 if(this.labelWidth > 12){
28387                     content[0].style = "width: " + this.labelWidth + 'px';
28388                 }
28389
28390                 if(this.labelWidth < 13 && this.labelmd == 0){
28391                     this.labelmd = this.labelWidth;
28392                 }
28393
28394                 if(this.labellg > 0){
28395                     content[0].cls += ' col-lg-' + this.labellg;
28396                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28397                 }
28398
28399                 if(this.labelmd > 0){
28400                     content[0].cls += ' col-md-' + this.labelmd;
28401                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28402                 }
28403
28404                 if(this.labelsm > 0){
28405                     content[0].cls += ' col-sm-' + this.labelsm;
28406                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28407                 }
28408
28409                 if(this.labelxs > 0){
28410                     content[0].cls += ' col-xs-' + this.labelxs;
28411                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28412                 }
28413                 
28414             }
28415         }
28416         
28417         var cfg = {
28418             tag : 'div',
28419             cls : 'row clearfix',
28420             cn : content
28421         };
28422         
28423         return cfg;
28424         
28425     },
28426     
28427     initEvents : function()
28428     {
28429         this.managerEl = this.el.select('.roo-document-manager', true).first();
28430         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28431         
28432         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28433         this.selectorEl.hide();
28434         
28435         if(this.multiple){
28436             this.selectorEl.attr('multiple', 'multiple');
28437         }
28438         
28439         this.selectorEl.on('change', this.onFileSelected, this);
28440         
28441         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28442         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28443         
28444         this.uploader.on('click', this.onUploaderClick, this);
28445         
28446         this.renderProgressDialog();
28447         
28448         var _this = this;
28449         
28450         window.addEventListener("resize", function() { _this.refresh(); } );
28451         
28452         this.fireEvent('initial', this);
28453     },
28454     
28455     renderProgressDialog : function()
28456     {
28457         var _this = this;
28458         
28459         this.progressDialog = new Roo.bootstrap.Modal({
28460             cls : 'roo-document-manager-progress-dialog',
28461             allow_close : false,
28462             title : '',
28463             buttons : [
28464                 {
28465                     name  :'cancel',
28466                     weight : 'danger',
28467                     html : 'Cancel'
28468                 }
28469             ], 
28470             listeners : { 
28471                 btnclick : function() {
28472                     _this.uploadCancel();
28473                     this.hide();
28474                 }
28475             }
28476         });
28477          
28478         this.progressDialog.render(Roo.get(document.body));
28479          
28480         this.progress = new Roo.bootstrap.Progress({
28481             cls : 'roo-document-manager-progress',
28482             active : true,
28483             striped : true
28484         });
28485         
28486         this.progress.render(this.progressDialog.getChildContainer());
28487         
28488         this.progressBar = new Roo.bootstrap.ProgressBar({
28489             cls : 'roo-document-manager-progress-bar',
28490             aria_valuenow : 0,
28491             aria_valuemin : 0,
28492             aria_valuemax : 12,
28493             panel : 'success'
28494         });
28495         
28496         this.progressBar.render(this.progress.getChildContainer());
28497     },
28498     
28499     onUploaderClick : function(e)
28500     {
28501         e.preventDefault();
28502      
28503         if(this.fireEvent('beforeselectfile', this) != false){
28504             this.selectorEl.dom.click();
28505         }
28506         
28507     },
28508     
28509     onFileSelected : function(e)
28510     {
28511         e.preventDefault();
28512         
28513         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28514             return;
28515         }
28516         
28517         Roo.each(this.selectorEl.dom.files, function(file){
28518             if(this.fireEvent('inspect', this, file) != false){
28519                 this.files.push(file);
28520             }
28521         }, this);
28522         
28523         this.queue();
28524         
28525     },
28526     
28527     queue : function()
28528     {
28529         this.selectorEl.dom.value = '';
28530         
28531         if(!this.files.length){
28532             return;
28533         }
28534         
28535         if(this.boxes > 0 && this.files.length > this.boxes){
28536             this.files = this.files.slice(0, this.boxes);
28537         }
28538         
28539         this.uploader.show();
28540         
28541         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28542             this.uploader.hide();
28543         }
28544         
28545         var _this = this;
28546         
28547         var files = [];
28548         
28549         var docs = [];
28550         
28551         Roo.each(this.files, function(file){
28552             
28553             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28554                 var f = this.renderPreview(file);
28555                 files.push(f);
28556                 return;
28557             }
28558             
28559             if(file.type.indexOf('image') != -1){
28560                 this.delegates.push(
28561                     (function(){
28562                         _this.process(file);
28563                     }).createDelegate(this)
28564                 );
28565         
28566                 return;
28567             }
28568             
28569             docs.push(
28570                 (function(){
28571                     _this.process(file);
28572                 }).createDelegate(this)
28573             );
28574             
28575         }, this);
28576         
28577         this.files = files;
28578         
28579         this.delegates = this.delegates.concat(docs);
28580         
28581         if(!this.delegates.length){
28582             this.refresh();
28583             return;
28584         }
28585         
28586         this.progressBar.aria_valuemax = this.delegates.length;
28587         
28588         this.arrange();
28589         
28590         return;
28591     },
28592     
28593     arrange : function()
28594     {
28595         if(!this.delegates.length){
28596             this.progressDialog.hide();
28597             this.refresh();
28598             return;
28599         }
28600         
28601         var delegate = this.delegates.shift();
28602         
28603         this.progressDialog.show();
28604         
28605         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28606         
28607         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28608         
28609         delegate();
28610     },
28611     
28612     refresh : function()
28613     {
28614         this.uploader.show();
28615         
28616         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28617             this.uploader.hide();
28618         }
28619         
28620         Roo.isTouch ? this.closable(false) : this.closable(true);
28621         
28622         this.fireEvent('refresh', this);
28623     },
28624     
28625     onRemove : function(e, el, o)
28626     {
28627         e.preventDefault();
28628         
28629         this.fireEvent('remove', this, o);
28630         
28631     },
28632     
28633     remove : function(o)
28634     {
28635         var files = [];
28636         
28637         Roo.each(this.files, function(file){
28638             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28639                 files.push(file);
28640                 return;
28641             }
28642
28643             o.target.remove();
28644
28645         }, this);
28646         
28647         this.files = files;
28648         
28649         this.refresh();
28650     },
28651     
28652     clear : function()
28653     {
28654         Roo.each(this.files, function(file){
28655             if(!file.target){
28656                 return;
28657             }
28658             
28659             file.target.remove();
28660
28661         }, this);
28662         
28663         this.files = [];
28664         
28665         this.refresh();
28666     },
28667     
28668     onClick : function(e, el, o)
28669     {
28670         e.preventDefault();
28671         
28672         this.fireEvent('click', this, o);
28673         
28674     },
28675     
28676     closable : function(closable)
28677     {
28678         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28679             
28680             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28681             
28682             if(closable){
28683                 el.show();
28684                 return;
28685             }
28686             
28687             el.hide();
28688             
28689         }, this);
28690     },
28691     
28692     xhrOnLoad : function(xhr)
28693     {
28694         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28695             el.remove();
28696         }, this);
28697         
28698         if (xhr.readyState !== 4) {
28699             this.arrange();
28700             this.fireEvent('exception', this, xhr);
28701             return;
28702         }
28703
28704         var response = Roo.decode(xhr.responseText);
28705         
28706         if(!response.success){
28707             this.arrange();
28708             this.fireEvent('exception', this, xhr);
28709             return;
28710         }
28711         
28712         var file = this.renderPreview(response.data);
28713         
28714         this.files.push(file);
28715         
28716         this.arrange();
28717         
28718         this.fireEvent('afterupload', this, xhr);
28719         
28720     },
28721     
28722     xhrOnError : function(xhr)
28723     {
28724         Roo.log('xhr on error');
28725         
28726         var response = Roo.decode(xhr.responseText);
28727           
28728         Roo.log(response);
28729         
28730         this.arrange();
28731     },
28732     
28733     process : function(file)
28734     {
28735         if(this.fireEvent('process', this, file) !== false){
28736             if(this.editable && file.type.indexOf('image') != -1){
28737                 this.fireEvent('edit', this, file);
28738                 return;
28739             }
28740
28741             this.uploadStart(file, false);
28742
28743             return;
28744         }
28745         
28746     },
28747     
28748     uploadStart : function(file, crop)
28749     {
28750         this.xhr = new XMLHttpRequest();
28751         
28752         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28753             this.arrange();
28754             return;
28755         }
28756         
28757         file.xhr = this.xhr;
28758             
28759         this.managerEl.createChild({
28760             tag : 'div',
28761             cls : 'roo-document-manager-loading',
28762             cn : [
28763                 {
28764                     tag : 'div',
28765                     tooltip : file.name,
28766                     cls : 'roo-document-manager-thumb',
28767                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28768                 }
28769             ]
28770
28771         });
28772
28773         this.xhr.open(this.method, this.url, true);
28774         
28775         var headers = {
28776             "Accept": "application/json",
28777             "Cache-Control": "no-cache",
28778             "X-Requested-With": "XMLHttpRequest"
28779         };
28780         
28781         for (var headerName in headers) {
28782             var headerValue = headers[headerName];
28783             if (headerValue) {
28784                 this.xhr.setRequestHeader(headerName, headerValue);
28785             }
28786         }
28787         
28788         var _this = this;
28789         
28790         this.xhr.onload = function()
28791         {
28792             _this.xhrOnLoad(_this.xhr);
28793         }
28794         
28795         this.xhr.onerror = function()
28796         {
28797             _this.xhrOnError(_this.xhr);
28798         }
28799         
28800         var formData = new FormData();
28801
28802         formData.append('returnHTML', 'NO');
28803         
28804         if(crop){
28805             formData.append('crop', crop);
28806         }
28807         
28808         formData.append(this.paramName, file, file.name);
28809         
28810         var options = {
28811             file : file, 
28812             manually : false
28813         };
28814         
28815         if(this.fireEvent('prepare', this, formData, options) != false){
28816             
28817             if(options.manually){
28818                 return;
28819             }
28820             
28821             this.xhr.send(formData);
28822             return;
28823         };
28824         
28825         this.uploadCancel();
28826     },
28827     
28828     uploadCancel : function()
28829     {
28830         if (this.xhr) {
28831             this.xhr.abort();
28832         }
28833         
28834         this.delegates = [];
28835         
28836         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28837             el.remove();
28838         }, this);
28839         
28840         this.arrange();
28841     },
28842     
28843     renderPreview : function(file)
28844     {
28845         if(typeof(file.target) != 'undefined' && file.target){
28846             return file;
28847         }
28848         
28849         var previewEl = this.managerEl.createChild({
28850             tag : 'div',
28851             cls : 'roo-document-manager-preview',
28852             cn : [
28853                 {
28854                     tag : 'div',
28855                     tooltip : file[this.toolTipName],
28856                     cls : 'roo-document-manager-thumb',
28857                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28858                 },
28859                 {
28860                     tag : 'button',
28861                     cls : 'close',
28862                     html : '<i class="fa fa-times-circle"></i>'
28863                 }
28864             ]
28865         });
28866
28867         var close = previewEl.select('button.close', true).first();
28868
28869         close.on('click', this.onRemove, this, file);
28870
28871         file.target = previewEl;
28872
28873         var image = previewEl.select('img', true).first();
28874         
28875         var _this = this;
28876         
28877         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28878         
28879         image.on('click', this.onClick, this, file);
28880         
28881         return file;
28882         
28883     },
28884     
28885     onPreviewLoad : function(file, image)
28886     {
28887         if(typeof(file.target) == 'undefined' || !file.target){
28888             return;
28889         }
28890         
28891         var width = image.dom.naturalWidth || image.dom.width;
28892         var height = image.dom.naturalHeight || image.dom.height;
28893         
28894         if(width > height){
28895             file.target.addClass('wide');
28896             return;
28897         }
28898         
28899         file.target.addClass('tall');
28900         return;
28901         
28902     },
28903     
28904     uploadFromSource : function(file, crop)
28905     {
28906         this.xhr = new XMLHttpRequest();
28907         
28908         this.managerEl.createChild({
28909             tag : 'div',
28910             cls : 'roo-document-manager-loading',
28911             cn : [
28912                 {
28913                     tag : 'div',
28914                     tooltip : file.name,
28915                     cls : 'roo-document-manager-thumb',
28916                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28917                 }
28918             ]
28919
28920         });
28921
28922         this.xhr.open(this.method, this.url, true);
28923         
28924         var headers = {
28925             "Accept": "application/json",
28926             "Cache-Control": "no-cache",
28927             "X-Requested-With": "XMLHttpRequest"
28928         };
28929         
28930         for (var headerName in headers) {
28931             var headerValue = headers[headerName];
28932             if (headerValue) {
28933                 this.xhr.setRequestHeader(headerName, headerValue);
28934             }
28935         }
28936         
28937         var _this = this;
28938         
28939         this.xhr.onload = function()
28940         {
28941             _this.xhrOnLoad(_this.xhr);
28942         }
28943         
28944         this.xhr.onerror = function()
28945         {
28946             _this.xhrOnError(_this.xhr);
28947         }
28948         
28949         var formData = new FormData();
28950
28951         formData.append('returnHTML', 'NO');
28952         
28953         formData.append('crop', crop);
28954         
28955         if(typeof(file.filename) != 'undefined'){
28956             formData.append('filename', file.filename);
28957         }
28958         
28959         if(typeof(file.mimetype) != 'undefined'){
28960             formData.append('mimetype', file.mimetype);
28961         }
28962         
28963         Roo.log(formData);
28964         
28965         if(this.fireEvent('prepare', this, formData) != false){
28966             this.xhr.send(formData);
28967         };
28968     }
28969 });
28970
28971 /*
28972 * Licence: LGPL
28973 */
28974
28975 /**
28976  * @class Roo.bootstrap.DocumentViewer
28977  * @extends Roo.bootstrap.Component
28978  * Bootstrap DocumentViewer class
28979  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28980  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28981  * 
28982  * @constructor
28983  * Create a new DocumentViewer
28984  * @param {Object} config The config object
28985  */
28986
28987 Roo.bootstrap.DocumentViewer = function(config){
28988     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28989     
28990     this.addEvents({
28991         /**
28992          * @event initial
28993          * Fire after initEvent
28994          * @param {Roo.bootstrap.DocumentViewer} this
28995          */
28996         "initial" : true,
28997         /**
28998          * @event click
28999          * Fire after click
29000          * @param {Roo.bootstrap.DocumentViewer} this
29001          */
29002         "click" : true,
29003         /**
29004          * @event download
29005          * Fire after download button
29006          * @param {Roo.bootstrap.DocumentViewer} this
29007          */
29008         "download" : true,
29009         /**
29010          * @event trash
29011          * Fire after trash button
29012          * @param {Roo.bootstrap.DocumentViewer} this
29013          */
29014         "trash" : true
29015         
29016     });
29017 };
29018
29019 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29020     
29021     showDownload : true,
29022     
29023     showTrash : true,
29024     
29025     getAutoCreate : function()
29026     {
29027         var cfg = {
29028             tag : 'div',
29029             cls : 'roo-document-viewer',
29030             cn : [
29031                 {
29032                     tag : 'div',
29033                     cls : 'roo-document-viewer-body',
29034                     cn : [
29035                         {
29036                             tag : 'div',
29037                             cls : 'roo-document-viewer-thumb',
29038                             cn : [
29039                                 {
29040                                     tag : 'img',
29041                                     cls : 'roo-document-viewer-image'
29042                                 }
29043                             ]
29044                         }
29045                     ]
29046                 },
29047                 {
29048                     tag : 'div',
29049                     cls : 'roo-document-viewer-footer',
29050                     cn : {
29051                         tag : 'div',
29052                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29053                         cn : [
29054                             {
29055                                 tag : 'div',
29056                                 cls : 'btn-group roo-document-viewer-download',
29057                                 cn : [
29058                                     {
29059                                         tag : 'button',
29060                                         cls : 'btn btn-default',
29061                                         html : '<i class="fa fa-download"></i>'
29062                                     }
29063                                 ]
29064                             },
29065                             {
29066                                 tag : 'div',
29067                                 cls : 'btn-group roo-document-viewer-trash',
29068                                 cn : [
29069                                     {
29070                                         tag : 'button',
29071                                         cls : 'btn btn-default',
29072                                         html : '<i class="fa fa-trash"></i>'
29073                                     }
29074                                 ]
29075                             }
29076                         ]
29077                     }
29078                 }
29079             ]
29080         };
29081         
29082         return cfg;
29083     },
29084     
29085     initEvents : function()
29086     {
29087         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29088         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29089         
29090         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29091         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29092         
29093         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29094         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29095         
29096         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29097         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29098         
29099         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29100         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29101         
29102         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29103         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29104         
29105         this.bodyEl.on('click', this.onClick, this);
29106         this.downloadBtn.on('click', this.onDownload, this);
29107         this.trashBtn.on('click', this.onTrash, this);
29108         
29109         this.downloadBtn.hide();
29110         this.trashBtn.hide();
29111         
29112         if(this.showDownload){
29113             this.downloadBtn.show();
29114         }
29115         
29116         if(this.showTrash){
29117             this.trashBtn.show();
29118         }
29119         
29120         if(!this.showDownload && !this.showTrash) {
29121             this.footerEl.hide();
29122         }
29123         
29124     },
29125     
29126     initial : function()
29127     {
29128         this.fireEvent('initial', this);
29129         
29130     },
29131     
29132     onClick : function(e)
29133     {
29134         e.preventDefault();
29135         
29136         this.fireEvent('click', this);
29137     },
29138     
29139     onDownload : function(e)
29140     {
29141         e.preventDefault();
29142         
29143         this.fireEvent('download', this);
29144     },
29145     
29146     onTrash : function(e)
29147     {
29148         e.preventDefault();
29149         
29150         this.fireEvent('trash', this);
29151     }
29152     
29153 });
29154 /*
29155  * - LGPL
29156  *
29157  * nav progress bar
29158  * 
29159  */
29160
29161 /**
29162  * @class Roo.bootstrap.NavProgressBar
29163  * @extends Roo.bootstrap.Component
29164  * Bootstrap NavProgressBar class
29165  * 
29166  * @constructor
29167  * Create a new nav progress bar
29168  * @param {Object} config The config object
29169  */
29170
29171 Roo.bootstrap.NavProgressBar = function(config){
29172     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29173
29174     this.bullets = this.bullets || [];
29175    
29176 //    Roo.bootstrap.NavProgressBar.register(this);
29177      this.addEvents({
29178         /**
29179              * @event changed
29180              * Fires when the active item changes
29181              * @param {Roo.bootstrap.NavProgressBar} this
29182              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29183              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29184          */
29185         'changed': true
29186      });
29187     
29188 };
29189
29190 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29191     
29192     bullets : [],
29193     barItems : [],
29194     
29195     getAutoCreate : function()
29196     {
29197         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29198         
29199         cfg = {
29200             tag : 'div',
29201             cls : 'roo-navigation-bar-group',
29202             cn : [
29203                 {
29204                     tag : 'div',
29205                     cls : 'roo-navigation-top-bar'
29206                 },
29207                 {
29208                     tag : 'div',
29209                     cls : 'roo-navigation-bullets-bar',
29210                     cn : [
29211                         {
29212                             tag : 'ul',
29213                             cls : 'roo-navigation-bar'
29214                         }
29215                     ]
29216                 },
29217                 
29218                 {
29219                     tag : 'div',
29220                     cls : 'roo-navigation-bottom-bar'
29221                 }
29222             ]
29223             
29224         };
29225         
29226         return cfg;
29227         
29228     },
29229     
29230     initEvents: function() 
29231     {
29232         
29233     },
29234     
29235     onRender : function(ct, position) 
29236     {
29237         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29238         
29239         if(this.bullets.length){
29240             Roo.each(this.bullets, function(b){
29241                this.addItem(b);
29242             }, this);
29243         }
29244         
29245         this.format();
29246         
29247     },
29248     
29249     addItem : function(cfg)
29250     {
29251         var item = new Roo.bootstrap.NavProgressItem(cfg);
29252         
29253         item.parentId = this.id;
29254         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29255         
29256         if(cfg.html){
29257             var top = new Roo.bootstrap.Element({
29258                 tag : 'div',
29259                 cls : 'roo-navigation-bar-text'
29260             });
29261             
29262             var bottom = new Roo.bootstrap.Element({
29263                 tag : 'div',
29264                 cls : 'roo-navigation-bar-text'
29265             });
29266             
29267             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29268             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29269             
29270             var topText = new Roo.bootstrap.Element({
29271                 tag : 'span',
29272                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29273             });
29274             
29275             var bottomText = new Roo.bootstrap.Element({
29276                 tag : 'span',
29277                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29278             });
29279             
29280             topText.onRender(top.el, null);
29281             bottomText.onRender(bottom.el, null);
29282             
29283             item.topEl = top;
29284             item.bottomEl = bottom;
29285         }
29286         
29287         this.barItems.push(item);
29288         
29289         return item;
29290     },
29291     
29292     getActive : function()
29293     {
29294         var active = false;
29295         
29296         Roo.each(this.barItems, function(v){
29297             
29298             if (!v.isActive()) {
29299                 return;
29300             }
29301             
29302             active = v;
29303             return false;
29304             
29305         });
29306         
29307         return active;
29308     },
29309     
29310     setActiveItem : function(item)
29311     {
29312         var prev = false;
29313         
29314         Roo.each(this.barItems, function(v){
29315             if (v.rid == item.rid) {
29316                 return ;
29317             }
29318             
29319             if (v.isActive()) {
29320                 v.setActive(false);
29321                 prev = v;
29322             }
29323         });
29324
29325         item.setActive(true);
29326         
29327         this.fireEvent('changed', this, item, prev);
29328     },
29329     
29330     getBarItem: function(rid)
29331     {
29332         var ret = false;
29333         
29334         Roo.each(this.barItems, function(e) {
29335             if (e.rid != rid) {
29336                 return;
29337             }
29338             
29339             ret =  e;
29340             return false;
29341         });
29342         
29343         return ret;
29344     },
29345     
29346     indexOfItem : function(item)
29347     {
29348         var index = false;
29349         
29350         Roo.each(this.barItems, function(v, i){
29351             
29352             if (v.rid != item.rid) {
29353                 return;
29354             }
29355             
29356             index = i;
29357             return false
29358         });
29359         
29360         return index;
29361     },
29362     
29363     setActiveNext : function()
29364     {
29365         var i = this.indexOfItem(this.getActive());
29366         
29367         if (i > this.barItems.length) {
29368             return;
29369         }
29370         
29371         this.setActiveItem(this.barItems[i+1]);
29372     },
29373     
29374     setActivePrev : function()
29375     {
29376         var i = this.indexOfItem(this.getActive());
29377         
29378         if (i  < 1) {
29379             return;
29380         }
29381         
29382         this.setActiveItem(this.barItems[i-1]);
29383     },
29384     
29385     format : function()
29386     {
29387         if(!this.barItems.length){
29388             return;
29389         }
29390      
29391         var width = 100 / this.barItems.length;
29392         
29393         Roo.each(this.barItems, function(i){
29394             i.el.setStyle('width', width + '%');
29395             i.topEl.el.setStyle('width', width + '%');
29396             i.bottomEl.el.setStyle('width', width + '%');
29397         }, this);
29398         
29399     }
29400     
29401 });
29402 /*
29403  * - LGPL
29404  *
29405  * Nav Progress Item
29406  * 
29407  */
29408
29409 /**
29410  * @class Roo.bootstrap.NavProgressItem
29411  * @extends Roo.bootstrap.Component
29412  * Bootstrap NavProgressItem class
29413  * @cfg {String} rid the reference id
29414  * @cfg {Boolean} active (true|false) Is item active default false
29415  * @cfg {Boolean} disabled (true|false) Is item active default false
29416  * @cfg {String} html
29417  * @cfg {String} position (top|bottom) text position default bottom
29418  * @cfg {String} icon show icon instead of number
29419  * 
29420  * @constructor
29421  * Create a new NavProgressItem
29422  * @param {Object} config The config object
29423  */
29424 Roo.bootstrap.NavProgressItem = function(config){
29425     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29426     this.addEvents({
29427         // raw events
29428         /**
29429          * @event click
29430          * The raw click event for the entire grid.
29431          * @param {Roo.bootstrap.NavProgressItem} this
29432          * @param {Roo.EventObject} e
29433          */
29434         "click" : true
29435     });
29436    
29437 };
29438
29439 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29440     
29441     rid : '',
29442     active : false,
29443     disabled : false,
29444     html : '',
29445     position : 'bottom',
29446     icon : false,
29447     
29448     getAutoCreate : function()
29449     {
29450         var iconCls = 'roo-navigation-bar-item-icon';
29451         
29452         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29453         
29454         var cfg = {
29455             tag: 'li',
29456             cls: 'roo-navigation-bar-item',
29457             cn : [
29458                 {
29459                     tag : 'i',
29460                     cls : iconCls
29461                 }
29462             ]
29463         };
29464         
29465         if(this.active){
29466             cfg.cls += ' active';
29467         }
29468         if(this.disabled){
29469             cfg.cls += ' disabled';
29470         }
29471         
29472         return cfg;
29473     },
29474     
29475     disable : function()
29476     {
29477         this.setDisabled(true);
29478     },
29479     
29480     enable : function()
29481     {
29482         this.setDisabled(false);
29483     },
29484     
29485     initEvents: function() 
29486     {
29487         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29488         
29489         this.iconEl.on('click', this.onClick, this);
29490     },
29491     
29492     onClick : function(e)
29493     {
29494         e.preventDefault();
29495         
29496         if(this.disabled){
29497             return;
29498         }
29499         
29500         if(this.fireEvent('click', this, e) === false){
29501             return;
29502         };
29503         
29504         this.parent().setActiveItem(this);
29505     },
29506     
29507     isActive: function () 
29508     {
29509         return this.active;
29510     },
29511     
29512     setActive : function(state)
29513     {
29514         if(this.active == state){
29515             return;
29516         }
29517         
29518         this.active = state;
29519         
29520         if (state) {
29521             this.el.addClass('active');
29522             return;
29523         }
29524         
29525         this.el.removeClass('active');
29526         
29527         return;
29528     },
29529     
29530     setDisabled : function(state)
29531     {
29532         if(this.disabled == state){
29533             return;
29534         }
29535         
29536         this.disabled = state;
29537         
29538         if (state) {
29539             this.el.addClass('disabled');
29540             return;
29541         }
29542         
29543         this.el.removeClass('disabled');
29544     },
29545     
29546     tooltipEl : function()
29547     {
29548         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29549     }
29550 });
29551  
29552
29553  /*
29554  * - LGPL
29555  *
29556  * FieldLabel
29557  * 
29558  */
29559
29560 /**
29561  * @class Roo.bootstrap.FieldLabel
29562  * @extends Roo.bootstrap.Component
29563  * Bootstrap FieldLabel class
29564  * @cfg {String} html contents of the element
29565  * @cfg {String} tag tag of the element default label
29566  * @cfg {String} cls class of the element
29567  * @cfg {String} target label target 
29568  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29569  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29570  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29571  * @cfg {String} iconTooltip default "This field is required"
29572  * 
29573  * @constructor
29574  * Create a new FieldLabel
29575  * @param {Object} config The config object
29576  */
29577
29578 Roo.bootstrap.FieldLabel = function(config){
29579     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29580     
29581     this.addEvents({
29582             /**
29583              * @event invalid
29584              * Fires after the field has been marked as invalid.
29585              * @param {Roo.form.FieldLabel} this
29586              * @param {String} msg The validation message
29587              */
29588             invalid : true,
29589             /**
29590              * @event valid
29591              * Fires after the field has been validated with no errors.
29592              * @param {Roo.form.FieldLabel} this
29593              */
29594             valid : true
29595         });
29596 };
29597
29598 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29599     
29600     tag: 'label',
29601     cls: '',
29602     html: '',
29603     target: '',
29604     allowBlank : true,
29605     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29606     validClass : 'text-success fa fa-lg fa-check',
29607     iconTooltip : 'This field is required',
29608     
29609     getAutoCreate : function(){
29610         
29611         var cfg = {
29612             tag : this.tag,
29613             cls : 'roo-bootstrap-field-label ' + this.cls,
29614             for : this.target,
29615             cn : [
29616                 {
29617                     tag : 'i',
29618                     cls : '',
29619                     tooltip : this.iconTooltip
29620                 },
29621                 {
29622                     tag : 'span',
29623                     html : this.html
29624                 }
29625             ] 
29626         };
29627         
29628         return cfg;
29629     },
29630     
29631     initEvents: function() 
29632     {
29633         Roo.bootstrap.Element.superclass.initEvents.call(this);
29634         
29635         this.iconEl = this.el.select('i', true).first();
29636         
29637         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29638         
29639         Roo.bootstrap.FieldLabel.register(this);
29640     },
29641     
29642     /**
29643      * Mark this field as valid
29644      */
29645     markValid : function()
29646     {
29647         this.iconEl.show();
29648         
29649         this.iconEl.removeClass(this.invalidClass);
29650         
29651         this.iconEl.addClass(this.validClass);
29652         
29653         this.fireEvent('valid', this);
29654     },
29655     
29656     /**
29657      * Mark this field as invalid
29658      * @param {String} msg The validation message
29659      */
29660     markInvalid : function(msg)
29661     {
29662         this.iconEl.show();
29663         
29664         this.iconEl.removeClass(this.validClass);
29665         
29666         this.iconEl.addClass(this.invalidClass);
29667         
29668         this.fireEvent('invalid', this, msg);
29669     }
29670     
29671    
29672 });
29673
29674 Roo.apply(Roo.bootstrap.FieldLabel, {
29675     
29676     groups: {},
29677     
29678      /**
29679     * register a FieldLabel Group
29680     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29681     */
29682     register : function(label)
29683     {
29684         if(this.groups.hasOwnProperty(label.target)){
29685             return;
29686         }
29687      
29688         this.groups[label.target] = label;
29689         
29690     },
29691     /**
29692     * fetch a FieldLabel Group based on the target
29693     * @param {string} target
29694     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29695     */
29696     get: function(target) {
29697         if (typeof(this.groups[target]) == 'undefined') {
29698             return false;
29699         }
29700         
29701         return this.groups[target] ;
29702     }
29703 });
29704
29705  
29706
29707  /*
29708  * - LGPL
29709  *
29710  * page DateSplitField.
29711  * 
29712  */
29713
29714
29715 /**
29716  * @class Roo.bootstrap.DateSplitField
29717  * @extends Roo.bootstrap.Component
29718  * Bootstrap DateSplitField class
29719  * @cfg {string} fieldLabel - the label associated
29720  * @cfg {Number} labelWidth set the width of label (0-12)
29721  * @cfg {String} labelAlign (top|left)
29722  * @cfg {Boolean} dayAllowBlank (true|false) default false
29723  * @cfg {Boolean} monthAllowBlank (true|false) default false
29724  * @cfg {Boolean} yearAllowBlank (true|false) default false
29725  * @cfg {string} dayPlaceholder 
29726  * @cfg {string} monthPlaceholder
29727  * @cfg {string} yearPlaceholder
29728  * @cfg {string} dayFormat default 'd'
29729  * @cfg {string} monthFormat default 'm'
29730  * @cfg {string} yearFormat default 'Y'
29731  * @cfg {Number} labellg set the width of label (1-12)
29732  * @cfg {Number} labelmd set the width of label (1-12)
29733  * @cfg {Number} labelsm set the width of label (1-12)
29734  * @cfg {Number} labelxs set the width of label (1-12)
29735
29736  *     
29737  * @constructor
29738  * Create a new DateSplitField
29739  * @param {Object} config The config object
29740  */
29741
29742 Roo.bootstrap.DateSplitField = function(config){
29743     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29744     
29745     this.addEvents({
29746         // raw events
29747          /**
29748          * @event years
29749          * getting the data of years
29750          * @param {Roo.bootstrap.DateSplitField} this
29751          * @param {Object} years
29752          */
29753         "years" : true,
29754         /**
29755          * @event days
29756          * getting the data of days
29757          * @param {Roo.bootstrap.DateSplitField} this
29758          * @param {Object} days
29759          */
29760         "days" : true,
29761         /**
29762          * @event invalid
29763          * Fires after the field has been marked as invalid.
29764          * @param {Roo.form.Field} this
29765          * @param {String} msg The validation message
29766          */
29767         invalid : true,
29768        /**
29769          * @event valid
29770          * Fires after the field has been validated with no errors.
29771          * @param {Roo.form.Field} this
29772          */
29773         valid : true
29774     });
29775 };
29776
29777 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29778     
29779     fieldLabel : '',
29780     labelAlign : 'top',
29781     labelWidth : 3,
29782     dayAllowBlank : false,
29783     monthAllowBlank : false,
29784     yearAllowBlank : false,
29785     dayPlaceholder : '',
29786     monthPlaceholder : '',
29787     yearPlaceholder : '',
29788     dayFormat : 'd',
29789     monthFormat : 'm',
29790     yearFormat : 'Y',
29791     isFormField : true,
29792     labellg : 0,
29793     labelmd : 0,
29794     labelsm : 0,
29795     labelxs : 0,
29796     
29797     getAutoCreate : function()
29798     {
29799         var cfg = {
29800             tag : 'div',
29801             cls : 'row roo-date-split-field-group',
29802             cn : [
29803                 {
29804                     tag : 'input',
29805                     type : 'hidden',
29806                     cls : 'form-hidden-field roo-date-split-field-group-value',
29807                     name : this.name
29808                 }
29809             ]
29810         };
29811         
29812         var labelCls = 'col-md-12';
29813         var contentCls = 'col-md-4';
29814         
29815         if(this.fieldLabel){
29816             
29817             var label = {
29818                 tag : 'div',
29819                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29820                 cn : [
29821                     {
29822                         tag : 'label',
29823                         html : this.fieldLabel
29824                     }
29825                 ]
29826             };
29827             
29828             if(this.labelAlign == 'left'){
29829             
29830                 if(this.labelWidth > 12){
29831                     label.style = "width: " + this.labelWidth + 'px';
29832                 }
29833
29834                 if(this.labelWidth < 13 && this.labelmd == 0){
29835                     this.labelmd = this.labelWidth;
29836                 }
29837
29838                 if(this.labellg > 0){
29839                     labelCls = ' col-lg-' + this.labellg;
29840                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29841                 }
29842
29843                 if(this.labelmd > 0){
29844                     labelCls = ' col-md-' + this.labelmd;
29845                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29846                 }
29847
29848                 if(this.labelsm > 0){
29849                     labelCls = ' col-sm-' + this.labelsm;
29850                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29851                 }
29852
29853                 if(this.labelxs > 0){
29854                     labelCls = ' col-xs-' + this.labelxs;
29855                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29856                 }
29857             }
29858             
29859             label.cls += ' ' + labelCls;
29860             
29861             cfg.cn.push(label);
29862         }
29863         
29864         Roo.each(['day', 'month', 'year'], function(t){
29865             cfg.cn.push({
29866                 tag : 'div',
29867                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29868             });
29869         }, this);
29870         
29871         return cfg;
29872     },
29873     
29874     inputEl: function ()
29875     {
29876         return this.el.select('.roo-date-split-field-group-value', true).first();
29877     },
29878     
29879     onRender : function(ct, position) 
29880     {
29881         var _this = this;
29882         
29883         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29884         
29885         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29886         
29887         this.dayField = new Roo.bootstrap.ComboBox({
29888             allowBlank : this.dayAllowBlank,
29889             alwaysQuery : true,
29890             displayField : 'value',
29891             editable : false,
29892             fieldLabel : '',
29893             forceSelection : true,
29894             mode : 'local',
29895             placeholder : this.dayPlaceholder,
29896             selectOnFocus : true,
29897             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29898             triggerAction : 'all',
29899             typeAhead : true,
29900             valueField : 'value',
29901             store : new Roo.data.SimpleStore({
29902                 data : (function() {    
29903                     var days = [];
29904                     _this.fireEvent('days', _this, days);
29905                     return days;
29906                 })(),
29907                 fields : [ 'value' ]
29908             }),
29909             listeners : {
29910                 select : function (_self, record, index)
29911                 {
29912                     _this.setValue(_this.getValue());
29913                 }
29914             }
29915         });
29916
29917         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29918         
29919         this.monthField = new Roo.bootstrap.MonthField({
29920             after : '<i class=\"fa fa-calendar\"></i>',
29921             allowBlank : this.monthAllowBlank,
29922             placeholder : this.monthPlaceholder,
29923             readOnly : true,
29924             listeners : {
29925                 render : function (_self)
29926                 {
29927                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29928                         e.preventDefault();
29929                         _self.focus();
29930                     });
29931                 },
29932                 select : function (_self, oldvalue, newvalue)
29933                 {
29934                     _this.setValue(_this.getValue());
29935                 }
29936             }
29937         });
29938         
29939         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29940         
29941         this.yearField = new Roo.bootstrap.ComboBox({
29942             allowBlank : this.yearAllowBlank,
29943             alwaysQuery : true,
29944             displayField : 'value',
29945             editable : false,
29946             fieldLabel : '',
29947             forceSelection : true,
29948             mode : 'local',
29949             placeholder : this.yearPlaceholder,
29950             selectOnFocus : true,
29951             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29952             triggerAction : 'all',
29953             typeAhead : true,
29954             valueField : 'value',
29955             store : new Roo.data.SimpleStore({
29956                 data : (function() {
29957                     var years = [];
29958                     _this.fireEvent('years', _this, years);
29959                     return years;
29960                 })(),
29961                 fields : [ 'value' ]
29962             }),
29963             listeners : {
29964                 select : function (_self, record, index)
29965                 {
29966                     _this.setValue(_this.getValue());
29967                 }
29968             }
29969         });
29970
29971         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29972     },
29973     
29974     setValue : function(v, format)
29975     {
29976         this.inputEl.dom.value = v;
29977         
29978         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29979         
29980         var d = Date.parseDate(v, f);
29981         
29982         if(!d){
29983             this.validate();
29984             return;
29985         }
29986         
29987         this.setDay(d.format(this.dayFormat));
29988         this.setMonth(d.format(this.monthFormat));
29989         this.setYear(d.format(this.yearFormat));
29990         
29991         this.validate();
29992         
29993         return;
29994     },
29995     
29996     setDay : function(v)
29997     {
29998         this.dayField.setValue(v);
29999         this.inputEl.dom.value = this.getValue();
30000         this.validate();
30001         return;
30002     },
30003     
30004     setMonth : function(v)
30005     {
30006         this.monthField.setValue(v, true);
30007         this.inputEl.dom.value = this.getValue();
30008         this.validate();
30009         return;
30010     },
30011     
30012     setYear : function(v)
30013     {
30014         this.yearField.setValue(v);
30015         this.inputEl.dom.value = this.getValue();
30016         this.validate();
30017         return;
30018     },
30019     
30020     getDay : function()
30021     {
30022         return this.dayField.getValue();
30023     },
30024     
30025     getMonth : function()
30026     {
30027         return this.monthField.getValue();
30028     },
30029     
30030     getYear : function()
30031     {
30032         return this.yearField.getValue();
30033     },
30034     
30035     getValue : function()
30036     {
30037         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30038         
30039         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30040         
30041         return date;
30042     },
30043     
30044     reset : function()
30045     {
30046         this.setDay('');
30047         this.setMonth('');
30048         this.setYear('');
30049         this.inputEl.dom.value = '';
30050         this.validate();
30051         return;
30052     },
30053     
30054     validate : function()
30055     {
30056         var d = this.dayField.validate();
30057         var m = this.monthField.validate();
30058         var y = this.yearField.validate();
30059         
30060         var valid = true;
30061         
30062         if(
30063                 (!this.dayAllowBlank && !d) ||
30064                 (!this.monthAllowBlank && !m) ||
30065                 (!this.yearAllowBlank && !y)
30066         ){
30067             valid = false;
30068         }
30069         
30070         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30071             return valid;
30072         }
30073         
30074         if(valid){
30075             this.markValid();
30076             return valid;
30077         }
30078         
30079         this.markInvalid();
30080         
30081         return valid;
30082     },
30083     
30084     markValid : function()
30085     {
30086         
30087         var label = this.el.select('label', true).first();
30088         var icon = this.el.select('i.fa-star', true).first();
30089
30090         if(label && icon){
30091             icon.remove();
30092         }
30093         
30094         this.fireEvent('valid', this);
30095     },
30096     
30097      /**
30098      * Mark this field as invalid
30099      * @param {String} msg The validation message
30100      */
30101     markInvalid : function(msg)
30102     {
30103         
30104         var label = this.el.select('label', true).first();
30105         var icon = this.el.select('i.fa-star', true).first();
30106
30107         if(label && !icon){
30108             this.el.select('.roo-date-split-field-label', true).createChild({
30109                 tag : 'i',
30110                 cls : 'text-danger fa fa-lg fa-star',
30111                 tooltip : 'This field is required',
30112                 style : 'margin-right:5px;'
30113             }, label, true);
30114         }
30115         
30116         this.fireEvent('invalid', this, msg);
30117     },
30118     
30119     clearInvalid : function()
30120     {
30121         var label = this.el.select('label', true).first();
30122         var icon = this.el.select('i.fa-star', true).first();
30123
30124         if(label && icon){
30125             icon.remove();
30126         }
30127         
30128         this.fireEvent('valid', this);
30129     },
30130     
30131     getName: function()
30132     {
30133         return this.name;
30134     }
30135     
30136 });
30137
30138  /**
30139  *
30140  * This is based on 
30141  * http://masonry.desandro.com
30142  *
30143  * The idea is to render all the bricks based on vertical width...
30144  *
30145  * The original code extends 'outlayer' - we might need to use that....
30146  * 
30147  */
30148
30149
30150 /**
30151  * @class Roo.bootstrap.LayoutMasonry
30152  * @extends Roo.bootstrap.Component
30153  * Bootstrap Layout Masonry class
30154  * 
30155  * @constructor
30156  * Create a new Element
30157  * @param {Object} config The config object
30158  */
30159
30160 Roo.bootstrap.LayoutMasonry = function(config){
30161     
30162     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30163     
30164     this.bricks = [];
30165     
30166     Roo.bootstrap.LayoutMasonry.register(this);
30167     
30168     this.addEvents({
30169         // raw events
30170         /**
30171          * @event layout
30172          * Fire after layout the items
30173          * @param {Roo.bootstrap.LayoutMasonry} this
30174          * @param {Roo.EventObject} e
30175          */
30176         "layout" : true
30177     });
30178     
30179 };
30180
30181 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30182     
30183     /**
30184      * @cfg {Boolean} isLayoutInstant = no animation?
30185      */   
30186     isLayoutInstant : false, // needed?
30187    
30188     /**
30189      * @cfg {Number} boxWidth  width of the columns
30190      */   
30191     boxWidth : 450,
30192     
30193       /**
30194      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30195      */   
30196     boxHeight : 0,
30197     
30198     /**
30199      * @cfg {Number} padWidth padding below box..
30200      */   
30201     padWidth : 10, 
30202     
30203     /**
30204      * @cfg {Number} gutter gutter width..
30205      */   
30206     gutter : 10,
30207     
30208      /**
30209      * @cfg {Number} maxCols maximum number of columns
30210      */   
30211     
30212     maxCols: 0,
30213     
30214     /**
30215      * @cfg {Boolean} isAutoInitial defalut true
30216      */   
30217     isAutoInitial : true, 
30218     
30219     containerWidth: 0,
30220     
30221     /**
30222      * @cfg {Boolean} isHorizontal defalut false
30223      */   
30224     isHorizontal : false, 
30225
30226     currentSize : null,
30227     
30228     tag: 'div',
30229     
30230     cls: '',
30231     
30232     bricks: null, //CompositeElement
30233     
30234     cols : 1,
30235     
30236     _isLayoutInited : false,
30237     
30238 //    isAlternative : false, // only use for vertical layout...
30239     
30240     /**
30241      * @cfg {Number} alternativePadWidth padding below box..
30242      */   
30243     alternativePadWidth : 50,
30244     
30245     selectedBrick : [],
30246     
30247     getAutoCreate : function(){
30248         
30249         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30250         
30251         var cfg = {
30252             tag: this.tag,
30253             cls: 'blog-masonary-wrapper ' + this.cls,
30254             cn : {
30255                 cls : 'mas-boxes masonary'
30256             }
30257         };
30258         
30259         return cfg;
30260     },
30261     
30262     getChildContainer: function( )
30263     {
30264         if (this.boxesEl) {
30265             return this.boxesEl;
30266         }
30267         
30268         this.boxesEl = this.el.select('.mas-boxes').first();
30269         
30270         return this.boxesEl;
30271     },
30272     
30273     
30274     initEvents : function()
30275     {
30276         var _this = this;
30277         
30278         if(this.isAutoInitial){
30279             Roo.log('hook children rendered');
30280             this.on('childrenrendered', function() {
30281                 Roo.log('children rendered');
30282                 _this.initial();
30283             } ,this);
30284         }
30285     },
30286     
30287     initial : function()
30288     {
30289         this.selectedBrick = [];
30290         
30291         this.currentSize = this.el.getBox(true);
30292         
30293         Roo.EventManager.onWindowResize(this.resize, this); 
30294
30295         if(!this.isAutoInitial){
30296             this.layout();
30297             return;
30298         }
30299         
30300         this.layout();
30301         
30302         return;
30303         //this.layout.defer(500,this);
30304         
30305     },
30306     
30307     resize : function()
30308     {
30309         var cs = this.el.getBox(true);
30310         
30311         if (
30312                 this.currentSize.width == cs.width && 
30313                 this.currentSize.x == cs.x && 
30314                 this.currentSize.height == cs.height && 
30315                 this.currentSize.y == cs.y 
30316         ) {
30317             Roo.log("no change in with or X or Y");
30318             return;
30319         }
30320         
30321         this.currentSize = cs;
30322         
30323         this.layout();
30324         
30325     },
30326     
30327     layout : function()
30328     {   
30329         this._resetLayout();
30330         
30331         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30332         
30333         this.layoutItems( isInstant );
30334       
30335         this._isLayoutInited = true;
30336         
30337         this.fireEvent('layout', this);
30338         
30339     },
30340     
30341     _resetLayout : function()
30342     {
30343         if(this.isHorizontal){
30344             this.horizontalMeasureColumns();
30345             return;
30346         }
30347         
30348         this.verticalMeasureColumns();
30349         
30350     },
30351     
30352     verticalMeasureColumns : function()
30353     {
30354         this.getContainerWidth();
30355         
30356 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30357 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30358 //            return;
30359 //        }
30360         
30361         var boxWidth = this.boxWidth + this.padWidth;
30362         
30363         if(this.containerWidth < this.boxWidth){
30364             boxWidth = this.containerWidth
30365         }
30366         
30367         var containerWidth = this.containerWidth;
30368         
30369         var cols = Math.floor(containerWidth / boxWidth);
30370         
30371         this.cols = Math.max( cols, 1 );
30372         
30373         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30374         
30375         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30376         
30377         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30378         
30379         this.colWidth = boxWidth + avail - this.padWidth;
30380         
30381         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30382         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30383     },
30384     
30385     horizontalMeasureColumns : function()
30386     {
30387         this.getContainerWidth();
30388         
30389         var boxWidth = this.boxWidth;
30390         
30391         if(this.containerWidth < boxWidth){
30392             boxWidth = this.containerWidth;
30393         }
30394         
30395         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30396         
30397         this.el.setHeight(boxWidth);
30398         
30399     },
30400     
30401     getContainerWidth : function()
30402     {
30403         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30404     },
30405     
30406     layoutItems : function( isInstant )
30407     {
30408         Roo.log(this.bricks);
30409         
30410         var items = Roo.apply([], this.bricks);
30411         
30412         if(this.isHorizontal){
30413             this._horizontalLayoutItems( items , isInstant );
30414             return;
30415         }
30416         
30417 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30418 //            this._verticalAlternativeLayoutItems( items , isInstant );
30419 //            return;
30420 //        }
30421         
30422         this._verticalLayoutItems( items , isInstant );
30423         
30424     },
30425     
30426     _verticalLayoutItems : function ( items , isInstant)
30427     {
30428         if ( !items || !items.length ) {
30429             return;
30430         }
30431         
30432         var standard = [
30433             ['xs', 'xs', 'xs', 'tall'],
30434             ['xs', 'xs', 'tall'],
30435             ['xs', 'xs', 'sm'],
30436             ['xs', 'xs', 'xs'],
30437             ['xs', 'tall'],
30438             ['xs', 'sm'],
30439             ['xs', 'xs'],
30440             ['xs'],
30441             
30442             ['sm', 'xs', 'xs'],
30443             ['sm', 'xs'],
30444             ['sm'],
30445             
30446             ['tall', 'xs', 'xs', 'xs'],
30447             ['tall', 'xs', 'xs'],
30448             ['tall', 'xs'],
30449             ['tall']
30450             
30451         ];
30452         
30453         var queue = [];
30454         
30455         var boxes = [];
30456         
30457         var box = [];
30458         
30459         Roo.each(items, function(item, k){
30460             
30461             switch (item.size) {
30462                 // these layouts take up a full box,
30463                 case 'md' :
30464                 case 'md-left' :
30465                 case 'md-right' :
30466                 case 'wide' :
30467                     
30468                     if(box.length){
30469                         boxes.push(box);
30470                         box = [];
30471                     }
30472                     
30473                     boxes.push([item]);
30474                     
30475                     break;
30476                     
30477                 case 'xs' :
30478                 case 'sm' :
30479                 case 'tall' :
30480                     
30481                     box.push(item);
30482                     
30483                     break;
30484                 default :
30485                     break;
30486                     
30487             }
30488             
30489         }, this);
30490         
30491         if(box.length){
30492             boxes.push(box);
30493             box = [];
30494         }
30495         
30496         var filterPattern = function(box, length)
30497         {
30498             if(!box.length){
30499                 return;
30500             }
30501             
30502             var match = false;
30503             
30504             var pattern = box.slice(0, length);
30505             
30506             var format = [];
30507             
30508             Roo.each(pattern, function(i){
30509                 format.push(i.size);
30510             }, this);
30511             
30512             Roo.each(standard, function(s){
30513                 
30514                 if(String(s) != String(format)){
30515                     return;
30516                 }
30517                 
30518                 match = true;
30519                 return false;
30520                 
30521             }, this);
30522             
30523             if(!match && length == 1){
30524                 return;
30525             }
30526             
30527             if(!match){
30528                 filterPattern(box, length - 1);
30529                 return;
30530             }
30531                 
30532             queue.push(pattern);
30533
30534             box = box.slice(length, box.length);
30535
30536             filterPattern(box, 4);
30537
30538             return;
30539             
30540         }
30541         
30542         Roo.each(boxes, function(box, k){
30543             
30544             if(!box.length){
30545                 return;
30546             }
30547             
30548             if(box.length == 1){
30549                 queue.push(box);
30550                 return;
30551             }
30552             
30553             filterPattern(box, 4);
30554             
30555         }, this);
30556         
30557         this._processVerticalLayoutQueue( queue, isInstant );
30558         
30559     },
30560     
30561 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30562 //    {
30563 //        if ( !items || !items.length ) {
30564 //            return;
30565 //        }
30566 //
30567 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30568 //        
30569 //    },
30570     
30571     _horizontalLayoutItems : function ( items , isInstant)
30572     {
30573         if ( !items || !items.length || items.length < 3) {
30574             return;
30575         }
30576         
30577         items.reverse();
30578         
30579         var eItems = items.slice(0, 3);
30580         
30581         items = items.slice(3, items.length);
30582         
30583         var standard = [
30584             ['xs', 'xs', 'xs', 'wide'],
30585             ['xs', 'xs', 'wide'],
30586             ['xs', 'xs', 'sm'],
30587             ['xs', 'xs', 'xs'],
30588             ['xs', 'wide'],
30589             ['xs', 'sm'],
30590             ['xs', 'xs'],
30591             ['xs'],
30592             
30593             ['sm', 'xs', 'xs'],
30594             ['sm', 'xs'],
30595             ['sm'],
30596             
30597             ['wide', 'xs', 'xs', 'xs'],
30598             ['wide', 'xs', 'xs'],
30599             ['wide', 'xs'],
30600             ['wide'],
30601             
30602             ['wide-thin']
30603         ];
30604         
30605         var queue = [];
30606         
30607         var boxes = [];
30608         
30609         var box = [];
30610         
30611         Roo.each(items, function(item, k){
30612             
30613             switch (item.size) {
30614                 case 'md' :
30615                 case 'md-left' :
30616                 case 'md-right' :
30617                 case 'tall' :
30618                     
30619                     if(box.length){
30620                         boxes.push(box);
30621                         box = [];
30622                     }
30623                     
30624                     boxes.push([item]);
30625                     
30626                     break;
30627                     
30628                 case 'xs' :
30629                 case 'sm' :
30630                 case 'wide' :
30631                 case 'wide-thin' :
30632                     
30633                     box.push(item);
30634                     
30635                     break;
30636                 default :
30637                     break;
30638                     
30639             }
30640             
30641         }, this);
30642         
30643         if(box.length){
30644             boxes.push(box);
30645             box = [];
30646         }
30647         
30648         var filterPattern = function(box, length)
30649         {
30650             if(!box.length){
30651                 return;
30652             }
30653             
30654             var match = false;
30655             
30656             var pattern = box.slice(0, length);
30657             
30658             var format = [];
30659             
30660             Roo.each(pattern, function(i){
30661                 format.push(i.size);
30662             }, this);
30663             
30664             Roo.each(standard, function(s){
30665                 
30666                 if(String(s) != String(format)){
30667                     return;
30668                 }
30669                 
30670                 match = true;
30671                 return false;
30672                 
30673             }, this);
30674             
30675             if(!match && length == 1){
30676                 return;
30677             }
30678             
30679             if(!match){
30680                 filterPattern(box, length - 1);
30681                 return;
30682             }
30683                 
30684             queue.push(pattern);
30685
30686             box = box.slice(length, box.length);
30687
30688             filterPattern(box, 4);
30689
30690             return;
30691             
30692         }
30693         
30694         Roo.each(boxes, function(box, k){
30695             
30696             if(!box.length){
30697                 return;
30698             }
30699             
30700             if(box.length == 1){
30701                 queue.push(box);
30702                 return;
30703             }
30704             
30705             filterPattern(box, 4);
30706             
30707         }, this);
30708         
30709         
30710         var prune = [];
30711         
30712         var pos = this.el.getBox(true);
30713         
30714         var minX = pos.x;
30715         
30716         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30717         
30718         var hit_end = false;
30719         
30720         Roo.each(queue, function(box){
30721             
30722             if(hit_end){
30723                 
30724                 Roo.each(box, function(b){
30725                 
30726                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30727                     b.el.hide();
30728
30729                 }, this);
30730
30731                 return;
30732             }
30733             
30734             var mx = 0;
30735             
30736             Roo.each(box, function(b){
30737                 
30738                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30739                 b.el.show();
30740
30741                 mx = Math.max(mx, b.x);
30742                 
30743             }, this);
30744             
30745             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30746             
30747             if(maxX < minX){
30748                 
30749                 Roo.each(box, function(b){
30750                 
30751                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30752                     b.el.hide();
30753                     
30754                 }, this);
30755                 
30756                 hit_end = true;
30757                 
30758                 return;
30759             }
30760             
30761             prune.push(box);
30762             
30763         }, this);
30764         
30765         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30766     },
30767     
30768     /** Sets position of item in DOM
30769     * @param {Element} item
30770     * @param {Number} x - horizontal position
30771     * @param {Number} y - vertical position
30772     * @param {Boolean} isInstant - disables transitions
30773     */
30774     _processVerticalLayoutQueue : function( queue, isInstant )
30775     {
30776         var pos = this.el.getBox(true);
30777         var x = pos.x;
30778         var y = pos.y;
30779         var maxY = [];
30780         
30781         for (var i = 0; i < this.cols; i++){
30782             maxY[i] = pos.y;
30783         }
30784         
30785         Roo.each(queue, function(box, k){
30786             
30787             var col = k % this.cols;
30788             
30789             Roo.each(box, function(b,kk){
30790                 
30791                 b.el.position('absolute');
30792                 
30793                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30794                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30795                 
30796                 if(b.size == 'md-left' || b.size == 'md-right'){
30797                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30798                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30799                 }
30800                 
30801                 b.el.setWidth(width);
30802                 b.el.setHeight(height);
30803                 // iframe?
30804                 b.el.select('iframe',true).setSize(width,height);
30805                 
30806             }, this);
30807             
30808             for (var i = 0; i < this.cols; i++){
30809                 
30810                 if(maxY[i] < maxY[col]){
30811                     col = i;
30812                     continue;
30813                 }
30814                 
30815                 col = Math.min(col, i);
30816                 
30817             }
30818             
30819             x = pos.x + col * (this.colWidth + this.padWidth);
30820             
30821             y = maxY[col];
30822             
30823             var positions = [];
30824             
30825             switch (box.length){
30826                 case 1 :
30827                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30828                     break;
30829                 case 2 :
30830                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30831                     break;
30832                 case 3 :
30833                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30834                     break;
30835                 case 4 :
30836                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30837                     break;
30838                 default :
30839                     break;
30840             }
30841             
30842             Roo.each(box, function(b,kk){
30843                 
30844                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30845                 
30846                 var sz = b.el.getSize();
30847                 
30848                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30849                 
30850             }, this);
30851             
30852         }, this);
30853         
30854         var mY = 0;
30855         
30856         for (var i = 0; i < this.cols; i++){
30857             mY = Math.max(mY, maxY[i]);
30858         }
30859         
30860         this.el.setHeight(mY - pos.y);
30861         
30862     },
30863     
30864 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30865 //    {
30866 //        var pos = this.el.getBox(true);
30867 //        var x = pos.x;
30868 //        var y = pos.y;
30869 //        var maxX = pos.right;
30870 //        
30871 //        var maxHeight = 0;
30872 //        
30873 //        Roo.each(items, function(item, k){
30874 //            
30875 //            var c = k % 2;
30876 //            
30877 //            item.el.position('absolute');
30878 //                
30879 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30880 //
30881 //            item.el.setWidth(width);
30882 //
30883 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30884 //
30885 //            item.el.setHeight(height);
30886 //            
30887 //            if(c == 0){
30888 //                item.el.setXY([x, y], isInstant ? false : true);
30889 //            } else {
30890 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30891 //            }
30892 //            
30893 //            y = y + height + this.alternativePadWidth;
30894 //            
30895 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30896 //            
30897 //        }, this);
30898 //        
30899 //        this.el.setHeight(maxHeight);
30900 //        
30901 //    },
30902     
30903     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30904     {
30905         var pos = this.el.getBox(true);
30906         
30907         var minX = pos.x;
30908         var minY = pos.y;
30909         
30910         var maxX = pos.right;
30911         
30912         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30913         
30914         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30915         
30916         Roo.each(queue, function(box, k){
30917             
30918             Roo.each(box, function(b, kk){
30919                 
30920                 b.el.position('absolute');
30921                 
30922                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30923                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30924                 
30925                 if(b.size == 'md-left' || b.size == 'md-right'){
30926                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30927                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30928                 }
30929                 
30930                 b.el.setWidth(width);
30931                 b.el.setHeight(height);
30932                 
30933             }, this);
30934             
30935             if(!box.length){
30936                 return;
30937             }
30938             
30939             var positions = [];
30940             
30941             switch (box.length){
30942                 case 1 :
30943                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30944                     break;
30945                 case 2 :
30946                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30947                     break;
30948                 case 3 :
30949                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30950                     break;
30951                 case 4 :
30952                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30953                     break;
30954                 default :
30955                     break;
30956             }
30957             
30958             Roo.each(box, function(b,kk){
30959                 
30960                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30961                 
30962                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30963                 
30964             }, this);
30965             
30966         }, this);
30967         
30968     },
30969     
30970     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30971     {
30972         Roo.each(eItems, function(b,k){
30973             
30974             b.size = (k == 0) ? 'sm' : 'xs';
30975             b.x = (k == 0) ? 2 : 1;
30976             b.y = (k == 0) ? 2 : 1;
30977             
30978             b.el.position('absolute');
30979             
30980             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30981                 
30982             b.el.setWidth(width);
30983             
30984             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30985             
30986             b.el.setHeight(height);
30987             
30988         }, this);
30989
30990         var positions = [];
30991         
30992         positions.push({
30993             x : maxX - this.unitWidth * 2 - this.gutter,
30994             y : minY
30995         });
30996         
30997         positions.push({
30998             x : maxX - this.unitWidth,
30999             y : minY + (this.unitWidth + this.gutter) * 2
31000         });
31001         
31002         positions.push({
31003             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31004             y : minY
31005         });
31006         
31007         Roo.each(eItems, function(b,k){
31008             
31009             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31010
31011         }, this);
31012         
31013     },
31014     
31015     getVerticalOneBoxColPositions : function(x, y, box)
31016     {
31017         var pos = [];
31018         
31019         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31020         
31021         if(box[0].size == 'md-left'){
31022             rand = 0;
31023         }
31024         
31025         if(box[0].size == 'md-right'){
31026             rand = 1;
31027         }
31028         
31029         pos.push({
31030             x : x + (this.unitWidth + this.gutter) * rand,
31031             y : y
31032         });
31033         
31034         return pos;
31035     },
31036     
31037     getVerticalTwoBoxColPositions : function(x, y, box)
31038     {
31039         var pos = [];
31040         
31041         if(box[0].size == 'xs'){
31042             
31043             pos.push({
31044                 x : x,
31045                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31046             });
31047
31048             pos.push({
31049                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31050                 y : y
31051             });
31052             
31053             return pos;
31054             
31055         }
31056         
31057         pos.push({
31058             x : x,
31059             y : y
31060         });
31061
31062         pos.push({
31063             x : x + (this.unitWidth + this.gutter) * 2,
31064             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31065         });
31066         
31067         return pos;
31068         
31069     },
31070     
31071     getVerticalThreeBoxColPositions : function(x, y, box)
31072     {
31073         var pos = [];
31074         
31075         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31076             
31077             pos.push({
31078                 x : x,
31079                 y : y
31080             });
31081
31082             pos.push({
31083                 x : x + (this.unitWidth + this.gutter) * 1,
31084                 y : y
31085             });
31086             
31087             pos.push({
31088                 x : x + (this.unitWidth + this.gutter) * 2,
31089                 y : y
31090             });
31091             
31092             return pos;
31093             
31094         }
31095         
31096         if(box[0].size == 'xs' && box[1].size == 'xs'){
31097             
31098             pos.push({
31099                 x : x,
31100                 y : y
31101             });
31102
31103             pos.push({
31104                 x : x,
31105                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31106             });
31107             
31108             pos.push({
31109                 x : x + (this.unitWidth + this.gutter) * 1,
31110                 y : y
31111             });
31112             
31113             return pos;
31114             
31115         }
31116         
31117         pos.push({
31118             x : x,
31119             y : y
31120         });
31121
31122         pos.push({
31123             x : x + (this.unitWidth + this.gutter) * 2,
31124             y : y
31125         });
31126
31127         pos.push({
31128             x : x + (this.unitWidth + this.gutter) * 2,
31129             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31130         });
31131             
31132         return pos;
31133         
31134     },
31135     
31136     getVerticalFourBoxColPositions : function(x, y, box)
31137     {
31138         var pos = [];
31139         
31140         if(box[0].size == 'xs'){
31141             
31142             pos.push({
31143                 x : x,
31144                 y : y
31145             });
31146
31147             pos.push({
31148                 x : x,
31149                 y : y + (this.unitHeight + this.gutter) * 1
31150             });
31151             
31152             pos.push({
31153                 x : x,
31154                 y : y + (this.unitHeight + this.gutter) * 2
31155             });
31156             
31157             pos.push({
31158                 x : x + (this.unitWidth + this.gutter) * 1,
31159                 y : y
31160             });
31161             
31162             return pos;
31163             
31164         }
31165         
31166         pos.push({
31167             x : x,
31168             y : y
31169         });
31170
31171         pos.push({
31172             x : x + (this.unitWidth + this.gutter) * 2,
31173             y : y
31174         });
31175
31176         pos.push({
31177             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31178             y : y + (this.unitHeight + this.gutter) * 1
31179         });
31180
31181         pos.push({
31182             x : x + (this.unitWidth + this.gutter) * 2,
31183             y : y + (this.unitWidth + this.gutter) * 2
31184         });
31185
31186         return pos;
31187         
31188     },
31189     
31190     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31191     {
31192         var pos = [];
31193         
31194         if(box[0].size == 'md-left'){
31195             pos.push({
31196                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31197                 y : minY
31198             });
31199             
31200             return pos;
31201         }
31202         
31203         if(box[0].size == 'md-right'){
31204             pos.push({
31205                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31206                 y : minY + (this.unitWidth + this.gutter) * 1
31207             });
31208             
31209             return pos;
31210         }
31211         
31212         var rand = Math.floor(Math.random() * (4 - box[0].y));
31213         
31214         pos.push({
31215             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31216             y : minY + (this.unitWidth + this.gutter) * rand
31217         });
31218         
31219         return pos;
31220         
31221     },
31222     
31223     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31224     {
31225         var pos = [];
31226         
31227         if(box[0].size == 'xs'){
31228             
31229             pos.push({
31230                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31231                 y : minY
31232             });
31233
31234             pos.push({
31235                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31236                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31237             });
31238             
31239             return pos;
31240             
31241         }
31242         
31243         pos.push({
31244             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31245             y : minY
31246         });
31247
31248         pos.push({
31249             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31250             y : minY + (this.unitWidth + this.gutter) * 2
31251         });
31252         
31253         return pos;
31254         
31255     },
31256     
31257     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31258     {
31259         var pos = [];
31260         
31261         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31262             
31263             pos.push({
31264                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31265                 y : minY
31266             });
31267
31268             pos.push({
31269                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31270                 y : minY + (this.unitWidth + this.gutter) * 1
31271             });
31272             
31273             pos.push({
31274                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31275                 y : minY + (this.unitWidth + this.gutter) * 2
31276             });
31277             
31278             return pos;
31279             
31280         }
31281         
31282         if(box[0].size == 'xs' && box[1].size == 'xs'){
31283             
31284             pos.push({
31285                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31286                 y : minY
31287             });
31288
31289             pos.push({
31290                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31291                 y : minY
31292             });
31293             
31294             pos.push({
31295                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31296                 y : minY + (this.unitWidth + this.gutter) * 1
31297             });
31298             
31299             return pos;
31300             
31301         }
31302         
31303         pos.push({
31304             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31305             y : minY
31306         });
31307
31308         pos.push({
31309             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31310             y : minY + (this.unitWidth + this.gutter) * 2
31311         });
31312
31313         pos.push({
31314             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31315             y : minY + (this.unitWidth + this.gutter) * 2
31316         });
31317             
31318         return pos;
31319         
31320     },
31321     
31322     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31323     {
31324         var pos = [];
31325         
31326         if(box[0].size == 'xs'){
31327             
31328             pos.push({
31329                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31330                 y : minY
31331             });
31332
31333             pos.push({
31334                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31335                 y : minY
31336             });
31337             
31338             pos.push({
31339                 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),
31340                 y : minY
31341             });
31342             
31343             pos.push({
31344                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31345                 y : minY + (this.unitWidth + this.gutter) * 1
31346             });
31347             
31348             return pos;
31349             
31350         }
31351         
31352         pos.push({
31353             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31354             y : minY
31355         });
31356         
31357         pos.push({
31358             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31359             y : minY + (this.unitWidth + this.gutter) * 2
31360         });
31361         
31362         pos.push({
31363             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31364             y : minY + (this.unitWidth + this.gutter) * 2
31365         });
31366         
31367         pos.push({
31368             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),
31369             y : minY + (this.unitWidth + this.gutter) * 2
31370         });
31371
31372         return pos;
31373         
31374     },
31375     
31376     /**
31377     * remove a Masonry Brick
31378     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31379     */
31380     removeBrick : function(brick_id)
31381     {
31382         if (!brick_id) {
31383             return;
31384         }
31385         
31386         for (var i = 0; i<this.bricks.length; i++) {
31387             if (this.bricks[i].id == brick_id) {
31388                 this.bricks.splice(i,1);
31389                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31390                 this.initial();
31391             }
31392         }
31393     },
31394     
31395     /**
31396     * adds a Masonry Brick
31397     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31398     */
31399     addBrick : function(cfg)
31400     {
31401         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31402         //this.register(cn);
31403         cn.parentId = this.id;
31404         cn.onRender(this.el, null);
31405         return cn;
31406     },
31407     
31408     /**
31409     * register a Masonry Brick
31410     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31411     */
31412     
31413     register : function(brick)
31414     {
31415         this.bricks.push(brick);
31416         brick.masonryId = this.id;
31417     },
31418     
31419     /**
31420     * clear all the Masonry Brick
31421     */
31422     clearAll : function()
31423     {
31424         this.bricks = [];
31425         //this.getChildContainer().dom.innerHTML = "";
31426         this.el.dom.innerHTML = '';
31427     },
31428     
31429     getSelected : function()
31430     {
31431         if (!this.selectedBrick) {
31432             return false;
31433         }
31434         
31435         return this.selectedBrick;
31436     }
31437 });
31438
31439 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31440     
31441     groups: {},
31442      /**
31443     * register a Masonry Layout
31444     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31445     */
31446     
31447     register : function(layout)
31448     {
31449         this.groups[layout.id] = layout;
31450     },
31451     /**
31452     * fetch a  Masonry Layout based on the masonry layout ID
31453     * @param {string} the masonry layout to add
31454     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31455     */
31456     
31457     get: function(layout_id) {
31458         if (typeof(this.groups[layout_id]) == 'undefined') {
31459             return false;
31460         }
31461         return this.groups[layout_id] ;
31462     }
31463     
31464     
31465     
31466 });
31467
31468  
31469
31470  /**
31471  *
31472  * This is based on 
31473  * http://masonry.desandro.com
31474  *
31475  * The idea is to render all the bricks based on vertical width...
31476  *
31477  * The original code extends 'outlayer' - we might need to use that....
31478  * 
31479  */
31480
31481
31482 /**
31483  * @class Roo.bootstrap.LayoutMasonryAuto
31484  * @extends Roo.bootstrap.Component
31485  * Bootstrap Layout Masonry class
31486  * 
31487  * @constructor
31488  * Create a new Element
31489  * @param {Object} config The config object
31490  */
31491
31492 Roo.bootstrap.LayoutMasonryAuto = function(config){
31493     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31494 };
31495
31496 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31497     
31498       /**
31499      * @cfg {Boolean} isFitWidth  - resize the width..
31500      */   
31501     isFitWidth : false,  // options..
31502     /**
31503      * @cfg {Boolean} isOriginLeft = left align?
31504      */   
31505     isOriginLeft : true,
31506     /**
31507      * @cfg {Boolean} isOriginTop = top align?
31508      */   
31509     isOriginTop : false,
31510     /**
31511      * @cfg {Boolean} isLayoutInstant = no animation?
31512      */   
31513     isLayoutInstant : false, // needed?
31514     /**
31515      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31516      */   
31517     isResizingContainer : true,
31518     /**
31519      * @cfg {Number} columnWidth  width of the columns 
31520      */   
31521     
31522     columnWidth : 0,
31523     
31524     /**
31525      * @cfg {Number} maxCols maximum number of columns
31526      */   
31527     
31528     maxCols: 0,
31529     /**
31530      * @cfg {Number} padHeight padding below box..
31531      */   
31532     
31533     padHeight : 10, 
31534     
31535     /**
31536      * @cfg {Boolean} isAutoInitial defalut true
31537      */   
31538     
31539     isAutoInitial : true, 
31540     
31541     // private?
31542     gutter : 0,
31543     
31544     containerWidth: 0,
31545     initialColumnWidth : 0,
31546     currentSize : null,
31547     
31548     colYs : null, // array.
31549     maxY : 0,
31550     padWidth: 10,
31551     
31552     
31553     tag: 'div',
31554     cls: '',
31555     bricks: null, //CompositeElement
31556     cols : 0, // array?
31557     // element : null, // wrapped now this.el
31558     _isLayoutInited : null, 
31559     
31560     
31561     getAutoCreate : function(){
31562         
31563         var cfg = {
31564             tag: this.tag,
31565             cls: 'blog-masonary-wrapper ' + this.cls,
31566             cn : {
31567                 cls : 'mas-boxes masonary'
31568             }
31569         };
31570         
31571         return cfg;
31572     },
31573     
31574     getChildContainer: function( )
31575     {
31576         if (this.boxesEl) {
31577             return this.boxesEl;
31578         }
31579         
31580         this.boxesEl = this.el.select('.mas-boxes').first();
31581         
31582         return this.boxesEl;
31583     },
31584     
31585     
31586     initEvents : function()
31587     {
31588         var _this = this;
31589         
31590         if(this.isAutoInitial){
31591             Roo.log('hook children rendered');
31592             this.on('childrenrendered', function() {
31593                 Roo.log('children rendered');
31594                 _this.initial();
31595             } ,this);
31596         }
31597         
31598     },
31599     
31600     initial : function()
31601     {
31602         this.reloadItems();
31603
31604         this.currentSize = this.el.getBox(true);
31605
31606         /// was window resize... - let's see if this works..
31607         Roo.EventManager.onWindowResize(this.resize, this); 
31608
31609         if(!this.isAutoInitial){
31610             this.layout();
31611             return;
31612         }
31613         
31614         this.layout.defer(500,this);
31615     },
31616     
31617     reloadItems: function()
31618     {
31619         this.bricks = this.el.select('.masonry-brick', true);
31620         
31621         this.bricks.each(function(b) {
31622             //Roo.log(b.getSize());
31623             if (!b.attr('originalwidth')) {
31624                 b.attr('originalwidth',  b.getSize().width);
31625             }
31626             
31627         });
31628         
31629         Roo.log(this.bricks.elements.length);
31630     },
31631     
31632     resize : function()
31633     {
31634         Roo.log('resize');
31635         var cs = this.el.getBox(true);
31636         
31637         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31638             Roo.log("no change in with or X");
31639             return;
31640         }
31641         this.currentSize = cs;
31642         this.layout();
31643     },
31644     
31645     layout : function()
31646     {
31647          Roo.log('layout');
31648         this._resetLayout();
31649         //this._manageStamps();
31650       
31651         // don't animate first layout
31652         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31653         this.layoutItems( isInstant );
31654       
31655         // flag for initalized
31656         this._isLayoutInited = true;
31657     },
31658     
31659     layoutItems : function( isInstant )
31660     {
31661         //var items = this._getItemsForLayout( this.items );
31662         // original code supports filtering layout items.. we just ignore it..
31663         
31664         this._layoutItems( this.bricks , isInstant );
31665       
31666         this._postLayout();
31667     },
31668     _layoutItems : function ( items , isInstant)
31669     {
31670        //this.fireEvent( 'layout', this, items );
31671     
31672
31673         if ( !items || !items.elements.length ) {
31674           // no items, emit event with empty array
31675             return;
31676         }
31677
31678         var queue = [];
31679         items.each(function(item) {
31680             Roo.log("layout item");
31681             Roo.log(item);
31682             // get x/y object from method
31683             var position = this._getItemLayoutPosition( item );
31684             // enqueue
31685             position.item = item;
31686             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31687             queue.push( position );
31688         }, this);
31689       
31690         this._processLayoutQueue( queue );
31691     },
31692     /** Sets position of item in DOM
31693     * @param {Element} item
31694     * @param {Number} x - horizontal position
31695     * @param {Number} y - vertical position
31696     * @param {Boolean} isInstant - disables transitions
31697     */
31698     _processLayoutQueue : function( queue )
31699     {
31700         for ( var i=0, len = queue.length; i < len; i++ ) {
31701             var obj = queue[i];
31702             obj.item.position('absolute');
31703             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31704         }
31705     },
31706       
31707     
31708     /**
31709     * Any logic you want to do after each layout,
31710     * i.e. size the container
31711     */
31712     _postLayout : function()
31713     {
31714         this.resizeContainer();
31715     },
31716     
31717     resizeContainer : function()
31718     {
31719         if ( !this.isResizingContainer ) {
31720             return;
31721         }
31722         var size = this._getContainerSize();
31723         if ( size ) {
31724             this.el.setSize(size.width,size.height);
31725             this.boxesEl.setSize(size.width,size.height);
31726         }
31727     },
31728     
31729     
31730     
31731     _resetLayout : function()
31732     {
31733         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31734         this.colWidth = this.el.getWidth();
31735         //this.gutter = this.el.getWidth(); 
31736         
31737         this.measureColumns();
31738
31739         // reset column Y
31740         var i = this.cols;
31741         this.colYs = [];
31742         while (i--) {
31743             this.colYs.push( 0 );
31744         }
31745     
31746         this.maxY = 0;
31747     },
31748
31749     measureColumns : function()
31750     {
31751         this.getContainerWidth();
31752       // if columnWidth is 0, default to outerWidth of first item
31753         if ( !this.columnWidth ) {
31754             var firstItem = this.bricks.first();
31755             Roo.log(firstItem);
31756             this.columnWidth  = this.containerWidth;
31757             if (firstItem && firstItem.attr('originalwidth') ) {
31758                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31759             }
31760             // columnWidth fall back to item of first element
31761             Roo.log("set column width?");
31762                         this.initialColumnWidth = this.columnWidth  ;
31763
31764             // if first elem has no width, default to size of container
31765             
31766         }
31767         
31768         
31769         if (this.initialColumnWidth) {
31770             this.columnWidth = this.initialColumnWidth;
31771         }
31772         
31773         
31774             
31775         // column width is fixed at the top - however if container width get's smaller we should
31776         // reduce it...
31777         
31778         // this bit calcs how man columns..
31779             
31780         var columnWidth = this.columnWidth += this.gutter;
31781       
31782         // calculate columns
31783         var containerWidth = this.containerWidth + this.gutter;
31784         
31785         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31786         // fix rounding errors, typically with gutters
31787         var excess = columnWidth - containerWidth % columnWidth;
31788         
31789         
31790         // if overshoot is less than a pixel, round up, otherwise floor it
31791         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31792         cols = Math[ mathMethod ]( cols );
31793         this.cols = Math.max( cols, 1 );
31794         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31795         
31796          // padding positioning..
31797         var totalColWidth = this.cols * this.columnWidth;
31798         var padavail = this.containerWidth - totalColWidth;
31799         // so for 2 columns - we need 3 'pads'
31800         
31801         var padNeeded = (1+this.cols) * this.padWidth;
31802         
31803         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31804         
31805         this.columnWidth += padExtra
31806         //this.padWidth = Math.floor(padavail /  ( this.cols));
31807         
31808         // adjust colum width so that padding is fixed??
31809         
31810         // we have 3 columns ... total = width * 3
31811         // we have X left over... that should be used by 
31812         
31813         //if (this.expandC) {
31814             
31815         //}
31816         
31817         
31818         
31819     },
31820     
31821     getContainerWidth : function()
31822     {
31823        /* // container is parent if fit width
31824         var container = this.isFitWidth ? this.element.parentNode : this.element;
31825         // check that this.size and size are there
31826         // IE8 triggers resize on body size change, so they might not be
31827         
31828         var size = getSize( container );  //FIXME
31829         this.containerWidth = size && size.innerWidth; //FIXME
31830         */
31831          
31832         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31833         
31834     },
31835     
31836     _getItemLayoutPosition : function( item )  // what is item?
31837     {
31838         // we resize the item to our columnWidth..
31839       
31840         item.setWidth(this.columnWidth);
31841         item.autoBoxAdjust  = false;
31842         
31843         var sz = item.getSize();
31844  
31845         // how many columns does this brick span
31846         var remainder = this.containerWidth % this.columnWidth;
31847         
31848         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31849         // round if off by 1 pixel, otherwise use ceil
31850         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31851         colSpan = Math.min( colSpan, this.cols );
31852         
31853         // normally this should be '1' as we dont' currently allow multi width columns..
31854         
31855         var colGroup = this._getColGroup( colSpan );
31856         // get the minimum Y value from the columns
31857         var minimumY = Math.min.apply( Math, colGroup );
31858         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31859         
31860         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31861          
31862         // position the brick
31863         var position = {
31864             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31865             y: this.currentSize.y + minimumY + this.padHeight
31866         };
31867         
31868         Roo.log(position);
31869         // apply setHeight to necessary columns
31870         var setHeight = minimumY + sz.height + this.padHeight;
31871         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31872         
31873         var setSpan = this.cols + 1 - colGroup.length;
31874         for ( var i = 0; i < setSpan; i++ ) {
31875           this.colYs[ shortColIndex + i ] = setHeight ;
31876         }
31877       
31878         return position;
31879     },
31880     
31881     /**
31882      * @param {Number} colSpan - number of columns the element spans
31883      * @returns {Array} colGroup
31884      */
31885     _getColGroup : function( colSpan )
31886     {
31887         if ( colSpan < 2 ) {
31888           // if brick spans only one column, use all the column Ys
31889           return this.colYs;
31890         }
31891       
31892         var colGroup = [];
31893         // how many different places could this brick fit horizontally
31894         var groupCount = this.cols + 1 - colSpan;
31895         // for each group potential horizontal position
31896         for ( var i = 0; i < groupCount; i++ ) {
31897           // make an array of colY values for that one group
31898           var groupColYs = this.colYs.slice( i, i + colSpan );
31899           // and get the max value of the array
31900           colGroup[i] = Math.max.apply( Math, groupColYs );
31901         }
31902         return colGroup;
31903     },
31904     /*
31905     _manageStamp : function( stamp )
31906     {
31907         var stampSize =  stamp.getSize();
31908         var offset = stamp.getBox();
31909         // get the columns that this stamp affects
31910         var firstX = this.isOriginLeft ? offset.x : offset.right;
31911         var lastX = firstX + stampSize.width;
31912         var firstCol = Math.floor( firstX / this.columnWidth );
31913         firstCol = Math.max( 0, firstCol );
31914         
31915         var lastCol = Math.floor( lastX / this.columnWidth );
31916         // lastCol should not go over if multiple of columnWidth #425
31917         lastCol -= lastX % this.columnWidth ? 0 : 1;
31918         lastCol = Math.min( this.cols - 1, lastCol );
31919         
31920         // set colYs to bottom of the stamp
31921         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31922             stampSize.height;
31923             
31924         for ( var i = firstCol; i <= lastCol; i++ ) {
31925           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31926         }
31927     },
31928     */
31929     
31930     _getContainerSize : function()
31931     {
31932         this.maxY = Math.max.apply( Math, this.colYs );
31933         var size = {
31934             height: this.maxY
31935         };
31936       
31937         if ( this.isFitWidth ) {
31938             size.width = this._getContainerFitWidth();
31939         }
31940       
31941         return size;
31942     },
31943     
31944     _getContainerFitWidth : function()
31945     {
31946         var unusedCols = 0;
31947         // count unused columns
31948         var i = this.cols;
31949         while ( --i ) {
31950           if ( this.colYs[i] !== 0 ) {
31951             break;
31952           }
31953           unusedCols++;
31954         }
31955         // fit container to columns that have been used
31956         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31957     },
31958     
31959     needsResizeLayout : function()
31960     {
31961         var previousWidth = this.containerWidth;
31962         this.getContainerWidth();
31963         return previousWidth !== this.containerWidth;
31964     }
31965  
31966 });
31967
31968  
31969
31970  /*
31971  * - LGPL
31972  *
31973  * element
31974  * 
31975  */
31976
31977 /**
31978  * @class Roo.bootstrap.MasonryBrick
31979  * @extends Roo.bootstrap.Component
31980  * Bootstrap MasonryBrick class
31981  * 
31982  * @constructor
31983  * Create a new MasonryBrick
31984  * @param {Object} config The config object
31985  */
31986
31987 Roo.bootstrap.MasonryBrick = function(config){
31988     
31989     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31990     
31991     Roo.bootstrap.MasonryBrick.register(this);
31992     
31993     this.addEvents({
31994         // raw events
31995         /**
31996          * @event click
31997          * When a MasonryBrick is clcik
31998          * @param {Roo.bootstrap.MasonryBrick} this
31999          * @param {Roo.EventObject} e
32000          */
32001         "click" : true
32002     });
32003 };
32004
32005 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32006     
32007     /**
32008      * @cfg {String} title
32009      */   
32010     title : '',
32011     /**
32012      * @cfg {String} html
32013      */   
32014     html : '',
32015     /**
32016      * @cfg {String} bgimage
32017      */   
32018     bgimage : '',
32019     /**
32020      * @cfg {String} videourl
32021      */   
32022     videourl : '',
32023     /**
32024      * @cfg {String} cls
32025      */   
32026     cls : '',
32027     /**
32028      * @cfg {String} href
32029      */   
32030     href : '',
32031     /**
32032      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32033      */   
32034     size : 'xs',
32035     
32036     /**
32037      * @cfg {String} placetitle (center|bottom)
32038      */   
32039     placetitle : '',
32040     
32041     /**
32042      * @cfg {Boolean} isFitContainer defalut true
32043      */   
32044     isFitContainer : true, 
32045     
32046     /**
32047      * @cfg {Boolean} preventDefault defalut false
32048      */   
32049     preventDefault : false, 
32050     
32051     /**
32052      * @cfg {Boolean} inverse defalut false
32053      */   
32054     maskInverse : false, 
32055     
32056     getAutoCreate : function()
32057     {
32058         if(!this.isFitContainer){
32059             return this.getSplitAutoCreate();
32060         }
32061         
32062         var cls = 'masonry-brick masonry-brick-full';
32063         
32064         if(this.href.length){
32065             cls += ' masonry-brick-link';
32066         }
32067         
32068         if(this.bgimage.length){
32069             cls += ' masonry-brick-image';
32070         }
32071         
32072         if(this.maskInverse){
32073             cls += ' mask-inverse';
32074         }
32075         
32076         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32077             cls += ' enable-mask';
32078         }
32079         
32080         if(this.size){
32081             cls += ' masonry-' + this.size + '-brick';
32082         }
32083         
32084         if(this.placetitle.length){
32085             
32086             switch (this.placetitle) {
32087                 case 'center' :
32088                     cls += ' masonry-center-title';
32089                     break;
32090                 case 'bottom' :
32091                     cls += ' masonry-bottom-title';
32092                     break;
32093                 default:
32094                     break;
32095             }
32096             
32097         } else {
32098             if(!this.html.length && !this.bgimage.length){
32099                 cls += ' masonry-center-title';
32100             }
32101
32102             if(!this.html.length && this.bgimage.length){
32103                 cls += ' masonry-bottom-title';
32104             }
32105         }
32106         
32107         if(this.cls){
32108             cls += ' ' + this.cls;
32109         }
32110         
32111         var cfg = {
32112             tag: (this.href.length) ? 'a' : 'div',
32113             cls: cls,
32114             cn: [
32115                 {
32116                     tag: 'div',
32117                     cls: 'masonry-brick-mask'
32118                 },
32119                 {
32120                     tag: 'div',
32121                     cls: 'masonry-brick-paragraph',
32122                     cn: []
32123                 }
32124             ]
32125         };
32126         
32127         if(this.href.length){
32128             cfg.href = this.href;
32129         }
32130         
32131         var cn = cfg.cn[1].cn;
32132         
32133         if(this.title.length){
32134             cn.push({
32135                 tag: 'h4',
32136                 cls: 'masonry-brick-title',
32137                 html: this.title
32138             });
32139         }
32140         
32141         if(this.html.length){
32142             cn.push({
32143                 tag: 'p',
32144                 cls: 'masonry-brick-text',
32145                 html: this.html
32146             });
32147         }
32148         
32149         if (!this.title.length && !this.html.length) {
32150             cfg.cn[1].cls += ' hide';
32151         }
32152         
32153         if(this.bgimage.length){
32154             cfg.cn.push({
32155                 tag: 'img',
32156                 cls: 'masonry-brick-image-view',
32157                 src: this.bgimage
32158             });
32159         }
32160         
32161         if(this.videourl.length){
32162             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32163             // youtube support only?
32164             cfg.cn.push({
32165                 tag: 'iframe',
32166                 cls: 'masonry-brick-image-view',
32167                 src: vurl,
32168                 frameborder : 0,
32169                 allowfullscreen : true
32170             });
32171         }
32172         
32173         return cfg;
32174         
32175     },
32176     
32177     getSplitAutoCreate : function()
32178     {
32179         var cls = 'masonry-brick masonry-brick-split';
32180         
32181         if(this.href.length){
32182             cls += ' masonry-brick-link';
32183         }
32184         
32185         if(this.bgimage.length){
32186             cls += ' masonry-brick-image';
32187         }
32188         
32189         if(this.size){
32190             cls += ' masonry-' + this.size + '-brick';
32191         }
32192         
32193         switch (this.placetitle) {
32194             case 'center' :
32195                 cls += ' masonry-center-title';
32196                 break;
32197             case 'bottom' :
32198                 cls += ' masonry-bottom-title';
32199                 break;
32200             default:
32201                 if(!this.bgimage.length){
32202                     cls += ' masonry-center-title';
32203                 }
32204
32205                 if(this.bgimage.length){
32206                     cls += ' masonry-bottom-title';
32207                 }
32208                 break;
32209         }
32210         
32211         if(this.cls){
32212             cls += ' ' + this.cls;
32213         }
32214         
32215         var cfg = {
32216             tag: (this.href.length) ? 'a' : 'div',
32217             cls: cls,
32218             cn: [
32219                 {
32220                     tag: 'div',
32221                     cls: 'masonry-brick-split-head',
32222                     cn: [
32223                         {
32224                             tag: 'div',
32225                             cls: 'masonry-brick-paragraph',
32226                             cn: []
32227                         }
32228                     ]
32229                 },
32230                 {
32231                     tag: 'div',
32232                     cls: 'masonry-brick-split-body',
32233                     cn: []
32234                 }
32235             ]
32236         };
32237         
32238         if(this.href.length){
32239             cfg.href = this.href;
32240         }
32241         
32242         if(this.title.length){
32243             cfg.cn[0].cn[0].cn.push({
32244                 tag: 'h4',
32245                 cls: 'masonry-brick-title',
32246                 html: this.title
32247             });
32248         }
32249         
32250         if(this.html.length){
32251             cfg.cn[1].cn.push({
32252                 tag: 'p',
32253                 cls: 'masonry-brick-text',
32254                 html: this.html
32255             });
32256         }
32257
32258         if(this.bgimage.length){
32259             cfg.cn[0].cn.push({
32260                 tag: 'img',
32261                 cls: 'masonry-brick-image-view',
32262                 src: this.bgimage
32263             });
32264         }
32265         
32266         if(this.videourl.length){
32267             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32268             // youtube support only?
32269             cfg.cn[0].cn.cn.push({
32270                 tag: 'iframe',
32271                 cls: 'masonry-brick-image-view',
32272                 src: vurl,
32273                 frameborder : 0,
32274                 allowfullscreen : true
32275             });
32276         }
32277         
32278         return cfg;
32279     },
32280     
32281     initEvents: function() 
32282     {
32283         switch (this.size) {
32284             case 'xs' :
32285                 this.x = 1;
32286                 this.y = 1;
32287                 break;
32288             case 'sm' :
32289                 this.x = 2;
32290                 this.y = 2;
32291                 break;
32292             case 'md' :
32293             case 'md-left' :
32294             case 'md-right' :
32295                 this.x = 3;
32296                 this.y = 3;
32297                 break;
32298             case 'tall' :
32299                 this.x = 2;
32300                 this.y = 3;
32301                 break;
32302             case 'wide' :
32303                 this.x = 3;
32304                 this.y = 2;
32305                 break;
32306             case 'wide-thin' :
32307                 this.x = 3;
32308                 this.y = 1;
32309                 break;
32310                         
32311             default :
32312                 break;
32313         }
32314         
32315         if(Roo.isTouch){
32316             this.el.on('touchstart', this.onTouchStart, this);
32317             this.el.on('touchmove', this.onTouchMove, this);
32318             this.el.on('touchend', this.onTouchEnd, this);
32319             this.el.on('contextmenu', this.onContextMenu, this);
32320         } else {
32321             this.el.on('mouseenter'  ,this.enter, this);
32322             this.el.on('mouseleave', this.leave, this);
32323             this.el.on('click', this.onClick, this);
32324         }
32325         
32326         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32327             this.parent().bricks.push(this);   
32328         }
32329         
32330     },
32331     
32332     onClick: function(e, el)
32333     {
32334         var time = this.endTimer - this.startTimer;
32335         // Roo.log(e.preventDefault());
32336         if(Roo.isTouch){
32337             if(time > 1000){
32338                 e.preventDefault();
32339                 return;
32340             }
32341         }
32342         
32343         if(!this.preventDefault){
32344             return;
32345         }
32346         
32347         e.preventDefault();
32348         
32349         if (this.activcClass != '') {
32350             this.selectBrick();
32351         }
32352         
32353         this.fireEvent('click', this);
32354     },
32355     
32356     enter: function(e, el)
32357     {
32358         e.preventDefault();
32359         
32360         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32361             return;
32362         }
32363         
32364         if(this.bgimage.length && this.html.length){
32365             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32366         }
32367     },
32368     
32369     leave: function(e, el)
32370     {
32371         e.preventDefault();
32372         
32373         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32374             return;
32375         }
32376         
32377         if(this.bgimage.length && this.html.length){
32378             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32379         }
32380     },
32381     
32382     onTouchStart: function(e, el)
32383     {
32384 //        e.preventDefault();
32385         
32386         this.touchmoved = false;
32387         
32388         if(!this.isFitContainer){
32389             return;
32390         }
32391         
32392         if(!this.bgimage.length || !this.html.length){
32393             return;
32394         }
32395         
32396         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32397         
32398         this.timer = new Date().getTime();
32399         
32400     },
32401     
32402     onTouchMove: function(e, el)
32403     {
32404         this.touchmoved = true;
32405     },
32406     
32407     onContextMenu : function(e,el)
32408     {
32409         e.preventDefault();
32410         e.stopPropagation();
32411         return false;
32412     },
32413     
32414     onTouchEnd: function(e, el)
32415     {
32416 //        e.preventDefault();
32417         
32418         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32419         
32420             this.leave(e,el);
32421             
32422             return;
32423         }
32424         
32425         if(!this.bgimage.length || !this.html.length){
32426             
32427             if(this.href.length){
32428                 window.location.href = this.href;
32429             }
32430             
32431             return;
32432         }
32433         
32434         if(!this.isFitContainer){
32435             return;
32436         }
32437         
32438         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32439         
32440         window.location.href = this.href;
32441     },
32442     
32443     //selection on single brick only
32444     selectBrick : function() {
32445         
32446         if (!this.parentId) {
32447             return;
32448         }
32449         
32450         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32451         var index = m.selectedBrick.indexOf(this.id);
32452         
32453         if ( index > -1) {
32454             m.selectedBrick.splice(index,1);
32455             this.el.removeClass(this.activeClass);
32456             return;
32457         }
32458         
32459         for(var i = 0; i < m.selectedBrick.length; i++) {
32460             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32461             b.el.removeClass(b.activeClass);
32462         }
32463         
32464         m.selectedBrick = [];
32465         
32466         m.selectedBrick.push(this.id);
32467         this.el.addClass(this.activeClass);
32468         return;
32469     }
32470     
32471 });
32472
32473 Roo.apply(Roo.bootstrap.MasonryBrick, {
32474     
32475     //groups: {},
32476     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32477      /**
32478     * register a Masonry Brick
32479     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32480     */
32481     
32482     register : function(brick)
32483     {
32484         //this.groups[brick.id] = brick;
32485         this.groups.add(brick.id, brick);
32486     },
32487     /**
32488     * fetch a  masonry brick based on the masonry brick ID
32489     * @param {string} the masonry brick to add
32490     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32491     */
32492     
32493     get: function(brick_id) 
32494     {
32495         // if (typeof(this.groups[brick_id]) == 'undefined') {
32496         //     return false;
32497         // }
32498         // return this.groups[brick_id] ;
32499         
32500         if(this.groups.key(brick_id)) {
32501             return this.groups.key(brick_id);
32502         }
32503         
32504         return false;
32505     }
32506     
32507     
32508     
32509 });
32510
32511  /*
32512  * - LGPL
32513  *
32514  * element
32515  * 
32516  */
32517
32518 /**
32519  * @class Roo.bootstrap.Brick
32520  * @extends Roo.bootstrap.Component
32521  * Bootstrap Brick class
32522  * 
32523  * @constructor
32524  * Create a new Brick
32525  * @param {Object} config The config object
32526  */
32527
32528 Roo.bootstrap.Brick = function(config){
32529     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32530     
32531     this.addEvents({
32532         // raw events
32533         /**
32534          * @event click
32535          * When a Brick is click
32536          * @param {Roo.bootstrap.Brick} this
32537          * @param {Roo.EventObject} e
32538          */
32539         "click" : true
32540     });
32541 };
32542
32543 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32544     
32545     /**
32546      * @cfg {String} title
32547      */   
32548     title : '',
32549     /**
32550      * @cfg {String} html
32551      */   
32552     html : '',
32553     /**
32554      * @cfg {String} bgimage
32555      */   
32556     bgimage : '',
32557     /**
32558      * @cfg {String} cls
32559      */   
32560     cls : '',
32561     /**
32562      * @cfg {String} href
32563      */   
32564     href : '',
32565     /**
32566      * @cfg {String} video
32567      */   
32568     video : '',
32569     /**
32570      * @cfg {Boolean} square
32571      */   
32572     square : true,
32573     
32574     getAutoCreate : function()
32575     {
32576         var cls = 'roo-brick';
32577         
32578         if(this.href.length){
32579             cls += ' roo-brick-link';
32580         }
32581         
32582         if(this.bgimage.length){
32583             cls += ' roo-brick-image';
32584         }
32585         
32586         if(!this.html.length && !this.bgimage.length){
32587             cls += ' roo-brick-center-title';
32588         }
32589         
32590         if(!this.html.length && this.bgimage.length){
32591             cls += ' roo-brick-bottom-title';
32592         }
32593         
32594         if(this.cls){
32595             cls += ' ' + this.cls;
32596         }
32597         
32598         var cfg = {
32599             tag: (this.href.length) ? 'a' : 'div',
32600             cls: cls,
32601             cn: [
32602                 {
32603                     tag: 'div',
32604                     cls: 'roo-brick-paragraph',
32605                     cn: []
32606                 }
32607             ]
32608         };
32609         
32610         if(this.href.length){
32611             cfg.href = this.href;
32612         }
32613         
32614         var cn = cfg.cn[0].cn;
32615         
32616         if(this.title.length){
32617             cn.push({
32618                 tag: 'h4',
32619                 cls: 'roo-brick-title',
32620                 html: this.title
32621             });
32622         }
32623         
32624         if(this.html.length){
32625             cn.push({
32626                 tag: 'p',
32627                 cls: 'roo-brick-text',
32628                 html: this.html
32629             });
32630         } else {
32631             cn.cls += ' hide';
32632         }
32633         
32634         if(this.bgimage.length){
32635             cfg.cn.push({
32636                 tag: 'img',
32637                 cls: 'roo-brick-image-view',
32638                 src: this.bgimage
32639             });
32640         }
32641         
32642         return cfg;
32643     },
32644     
32645     initEvents: function() 
32646     {
32647         if(this.title.length || this.html.length){
32648             this.el.on('mouseenter'  ,this.enter, this);
32649             this.el.on('mouseleave', this.leave, this);
32650         }
32651         
32652         Roo.EventManager.onWindowResize(this.resize, this); 
32653         
32654         if(this.bgimage.length){
32655             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32656             this.imageEl.on('load', this.onImageLoad, this);
32657             return;
32658         }
32659         
32660         this.resize();
32661     },
32662     
32663     onImageLoad : function()
32664     {
32665         this.resize();
32666     },
32667     
32668     resize : function()
32669     {
32670         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32671         
32672         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32673         
32674         if(this.bgimage.length){
32675             var image = this.el.select('.roo-brick-image-view', true).first();
32676             
32677             image.setWidth(paragraph.getWidth());
32678             
32679             if(this.square){
32680                 image.setHeight(paragraph.getWidth());
32681             }
32682             
32683             this.el.setHeight(image.getHeight());
32684             paragraph.setHeight(image.getHeight());
32685             
32686         }
32687         
32688     },
32689     
32690     enter: function(e, el)
32691     {
32692         e.preventDefault();
32693         
32694         if(this.bgimage.length){
32695             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32696             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32697         }
32698     },
32699     
32700     leave: function(e, el)
32701     {
32702         e.preventDefault();
32703         
32704         if(this.bgimage.length){
32705             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32706             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32707         }
32708     }
32709     
32710 });
32711
32712  
32713
32714  /*
32715  * - LGPL
32716  *
32717  * Input
32718  * 
32719  */
32720
32721 /**
32722  * @class Roo.bootstrap.NumberField
32723  * @extends Roo.bootstrap.Input
32724  * Bootstrap NumberField class
32725  * 
32726  * 
32727  * 
32728  * 
32729  * @constructor
32730  * Create a new NumberField
32731  * @param {Object} config The config object
32732  */
32733
32734 Roo.bootstrap.NumberField = function(config){
32735     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32736 };
32737
32738 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32739     
32740     /**
32741      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32742      */
32743     allowDecimals : true,
32744     /**
32745      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32746      */
32747     decimalSeparator : ".",
32748     /**
32749      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32750      */
32751     decimalPrecision : 2,
32752     /**
32753      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32754      */
32755     allowNegative : true,
32756     /**
32757      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32758      */
32759     minValue : Number.NEGATIVE_INFINITY,
32760     /**
32761      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32762      */
32763     maxValue : Number.MAX_VALUE,
32764     /**
32765      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32766      */
32767     minText : "The minimum value for this field is {0}",
32768     /**
32769      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32770      */
32771     maxText : "The maximum value for this field is {0}",
32772     /**
32773      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32774      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32775      */
32776     nanText : "{0} is not a valid number",
32777     /**
32778      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32779      */
32780     castInt : true,
32781
32782     // private
32783     initEvents : function()
32784     {   
32785         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32786         
32787         var allowed = "0123456789";
32788         
32789         if(this.allowDecimals){
32790             allowed += this.decimalSeparator;
32791         }
32792         
32793         if(this.allowNegative){
32794             allowed += "-";
32795         }
32796         
32797         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32798         
32799         var keyPress = function(e){
32800             
32801             var k = e.getKey();
32802             
32803             var c = e.getCharCode();
32804             
32805             if(
32806                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32807                     allowed.indexOf(String.fromCharCode(c)) === -1
32808             ){
32809                 e.stopEvent();
32810                 return;
32811             }
32812             
32813             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32814                 return;
32815             }
32816             
32817             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32818                 e.stopEvent();
32819             }
32820         };
32821         
32822         this.el.on("keypress", keyPress, this);
32823     },
32824     
32825     validateValue : function(value)
32826     {
32827         
32828         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32829             return false;
32830         }
32831         
32832         var num = this.parseValue(value);
32833         
32834         if(isNaN(num)){
32835             this.markInvalid(String.format(this.nanText, value));
32836             return false;
32837         }
32838         
32839         if(num < this.minValue){
32840             this.markInvalid(String.format(this.minText, this.minValue));
32841             return false;
32842         }
32843         
32844         if(num > this.maxValue){
32845             this.markInvalid(String.format(this.maxText, this.maxValue));
32846             return false;
32847         }
32848         
32849         return true;
32850     },
32851
32852     getValue : function()
32853     {
32854         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32855     },
32856
32857     parseValue : function(value)
32858     {
32859         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32860         return isNaN(value) ? '' : value;
32861     },
32862
32863     fixPrecision : function(value)
32864     {
32865         var nan = isNaN(value);
32866         
32867         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32868             return nan ? '' : value;
32869         }
32870         return parseFloat(value).toFixed(this.decimalPrecision);
32871     },
32872
32873     setValue : function(v)
32874     {
32875         v = this.fixPrecision(v);
32876         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32877     },
32878
32879     decimalPrecisionFcn : function(v)
32880     {
32881         return Math.floor(v);
32882     },
32883
32884     beforeBlur : function()
32885     {
32886         if(!this.castInt){
32887             return;
32888         }
32889         
32890         var v = this.parseValue(this.getRawValue());
32891         if(v){
32892             this.setValue(v);
32893         }
32894     }
32895     
32896 });
32897
32898  
32899
32900 /*
32901 * Licence: LGPL
32902 */
32903
32904 /**
32905  * @class Roo.bootstrap.DocumentSlider
32906  * @extends Roo.bootstrap.Component
32907  * Bootstrap DocumentSlider class
32908  * 
32909  * @constructor
32910  * Create a new DocumentViewer
32911  * @param {Object} config The config object
32912  */
32913
32914 Roo.bootstrap.DocumentSlider = function(config){
32915     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32916     
32917     this.files = [];
32918     
32919     this.addEvents({
32920         /**
32921          * @event initial
32922          * Fire after initEvent
32923          * @param {Roo.bootstrap.DocumentSlider} this
32924          */
32925         "initial" : true,
32926         /**
32927          * @event update
32928          * Fire after update
32929          * @param {Roo.bootstrap.DocumentSlider} this
32930          */
32931         "update" : true,
32932         /**
32933          * @event click
32934          * Fire after click
32935          * @param {Roo.bootstrap.DocumentSlider} this
32936          */
32937         "click" : true
32938     });
32939 };
32940
32941 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32942     
32943     files : false,
32944     
32945     indicator : 0,
32946     
32947     getAutoCreate : function()
32948     {
32949         var cfg = {
32950             tag : 'div',
32951             cls : 'roo-document-slider',
32952             cn : [
32953                 {
32954                     tag : 'div',
32955                     cls : 'roo-document-slider-header',
32956                     cn : [
32957                         {
32958                             tag : 'div',
32959                             cls : 'roo-document-slider-header-title'
32960                         }
32961                     ]
32962                 },
32963                 {
32964                     tag : 'div',
32965                     cls : 'roo-document-slider-body',
32966                     cn : [
32967                         {
32968                             tag : 'div',
32969                             cls : 'roo-document-slider-prev',
32970                             cn : [
32971                                 {
32972                                     tag : 'i',
32973                                     cls : 'fa fa-chevron-left'
32974                                 }
32975                             ]
32976                         },
32977                         {
32978                             tag : 'div',
32979                             cls : 'roo-document-slider-thumb',
32980                             cn : [
32981                                 {
32982                                     tag : 'img',
32983                                     cls : 'roo-document-slider-image'
32984                                 }
32985                             ]
32986                         },
32987                         {
32988                             tag : 'div',
32989                             cls : 'roo-document-slider-next',
32990                             cn : [
32991                                 {
32992                                     tag : 'i',
32993                                     cls : 'fa fa-chevron-right'
32994                                 }
32995                             ]
32996                         }
32997                     ]
32998                 }
32999             ]
33000         };
33001         
33002         return cfg;
33003     },
33004     
33005     initEvents : function()
33006     {
33007         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33008         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33009         
33010         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33011         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33012         
33013         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33014         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33015         
33016         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33017         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33018         
33019         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33020         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33021         
33022         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33023         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33024         
33025         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33026         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33027         
33028         this.thumbEl.on('click', this.onClick, this);
33029         
33030         this.prevIndicator.on('click', this.prev, this);
33031         
33032         this.nextIndicator.on('click', this.next, this);
33033         
33034     },
33035     
33036     initial : function()
33037     {
33038         if(this.files.length){
33039             this.indicator = 1;
33040             this.update()
33041         }
33042         
33043         this.fireEvent('initial', this);
33044     },
33045     
33046     update : function()
33047     {
33048         this.imageEl.attr('src', this.files[this.indicator - 1]);
33049         
33050         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33051         
33052         this.prevIndicator.show();
33053         
33054         if(this.indicator == 1){
33055             this.prevIndicator.hide();
33056         }
33057         
33058         this.nextIndicator.show();
33059         
33060         if(this.indicator == this.files.length){
33061             this.nextIndicator.hide();
33062         }
33063         
33064         this.thumbEl.scrollTo('top');
33065         
33066         this.fireEvent('update', this);
33067     },
33068     
33069     onClick : function(e)
33070     {
33071         e.preventDefault();
33072         
33073         this.fireEvent('click', this);
33074     },
33075     
33076     prev : function(e)
33077     {
33078         e.preventDefault();
33079         
33080         this.indicator = Math.max(1, this.indicator - 1);
33081         
33082         this.update();
33083     },
33084     
33085     next : function(e)
33086     {
33087         e.preventDefault();
33088         
33089         this.indicator = Math.min(this.files.length, this.indicator + 1);
33090         
33091         this.update();
33092     }
33093 });
33094 /*
33095  * - LGPL
33096  *
33097  * RadioSet
33098  *
33099  *
33100  */
33101
33102 /**
33103  * @class Roo.bootstrap.RadioSet
33104  * @extends Roo.bootstrap.Input
33105  * Bootstrap RadioSet class
33106  * @cfg {String} indicatorpos (left|right) default left
33107  * @cfg {Boolean} inline (true|false) inline the element (default true)
33108  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33109  * @constructor
33110  * Create a new RadioSet
33111  * @param {Object} config The config object
33112  */
33113
33114 Roo.bootstrap.RadioSet = function(config){
33115     
33116     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33117     
33118     this.radioes = [];
33119     
33120     Roo.bootstrap.RadioSet.register(this);
33121     
33122     this.addEvents({
33123         /**
33124         * @event check
33125         * Fires when the element is checked or unchecked.
33126         * @param {Roo.bootstrap.RadioSet} this This radio
33127         * @param {Roo.bootstrap.Radio} item The checked item
33128         */
33129        check : true
33130     });
33131     
33132 };
33133
33134 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33135
33136     radioes : false,
33137     
33138     inline : true,
33139     
33140     weight : '',
33141     
33142     indicatorpos : 'left',
33143     
33144     getAutoCreate : function()
33145     {
33146         var label = {
33147             tag : 'label',
33148             cls : 'roo-radio-set-label',
33149             cn : [
33150                 {
33151                     tag : 'span',
33152                     html : this.fieldLabel
33153                 }
33154             ]
33155         };
33156         
33157         if(this.indicatorpos == 'left'){
33158             label.cn.unshift({
33159                 tag : 'i',
33160                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33161                 tooltip : 'This field is required'
33162             });
33163         } else {
33164             label.cn.push({
33165                 tag : 'i',
33166                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33167                 tooltip : 'This field is required'
33168             });
33169         }
33170         
33171         var items = {
33172             tag : 'div',
33173             cls : 'roo-radio-set-items'
33174         };
33175         
33176         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33177         
33178         if (align === 'left' && this.fieldLabel.length) {
33179             
33180             items = {
33181                 cls : "roo-radio-set-right", 
33182                 cn: [
33183                     items
33184                 ]
33185             };
33186             
33187             if(this.labelWidth > 12){
33188                 label.style = "width: " + this.labelWidth + 'px';
33189             }
33190             
33191             if(this.labelWidth < 13 && this.labelmd == 0){
33192                 this.labelmd = this.labelWidth;
33193             }
33194             
33195             if(this.labellg > 0){
33196                 label.cls += ' col-lg-' + this.labellg;
33197                 items.cls += ' col-lg-' + (12 - this.labellg);
33198             }
33199             
33200             if(this.labelmd > 0){
33201                 label.cls += ' col-md-' + this.labelmd;
33202                 items.cls += ' col-md-' + (12 - this.labelmd);
33203             }
33204             
33205             if(this.labelsm > 0){
33206                 label.cls += ' col-sm-' + this.labelsm;
33207                 items.cls += ' col-sm-' + (12 - this.labelsm);
33208             }
33209             
33210             if(this.labelxs > 0){
33211                 label.cls += ' col-xs-' + this.labelxs;
33212                 items.cls += ' col-xs-' + (12 - this.labelxs);
33213             }
33214         }
33215         
33216         var cfg = {
33217             tag : 'div',
33218             cls : 'roo-radio-set',
33219             cn : [
33220                 {
33221                     tag : 'input',
33222                     cls : 'roo-radio-set-input',
33223                     type : 'hidden',
33224                     name : this.name,
33225                     value : this.value ? this.value :  ''
33226                 },
33227                 label,
33228                 items
33229             ]
33230         };
33231         
33232         if(this.weight.length){
33233             cfg.cls += ' roo-radio-' + this.weight;
33234         }
33235         
33236         if(this.inline) {
33237             cfg.cls += ' roo-radio-set-inline';
33238         }
33239         
33240         return cfg;
33241         
33242     },
33243
33244     initEvents : function()
33245     {
33246         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33247         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33248         
33249         if(!this.fieldLabel.length){
33250             this.labelEl.hide();
33251         }
33252         
33253         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33254         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33255         
33256         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33257         this.indicatorEl().hide();
33258         
33259         this.originalValue = this.getValue();
33260         
33261     },
33262     
33263     inputEl: function ()
33264     {
33265         return this.el.select('.roo-radio-set-input', true).first();
33266     },
33267     
33268     getChildContainer : function()
33269     {
33270         return this.itemsEl;
33271     },
33272     
33273     register : function(item)
33274     {
33275         this.radioes.push(item);
33276         
33277     },
33278     
33279     validate : function()
33280     {   
33281         var valid = false;
33282         
33283         Roo.each(this.radioes, function(i){
33284             if(!i.checked){
33285                 return;
33286             }
33287             
33288             valid = true;
33289             return false;
33290         });
33291         
33292         if(this.allowBlank) {
33293             return true;
33294         }
33295         
33296         if(this.disabled || valid){
33297             this.markValid();
33298             return true;
33299         }
33300         
33301         this.markInvalid();
33302         return false;
33303         
33304     },
33305     
33306     markValid : function()
33307     {
33308         if(this.labelEl.isVisible(true)){
33309             this.indicatorEl().hide();
33310         }
33311         
33312         this.el.removeClass([this.invalidClass, this.validClass]);
33313         this.el.addClass(this.validClass);
33314         
33315         this.fireEvent('valid', this);
33316     },
33317     
33318     markInvalid : function(msg)
33319     {
33320         if(this.allowBlank || this.disabled){
33321             return;
33322         }
33323         
33324         if(this.labelEl.isVisible(true)){
33325             this.indicatorEl().show();
33326         }
33327         
33328         this.el.removeClass([this.invalidClass, this.validClass]);
33329         this.el.addClass(this.invalidClass);
33330         
33331         this.fireEvent('invalid', this, msg);
33332         
33333     },
33334     
33335     setValue : function(v, suppressEvent)
33336     {   
33337         this.value = v;
33338         if(this.rendered){
33339             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33340         }
33341         
33342         Roo.each(this.radioes, function(i){
33343             
33344             i.checked = false;
33345             i.el.removeClass('checked');
33346             
33347             if(i.value === v || i.value.toString() === v.toString()){
33348                 i.checked = true;
33349                 i.el.addClass('checked');
33350                 
33351                 if(suppressEvent !== true){
33352                     this.fireEvent('check', this, i);
33353                 }
33354             }
33355             
33356         }, this);
33357         
33358         this.validate();
33359     },
33360     
33361     clearInvalid : function(){
33362         
33363         if(!this.el || this.preventMark){
33364             return;
33365         }
33366         
33367         this.el.removeClass([this.invalidClass]);
33368         
33369         this.fireEvent('valid', this);
33370     }
33371     
33372 });
33373
33374 Roo.apply(Roo.bootstrap.RadioSet, {
33375     
33376     groups: {},
33377     
33378     register : function(set)
33379     {
33380         this.groups[set.name] = set;
33381     },
33382     
33383     get: function(name) 
33384     {
33385         if (typeof(this.groups[name]) == 'undefined') {
33386             return false;
33387         }
33388         
33389         return this.groups[name] ;
33390     }
33391     
33392 });
33393 /*
33394  * Based on:
33395  * Ext JS Library 1.1.1
33396  * Copyright(c) 2006-2007, Ext JS, LLC.
33397  *
33398  * Originally Released Under LGPL - original licence link has changed is not relivant.
33399  *
33400  * Fork - LGPL
33401  * <script type="text/javascript">
33402  */
33403
33404
33405 /**
33406  * @class Roo.bootstrap.SplitBar
33407  * @extends Roo.util.Observable
33408  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33409  * <br><br>
33410  * Usage:
33411  * <pre><code>
33412 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33413                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33414 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33415 split.minSize = 100;
33416 split.maxSize = 600;
33417 split.animate = true;
33418 split.on('moved', splitterMoved);
33419 </code></pre>
33420  * @constructor
33421  * Create a new SplitBar
33422  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33423  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33424  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33425  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33426                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33427                         position of the SplitBar).
33428  */
33429 Roo.bootstrap.SplitBar = function(cfg){
33430     
33431     /** @private */
33432     
33433     //{
33434     //  dragElement : elm
33435     //  resizingElement: el,
33436         // optional..
33437     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33438     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33439         // existingProxy ???
33440     //}
33441     
33442     this.el = Roo.get(cfg.dragElement, true);
33443     this.el.dom.unselectable = "on";
33444     /** @private */
33445     this.resizingEl = Roo.get(cfg.resizingElement, true);
33446
33447     /**
33448      * @private
33449      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33450      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33451      * @type Number
33452      */
33453     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33454     
33455     /**
33456      * The minimum size of the resizing element. (Defaults to 0)
33457      * @type Number
33458      */
33459     this.minSize = 0;
33460     
33461     /**
33462      * The maximum size of the resizing element. (Defaults to 2000)
33463      * @type Number
33464      */
33465     this.maxSize = 2000;
33466     
33467     /**
33468      * Whether to animate the transition to the new size
33469      * @type Boolean
33470      */
33471     this.animate = false;
33472     
33473     /**
33474      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33475      * @type Boolean
33476      */
33477     this.useShim = false;
33478     
33479     /** @private */
33480     this.shim = null;
33481     
33482     if(!cfg.existingProxy){
33483         /** @private */
33484         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33485     }else{
33486         this.proxy = Roo.get(cfg.existingProxy).dom;
33487     }
33488     /** @private */
33489     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33490     
33491     /** @private */
33492     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33493     
33494     /** @private */
33495     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33496     
33497     /** @private */
33498     this.dragSpecs = {};
33499     
33500     /**
33501      * @private The adapter to use to positon and resize elements
33502      */
33503     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33504     this.adapter.init(this);
33505     
33506     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33507         /** @private */
33508         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33509         this.el.addClass("roo-splitbar-h");
33510     }else{
33511         /** @private */
33512         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33513         this.el.addClass("roo-splitbar-v");
33514     }
33515     
33516     this.addEvents({
33517         /**
33518          * @event resize
33519          * Fires when the splitter is moved (alias for {@link #event-moved})
33520          * @param {Roo.bootstrap.SplitBar} this
33521          * @param {Number} newSize the new width or height
33522          */
33523         "resize" : true,
33524         /**
33525          * @event moved
33526          * Fires when the splitter is moved
33527          * @param {Roo.bootstrap.SplitBar} this
33528          * @param {Number} newSize the new width or height
33529          */
33530         "moved" : true,
33531         /**
33532          * @event beforeresize
33533          * Fires before the splitter is dragged
33534          * @param {Roo.bootstrap.SplitBar} this
33535          */
33536         "beforeresize" : true,
33537
33538         "beforeapply" : true
33539     });
33540
33541     Roo.util.Observable.call(this);
33542 };
33543
33544 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33545     onStartProxyDrag : function(x, y){
33546         this.fireEvent("beforeresize", this);
33547         if(!this.overlay){
33548             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33549             o.unselectable();
33550             o.enableDisplayMode("block");
33551             // all splitbars share the same overlay
33552             Roo.bootstrap.SplitBar.prototype.overlay = o;
33553         }
33554         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33555         this.overlay.show();
33556         Roo.get(this.proxy).setDisplayed("block");
33557         var size = this.adapter.getElementSize(this);
33558         this.activeMinSize = this.getMinimumSize();;
33559         this.activeMaxSize = this.getMaximumSize();;
33560         var c1 = size - this.activeMinSize;
33561         var c2 = Math.max(this.activeMaxSize - size, 0);
33562         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33563             this.dd.resetConstraints();
33564             this.dd.setXConstraint(
33565                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33566                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33567             );
33568             this.dd.setYConstraint(0, 0);
33569         }else{
33570             this.dd.resetConstraints();
33571             this.dd.setXConstraint(0, 0);
33572             this.dd.setYConstraint(
33573                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33574                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33575             );
33576          }
33577         this.dragSpecs.startSize = size;
33578         this.dragSpecs.startPoint = [x, y];
33579         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33580     },
33581     
33582     /** 
33583      * @private Called after the drag operation by the DDProxy
33584      */
33585     onEndProxyDrag : function(e){
33586         Roo.get(this.proxy).setDisplayed(false);
33587         var endPoint = Roo.lib.Event.getXY(e);
33588         if(this.overlay){
33589             this.overlay.hide();
33590         }
33591         var newSize;
33592         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33593             newSize = this.dragSpecs.startSize + 
33594                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33595                     endPoint[0] - this.dragSpecs.startPoint[0] :
33596                     this.dragSpecs.startPoint[0] - endPoint[0]
33597                 );
33598         }else{
33599             newSize = this.dragSpecs.startSize + 
33600                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33601                     endPoint[1] - this.dragSpecs.startPoint[1] :
33602                     this.dragSpecs.startPoint[1] - endPoint[1]
33603                 );
33604         }
33605         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33606         if(newSize != this.dragSpecs.startSize){
33607             if(this.fireEvent('beforeapply', this, newSize) !== false){
33608                 this.adapter.setElementSize(this, newSize);
33609                 this.fireEvent("moved", this, newSize);
33610                 this.fireEvent("resize", this, newSize);
33611             }
33612         }
33613     },
33614     
33615     /**
33616      * Get the adapter this SplitBar uses
33617      * @return The adapter object
33618      */
33619     getAdapter : function(){
33620         return this.adapter;
33621     },
33622     
33623     /**
33624      * Set the adapter this SplitBar uses
33625      * @param {Object} adapter A SplitBar adapter object
33626      */
33627     setAdapter : function(adapter){
33628         this.adapter = adapter;
33629         this.adapter.init(this);
33630     },
33631     
33632     /**
33633      * Gets the minimum size for the resizing element
33634      * @return {Number} The minimum size
33635      */
33636     getMinimumSize : function(){
33637         return this.minSize;
33638     },
33639     
33640     /**
33641      * Sets the minimum size for the resizing element
33642      * @param {Number} minSize The minimum size
33643      */
33644     setMinimumSize : function(minSize){
33645         this.minSize = minSize;
33646     },
33647     
33648     /**
33649      * Gets the maximum size for the resizing element
33650      * @return {Number} The maximum size
33651      */
33652     getMaximumSize : function(){
33653         return this.maxSize;
33654     },
33655     
33656     /**
33657      * Sets the maximum size for the resizing element
33658      * @param {Number} maxSize The maximum size
33659      */
33660     setMaximumSize : function(maxSize){
33661         this.maxSize = maxSize;
33662     },
33663     
33664     /**
33665      * Sets the initialize size for the resizing element
33666      * @param {Number} size The initial size
33667      */
33668     setCurrentSize : function(size){
33669         var oldAnimate = this.animate;
33670         this.animate = false;
33671         this.adapter.setElementSize(this, size);
33672         this.animate = oldAnimate;
33673     },
33674     
33675     /**
33676      * Destroy this splitbar. 
33677      * @param {Boolean} removeEl True to remove the element
33678      */
33679     destroy : function(removeEl){
33680         if(this.shim){
33681             this.shim.remove();
33682         }
33683         this.dd.unreg();
33684         this.proxy.parentNode.removeChild(this.proxy);
33685         if(removeEl){
33686             this.el.remove();
33687         }
33688     }
33689 });
33690
33691 /**
33692  * @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.
33693  */
33694 Roo.bootstrap.SplitBar.createProxy = function(dir){
33695     var proxy = new Roo.Element(document.createElement("div"));
33696     proxy.unselectable();
33697     var cls = 'roo-splitbar-proxy';
33698     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33699     document.body.appendChild(proxy.dom);
33700     return proxy.dom;
33701 };
33702
33703 /** 
33704  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33705  * Default Adapter. It assumes the splitter and resizing element are not positioned
33706  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33707  */
33708 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33709 };
33710
33711 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33712     // do nothing for now
33713     init : function(s){
33714     
33715     },
33716     /**
33717      * Called before drag operations to get the current size of the resizing element. 
33718      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33719      */
33720      getElementSize : function(s){
33721         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33722             return s.resizingEl.getWidth();
33723         }else{
33724             return s.resizingEl.getHeight();
33725         }
33726     },
33727     
33728     /**
33729      * Called after drag operations to set the size of the resizing element.
33730      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33731      * @param {Number} newSize The new size to set
33732      * @param {Function} onComplete A function to be invoked when resizing is complete
33733      */
33734     setElementSize : function(s, newSize, onComplete){
33735         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33736             if(!s.animate){
33737                 s.resizingEl.setWidth(newSize);
33738                 if(onComplete){
33739                     onComplete(s, newSize);
33740                 }
33741             }else{
33742                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33743             }
33744         }else{
33745             
33746             if(!s.animate){
33747                 s.resizingEl.setHeight(newSize);
33748                 if(onComplete){
33749                     onComplete(s, newSize);
33750                 }
33751             }else{
33752                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33753             }
33754         }
33755     }
33756 };
33757
33758 /** 
33759  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33760  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33761  * Adapter that  moves the splitter element to align with the resized sizing element. 
33762  * Used with an absolute positioned SplitBar.
33763  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33764  * document.body, make sure you assign an id to the body element.
33765  */
33766 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33767     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33768     this.container = Roo.get(container);
33769 };
33770
33771 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33772     init : function(s){
33773         this.basic.init(s);
33774     },
33775     
33776     getElementSize : function(s){
33777         return this.basic.getElementSize(s);
33778     },
33779     
33780     setElementSize : function(s, newSize, onComplete){
33781         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33782     },
33783     
33784     moveSplitter : function(s){
33785         var yes = Roo.bootstrap.SplitBar;
33786         switch(s.placement){
33787             case yes.LEFT:
33788                 s.el.setX(s.resizingEl.getRight());
33789                 break;
33790             case yes.RIGHT:
33791                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33792                 break;
33793             case yes.TOP:
33794                 s.el.setY(s.resizingEl.getBottom());
33795                 break;
33796             case yes.BOTTOM:
33797                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33798                 break;
33799         }
33800     }
33801 };
33802
33803 /**
33804  * Orientation constant - Create a vertical SplitBar
33805  * @static
33806  * @type Number
33807  */
33808 Roo.bootstrap.SplitBar.VERTICAL = 1;
33809
33810 /**
33811  * Orientation constant - Create a horizontal SplitBar
33812  * @static
33813  * @type Number
33814  */
33815 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33816
33817 /**
33818  * Placement constant - The resizing element is to the left of the splitter element
33819  * @static
33820  * @type Number
33821  */
33822 Roo.bootstrap.SplitBar.LEFT = 1;
33823
33824 /**
33825  * Placement constant - The resizing element is to the right of the splitter element
33826  * @static
33827  * @type Number
33828  */
33829 Roo.bootstrap.SplitBar.RIGHT = 2;
33830
33831 /**
33832  * Placement constant - The resizing element is positioned above the splitter element
33833  * @static
33834  * @type Number
33835  */
33836 Roo.bootstrap.SplitBar.TOP = 3;
33837
33838 /**
33839  * Placement constant - The resizing element is positioned under splitter element
33840  * @static
33841  * @type Number
33842  */
33843 Roo.bootstrap.SplitBar.BOTTOM = 4;
33844 Roo.namespace("Roo.bootstrap.layout");/*
33845  * Based on:
33846  * Ext JS Library 1.1.1
33847  * Copyright(c) 2006-2007, Ext JS, LLC.
33848  *
33849  * Originally Released Under LGPL - original licence link has changed is not relivant.
33850  *
33851  * Fork - LGPL
33852  * <script type="text/javascript">
33853  */
33854
33855 /**
33856  * @class Roo.bootstrap.layout.Manager
33857  * @extends Roo.bootstrap.Component
33858  * Base class for layout managers.
33859  */
33860 Roo.bootstrap.layout.Manager = function(config)
33861 {
33862     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33863
33864
33865
33866
33867
33868     /** false to disable window resize monitoring @type Boolean */
33869     this.monitorWindowResize = true;
33870     this.regions = {};
33871     this.addEvents({
33872         /**
33873          * @event layout
33874          * Fires when a layout is performed.
33875          * @param {Roo.LayoutManager} this
33876          */
33877         "layout" : true,
33878         /**
33879          * @event regionresized
33880          * Fires when the user resizes a region.
33881          * @param {Roo.LayoutRegion} region The resized region
33882          * @param {Number} newSize The new size (width for east/west, height for north/south)
33883          */
33884         "regionresized" : true,
33885         /**
33886          * @event regioncollapsed
33887          * Fires when a region is collapsed.
33888          * @param {Roo.LayoutRegion} region The collapsed region
33889          */
33890         "regioncollapsed" : true,
33891         /**
33892          * @event regionexpanded
33893          * Fires when a region is expanded.
33894          * @param {Roo.LayoutRegion} region The expanded region
33895          */
33896         "regionexpanded" : true
33897     });
33898     this.updating = false;
33899
33900     if (config.el) {
33901         this.el = Roo.get(config.el);
33902         this.initEvents();
33903     }
33904
33905 };
33906
33907 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33908
33909
33910     regions : null,
33911
33912     monitorWindowResize : true,
33913
33914
33915     updating : false,
33916
33917
33918     onRender : function(ct, position)
33919     {
33920         if(!this.el){
33921             this.el = Roo.get(ct);
33922             this.initEvents();
33923         }
33924         //this.fireEvent('render',this);
33925     },
33926
33927
33928     initEvents: function()
33929     {
33930
33931
33932         // ie scrollbar fix
33933         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33934             document.body.scroll = "no";
33935         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33936             this.el.position('relative');
33937         }
33938         this.id = this.el.id;
33939         this.el.addClass("roo-layout-container");
33940         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33941         if(this.el.dom != document.body ) {
33942             this.el.on('resize', this.layout,this);
33943             this.el.on('show', this.layout,this);
33944         }
33945
33946     },
33947
33948     /**
33949      * Returns true if this layout is currently being updated
33950      * @return {Boolean}
33951      */
33952     isUpdating : function(){
33953         return this.updating;
33954     },
33955
33956     /**
33957      * Suspend the LayoutManager from doing auto-layouts while
33958      * making multiple add or remove calls
33959      */
33960     beginUpdate : function(){
33961         this.updating = true;
33962     },
33963
33964     /**
33965      * Restore auto-layouts and optionally disable the manager from performing a layout
33966      * @param {Boolean} noLayout true to disable a layout update
33967      */
33968     endUpdate : function(noLayout){
33969         this.updating = false;
33970         if(!noLayout){
33971             this.layout();
33972         }
33973     },
33974
33975     layout: function(){
33976         // abstract...
33977     },
33978
33979     onRegionResized : function(region, newSize){
33980         this.fireEvent("regionresized", region, newSize);
33981         this.layout();
33982     },
33983
33984     onRegionCollapsed : function(region){
33985         this.fireEvent("regioncollapsed", region);
33986     },
33987
33988     onRegionExpanded : function(region){
33989         this.fireEvent("regionexpanded", region);
33990     },
33991
33992     /**
33993      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33994      * performs box-model adjustments.
33995      * @return {Object} The size as an object {width: (the width), height: (the height)}
33996      */
33997     getViewSize : function()
33998     {
33999         var size;
34000         if(this.el.dom != document.body){
34001             size = this.el.getSize();
34002         }else{
34003             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34004         }
34005         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34006         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34007         return size;
34008     },
34009
34010     /**
34011      * Returns the Element this layout is bound to.
34012      * @return {Roo.Element}
34013      */
34014     getEl : function(){
34015         return this.el;
34016     },
34017
34018     /**
34019      * Returns the specified region.
34020      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34021      * @return {Roo.LayoutRegion}
34022      */
34023     getRegion : function(target){
34024         return this.regions[target.toLowerCase()];
34025     },
34026
34027     onWindowResize : function(){
34028         if(this.monitorWindowResize){
34029             this.layout();
34030         }
34031     }
34032 });
34033 /*
34034  * Based on:
34035  * Ext JS Library 1.1.1
34036  * Copyright(c) 2006-2007, Ext JS, LLC.
34037  *
34038  * Originally Released Under LGPL - original licence link has changed is not relivant.
34039  *
34040  * Fork - LGPL
34041  * <script type="text/javascript">
34042  */
34043 /**
34044  * @class Roo.bootstrap.layout.Border
34045  * @extends Roo.bootstrap.layout.Manager
34046  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34047  * please see: examples/bootstrap/nested.html<br><br>
34048  
34049 <b>The container the layout is rendered into can be either the body element or any other element.
34050 If it is not the body element, the container needs to either be an absolute positioned element,
34051 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34052 the container size if it is not the body element.</b>
34053
34054 * @constructor
34055 * Create a new Border
34056 * @param {Object} config Configuration options
34057  */
34058 Roo.bootstrap.layout.Border = function(config){
34059     config = config || {};
34060     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34061     
34062     
34063     
34064     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34065         if(config[region]){
34066             config[region].region = region;
34067             this.addRegion(config[region]);
34068         }
34069     },this);
34070     
34071 };
34072
34073 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34074
34075 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34076     /**
34077      * Creates and adds a new region if it doesn't already exist.
34078      * @param {String} target The target region key (north, south, east, west or center).
34079      * @param {Object} config The regions config object
34080      * @return {BorderLayoutRegion} The new region
34081      */
34082     addRegion : function(config)
34083     {
34084         if(!this.regions[config.region]){
34085             var r = this.factory(config);
34086             this.bindRegion(r);
34087         }
34088         return this.regions[config.region];
34089     },
34090
34091     // private (kinda)
34092     bindRegion : function(r){
34093         this.regions[r.config.region] = r;
34094         
34095         r.on("visibilitychange",    this.layout, this);
34096         r.on("paneladded",          this.layout, this);
34097         r.on("panelremoved",        this.layout, this);
34098         r.on("invalidated",         this.layout, this);
34099         r.on("resized",             this.onRegionResized, this);
34100         r.on("collapsed",           this.onRegionCollapsed, this);
34101         r.on("expanded",            this.onRegionExpanded, this);
34102     },
34103
34104     /**
34105      * Performs a layout update.
34106      */
34107     layout : function()
34108     {
34109         if(this.updating) {
34110             return;
34111         }
34112         
34113         // render all the rebions if they have not been done alreayd?
34114         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34115             if(this.regions[region] && !this.regions[region].bodyEl){
34116                 this.regions[region].onRender(this.el)
34117             }
34118         },this);
34119         
34120         var size = this.getViewSize();
34121         var w = size.width;
34122         var h = size.height;
34123         var centerW = w;
34124         var centerH = h;
34125         var centerY = 0;
34126         var centerX = 0;
34127         //var x = 0, y = 0;
34128
34129         var rs = this.regions;
34130         var north = rs["north"];
34131         var south = rs["south"]; 
34132         var west = rs["west"];
34133         var east = rs["east"];
34134         var center = rs["center"];
34135         //if(this.hideOnLayout){ // not supported anymore
34136             //c.el.setStyle("display", "none");
34137         //}
34138         if(north && north.isVisible()){
34139             var b = north.getBox();
34140             var m = north.getMargins();
34141             b.width = w - (m.left+m.right);
34142             b.x = m.left;
34143             b.y = m.top;
34144             centerY = b.height + b.y + m.bottom;
34145             centerH -= centerY;
34146             north.updateBox(this.safeBox(b));
34147         }
34148         if(south && south.isVisible()){
34149             var b = south.getBox();
34150             var m = south.getMargins();
34151             b.width = w - (m.left+m.right);
34152             b.x = m.left;
34153             var totalHeight = (b.height + m.top + m.bottom);
34154             b.y = h - totalHeight + m.top;
34155             centerH -= totalHeight;
34156             south.updateBox(this.safeBox(b));
34157         }
34158         if(west && west.isVisible()){
34159             var b = west.getBox();
34160             var m = west.getMargins();
34161             b.height = centerH - (m.top+m.bottom);
34162             b.x = m.left;
34163             b.y = centerY + m.top;
34164             var totalWidth = (b.width + m.left + m.right);
34165             centerX += totalWidth;
34166             centerW -= totalWidth;
34167             west.updateBox(this.safeBox(b));
34168         }
34169         if(east && east.isVisible()){
34170             var b = east.getBox();
34171             var m = east.getMargins();
34172             b.height = centerH - (m.top+m.bottom);
34173             var totalWidth = (b.width + m.left + m.right);
34174             b.x = w - totalWidth + m.left;
34175             b.y = centerY + m.top;
34176             centerW -= totalWidth;
34177             east.updateBox(this.safeBox(b));
34178         }
34179         if(center){
34180             var m = center.getMargins();
34181             var centerBox = {
34182                 x: centerX + m.left,
34183                 y: centerY + m.top,
34184                 width: centerW - (m.left+m.right),
34185                 height: centerH - (m.top+m.bottom)
34186             };
34187             //if(this.hideOnLayout){
34188                 //center.el.setStyle("display", "block");
34189             //}
34190             center.updateBox(this.safeBox(centerBox));
34191         }
34192         this.el.repaint();
34193         this.fireEvent("layout", this);
34194     },
34195
34196     // private
34197     safeBox : function(box){
34198         box.width = Math.max(0, box.width);
34199         box.height = Math.max(0, box.height);
34200         return box;
34201     },
34202
34203     /**
34204      * Adds a ContentPanel (or subclass) to this layout.
34205      * @param {String} target The target region key (north, south, east, west or center).
34206      * @param {Roo.ContentPanel} panel The panel to add
34207      * @return {Roo.ContentPanel} The added panel
34208      */
34209     add : function(target, panel){
34210          
34211         target = target.toLowerCase();
34212         return this.regions[target].add(panel);
34213     },
34214
34215     /**
34216      * Remove a ContentPanel (or subclass) to this layout.
34217      * @param {String} target The target region key (north, south, east, west or center).
34218      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34219      * @return {Roo.ContentPanel} The removed panel
34220      */
34221     remove : function(target, panel){
34222         target = target.toLowerCase();
34223         return this.regions[target].remove(panel);
34224     },
34225
34226     /**
34227      * Searches all regions for a panel with the specified id
34228      * @param {String} panelId
34229      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34230      */
34231     findPanel : function(panelId){
34232         var rs = this.regions;
34233         for(var target in rs){
34234             if(typeof rs[target] != "function"){
34235                 var p = rs[target].getPanel(panelId);
34236                 if(p){
34237                     return p;
34238                 }
34239             }
34240         }
34241         return null;
34242     },
34243
34244     /**
34245      * Searches all regions for a panel with the specified id and activates (shows) it.
34246      * @param {String/ContentPanel} panelId The panels id or the panel itself
34247      * @return {Roo.ContentPanel} The shown panel or null
34248      */
34249     showPanel : function(panelId) {
34250       var rs = this.regions;
34251       for(var target in rs){
34252          var r = rs[target];
34253          if(typeof r != "function"){
34254             if(r.hasPanel(panelId)){
34255                return r.showPanel(panelId);
34256             }
34257          }
34258       }
34259       return null;
34260    },
34261
34262    /**
34263      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34264      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34265      */
34266    /*
34267     restoreState : function(provider){
34268         if(!provider){
34269             provider = Roo.state.Manager;
34270         }
34271         var sm = new Roo.LayoutStateManager();
34272         sm.init(this, provider);
34273     },
34274 */
34275  
34276  
34277     /**
34278      * Adds a xtype elements to the layout.
34279      * <pre><code>
34280
34281 layout.addxtype({
34282        xtype : 'ContentPanel',
34283        region: 'west',
34284        items: [ .... ]
34285    }
34286 );
34287
34288 layout.addxtype({
34289         xtype : 'NestedLayoutPanel',
34290         region: 'west',
34291         layout: {
34292            center: { },
34293            west: { }   
34294         },
34295         items : [ ... list of content panels or nested layout panels.. ]
34296    }
34297 );
34298 </code></pre>
34299      * @param {Object} cfg Xtype definition of item to add.
34300      */
34301     addxtype : function(cfg)
34302     {
34303         // basically accepts a pannel...
34304         // can accept a layout region..!?!?
34305         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34306         
34307         
34308         // theory?  children can only be panels??
34309         
34310         //if (!cfg.xtype.match(/Panel$/)) {
34311         //    return false;
34312         //}
34313         var ret = false;
34314         
34315         if (typeof(cfg.region) == 'undefined') {
34316             Roo.log("Failed to add Panel, region was not set");
34317             Roo.log(cfg);
34318             return false;
34319         }
34320         var region = cfg.region;
34321         delete cfg.region;
34322         
34323           
34324         var xitems = [];
34325         if (cfg.items) {
34326             xitems = cfg.items;
34327             delete cfg.items;
34328         }
34329         var nb = false;
34330         
34331         switch(cfg.xtype) 
34332         {
34333             case 'Content':  // ContentPanel (el, cfg)
34334             case 'Scroll':  // ContentPanel (el, cfg)
34335             case 'View': 
34336                 cfg.autoCreate = true;
34337                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34338                 //} else {
34339                 //    var el = this.el.createChild();
34340                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34341                 //}
34342                 
34343                 this.add(region, ret);
34344                 break;
34345             
34346             /*
34347             case 'TreePanel': // our new panel!
34348                 cfg.el = this.el.createChild();
34349                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34350                 this.add(region, ret);
34351                 break;
34352             */
34353             
34354             case 'Nest': 
34355                 // create a new Layout (which is  a Border Layout...
34356                 
34357                 var clayout = cfg.layout;
34358                 clayout.el  = this.el.createChild();
34359                 clayout.items   = clayout.items  || [];
34360                 
34361                 delete cfg.layout;
34362                 
34363                 // replace this exitems with the clayout ones..
34364                 xitems = clayout.items;
34365                  
34366                 // force background off if it's in center...
34367                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34368                     cfg.background = false;
34369                 }
34370                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34371                 
34372                 
34373                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34374                 //console.log('adding nested layout panel '  + cfg.toSource());
34375                 this.add(region, ret);
34376                 nb = {}; /// find first...
34377                 break;
34378             
34379             case 'Grid':
34380                 
34381                 // needs grid and region
34382                 
34383                 //var el = this.getRegion(region).el.createChild();
34384                 /*
34385                  *var el = this.el.createChild();
34386                 // create the grid first...
34387                 cfg.grid.container = el;
34388                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34389                 */
34390                 
34391                 if (region == 'center' && this.active ) {
34392                     cfg.background = false;
34393                 }
34394                 
34395                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34396                 
34397                 this.add(region, ret);
34398                 /*
34399                 if (cfg.background) {
34400                     // render grid on panel activation (if panel background)
34401                     ret.on('activate', function(gp) {
34402                         if (!gp.grid.rendered) {
34403                     //        gp.grid.render(el);
34404                         }
34405                     });
34406                 } else {
34407                   //  cfg.grid.render(el);
34408                 }
34409                 */
34410                 break;
34411            
34412            
34413             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34414                 // it was the old xcomponent building that caused this before.
34415                 // espeically if border is the top element in the tree.
34416                 ret = this;
34417                 break; 
34418                 
34419                     
34420                 
34421                 
34422                 
34423             default:
34424                 /*
34425                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34426                     
34427                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34428                     this.add(region, ret);
34429                 } else {
34430                 */
34431                     Roo.log(cfg);
34432                     throw "Can not add '" + cfg.xtype + "' to Border";
34433                     return null;
34434              
34435                                 
34436              
34437         }
34438         this.beginUpdate();
34439         // add children..
34440         var region = '';
34441         var abn = {};
34442         Roo.each(xitems, function(i)  {
34443             region = nb && i.region ? i.region : false;
34444             
34445             var add = ret.addxtype(i);
34446            
34447             if (region) {
34448                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34449                 if (!i.background) {
34450                     abn[region] = nb[region] ;
34451                 }
34452             }
34453             
34454         });
34455         this.endUpdate();
34456
34457         // make the last non-background panel active..
34458         //if (nb) { Roo.log(abn); }
34459         if (nb) {
34460             
34461             for(var r in abn) {
34462                 region = this.getRegion(r);
34463                 if (region) {
34464                     // tried using nb[r], but it does not work..
34465                      
34466                     region.showPanel(abn[r]);
34467                    
34468                 }
34469             }
34470         }
34471         return ret;
34472         
34473     },
34474     
34475     
34476 // private
34477     factory : function(cfg)
34478     {
34479         
34480         var validRegions = Roo.bootstrap.layout.Border.regions;
34481
34482         var target = cfg.region;
34483         cfg.mgr = this;
34484         
34485         var r = Roo.bootstrap.layout;
34486         Roo.log(target);
34487         switch(target){
34488             case "north":
34489                 return new r.North(cfg);
34490             case "south":
34491                 return new r.South(cfg);
34492             case "east":
34493                 return new r.East(cfg);
34494             case "west":
34495                 return new r.West(cfg);
34496             case "center":
34497                 return new r.Center(cfg);
34498         }
34499         throw 'Layout region "'+target+'" not supported.';
34500     }
34501     
34502     
34503 });
34504  /*
34505  * Based on:
34506  * Ext JS Library 1.1.1
34507  * Copyright(c) 2006-2007, Ext JS, LLC.
34508  *
34509  * Originally Released Under LGPL - original licence link has changed is not relivant.
34510  *
34511  * Fork - LGPL
34512  * <script type="text/javascript">
34513  */
34514  
34515 /**
34516  * @class Roo.bootstrap.layout.Basic
34517  * @extends Roo.util.Observable
34518  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34519  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34520  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34521  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34522  * @cfg {string}   region  the region that it inhabits..
34523  * @cfg {bool}   skipConfig skip config?
34524  * 
34525
34526  */
34527 Roo.bootstrap.layout.Basic = function(config){
34528     
34529     this.mgr = config.mgr;
34530     
34531     this.position = config.region;
34532     
34533     var skipConfig = config.skipConfig;
34534     
34535     this.events = {
34536         /**
34537          * @scope Roo.BasicLayoutRegion
34538          */
34539         
34540         /**
34541          * @event beforeremove
34542          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34543          * @param {Roo.LayoutRegion} this
34544          * @param {Roo.ContentPanel} panel The panel
34545          * @param {Object} e The cancel event object
34546          */
34547         "beforeremove" : true,
34548         /**
34549          * @event invalidated
34550          * Fires when the layout for this region is changed.
34551          * @param {Roo.LayoutRegion} this
34552          */
34553         "invalidated" : true,
34554         /**
34555          * @event visibilitychange
34556          * Fires when this region is shown or hidden 
34557          * @param {Roo.LayoutRegion} this
34558          * @param {Boolean} visibility true or false
34559          */
34560         "visibilitychange" : true,
34561         /**
34562          * @event paneladded
34563          * Fires when a panel is added. 
34564          * @param {Roo.LayoutRegion} this
34565          * @param {Roo.ContentPanel} panel The panel
34566          */
34567         "paneladded" : true,
34568         /**
34569          * @event panelremoved
34570          * Fires when a panel is removed. 
34571          * @param {Roo.LayoutRegion} this
34572          * @param {Roo.ContentPanel} panel The panel
34573          */
34574         "panelremoved" : true,
34575         /**
34576          * @event beforecollapse
34577          * Fires when this region before collapse.
34578          * @param {Roo.LayoutRegion} this
34579          */
34580         "beforecollapse" : true,
34581         /**
34582          * @event collapsed
34583          * Fires when this region is collapsed.
34584          * @param {Roo.LayoutRegion} this
34585          */
34586         "collapsed" : true,
34587         /**
34588          * @event expanded
34589          * Fires when this region is expanded.
34590          * @param {Roo.LayoutRegion} this
34591          */
34592         "expanded" : true,
34593         /**
34594          * @event slideshow
34595          * Fires when this region is slid into view.
34596          * @param {Roo.LayoutRegion} this
34597          */
34598         "slideshow" : true,
34599         /**
34600          * @event slidehide
34601          * Fires when this region slides out of view. 
34602          * @param {Roo.LayoutRegion} this
34603          */
34604         "slidehide" : true,
34605         /**
34606          * @event panelactivated
34607          * Fires when a panel is activated. 
34608          * @param {Roo.LayoutRegion} this
34609          * @param {Roo.ContentPanel} panel The activated panel
34610          */
34611         "panelactivated" : true,
34612         /**
34613          * @event resized
34614          * Fires when the user resizes this region. 
34615          * @param {Roo.LayoutRegion} this
34616          * @param {Number} newSize The new size (width for east/west, height for north/south)
34617          */
34618         "resized" : true
34619     };
34620     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34621     this.panels = new Roo.util.MixedCollection();
34622     this.panels.getKey = this.getPanelId.createDelegate(this);
34623     this.box = null;
34624     this.activePanel = null;
34625     // ensure listeners are added...
34626     
34627     if (config.listeners || config.events) {
34628         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34629             listeners : config.listeners || {},
34630             events : config.events || {}
34631         });
34632     }
34633     
34634     if(skipConfig !== true){
34635         this.applyConfig(config);
34636     }
34637 };
34638
34639 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34640 {
34641     getPanelId : function(p){
34642         return p.getId();
34643     },
34644     
34645     applyConfig : function(config){
34646         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34647         this.config = config;
34648         
34649     },
34650     
34651     /**
34652      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34653      * the width, for horizontal (north, south) the height.
34654      * @param {Number} newSize The new width or height
34655      */
34656     resizeTo : function(newSize){
34657         var el = this.el ? this.el :
34658                  (this.activePanel ? this.activePanel.getEl() : null);
34659         if(el){
34660             switch(this.position){
34661                 case "east":
34662                 case "west":
34663                     el.setWidth(newSize);
34664                     this.fireEvent("resized", this, newSize);
34665                 break;
34666                 case "north":
34667                 case "south":
34668                     el.setHeight(newSize);
34669                     this.fireEvent("resized", this, newSize);
34670                 break;                
34671             }
34672         }
34673     },
34674     
34675     getBox : function(){
34676         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34677     },
34678     
34679     getMargins : function(){
34680         return this.margins;
34681     },
34682     
34683     updateBox : function(box){
34684         this.box = box;
34685         var el = this.activePanel.getEl();
34686         el.dom.style.left = box.x + "px";
34687         el.dom.style.top = box.y + "px";
34688         this.activePanel.setSize(box.width, box.height);
34689     },
34690     
34691     /**
34692      * Returns the container element for this region.
34693      * @return {Roo.Element}
34694      */
34695     getEl : function(){
34696         return this.activePanel;
34697     },
34698     
34699     /**
34700      * Returns true if this region is currently visible.
34701      * @return {Boolean}
34702      */
34703     isVisible : function(){
34704         return this.activePanel ? true : false;
34705     },
34706     
34707     setActivePanel : function(panel){
34708         panel = this.getPanel(panel);
34709         if(this.activePanel && this.activePanel != panel){
34710             this.activePanel.setActiveState(false);
34711             this.activePanel.getEl().setLeftTop(-10000,-10000);
34712         }
34713         this.activePanel = panel;
34714         panel.setActiveState(true);
34715         if(this.box){
34716             panel.setSize(this.box.width, this.box.height);
34717         }
34718         this.fireEvent("panelactivated", this, panel);
34719         this.fireEvent("invalidated");
34720     },
34721     
34722     /**
34723      * Show the specified panel.
34724      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34725      * @return {Roo.ContentPanel} The shown panel or null
34726      */
34727     showPanel : function(panel){
34728         panel = this.getPanel(panel);
34729         if(panel){
34730             this.setActivePanel(panel);
34731         }
34732         return panel;
34733     },
34734     
34735     /**
34736      * Get the active panel for this region.
34737      * @return {Roo.ContentPanel} The active panel or null
34738      */
34739     getActivePanel : function(){
34740         return this.activePanel;
34741     },
34742     
34743     /**
34744      * Add the passed ContentPanel(s)
34745      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34746      * @return {Roo.ContentPanel} The panel added (if only one was added)
34747      */
34748     add : function(panel){
34749         if(arguments.length > 1){
34750             for(var i = 0, len = arguments.length; i < len; i++) {
34751                 this.add(arguments[i]);
34752             }
34753             return null;
34754         }
34755         if(this.hasPanel(panel)){
34756             this.showPanel(panel);
34757             return panel;
34758         }
34759         var el = panel.getEl();
34760         if(el.dom.parentNode != this.mgr.el.dom){
34761             this.mgr.el.dom.appendChild(el.dom);
34762         }
34763         if(panel.setRegion){
34764             panel.setRegion(this);
34765         }
34766         this.panels.add(panel);
34767         el.setStyle("position", "absolute");
34768         if(!panel.background){
34769             this.setActivePanel(panel);
34770             if(this.config.initialSize && this.panels.getCount()==1){
34771                 this.resizeTo(this.config.initialSize);
34772             }
34773         }
34774         this.fireEvent("paneladded", this, panel);
34775         return panel;
34776     },
34777     
34778     /**
34779      * Returns true if the panel is in this region.
34780      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34781      * @return {Boolean}
34782      */
34783     hasPanel : function(panel){
34784         if(typeof panel == "object"){ // must be panel obj
34785             panel = panel.getId();
34786         }
34787         return this.getPanel(panel) ? true : false;
34788     },
34789     
34790     /**
34791      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34792      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34793      * @param {Boolean} preservePanel Overrides the config preservePanel option
34794      * @return {Roo.ContentPanel} The panel that was removed
34795      */
34796     remove : function(panel, preservePanel){
34797         panel = this.getPanel(panel);
34798         if(!panel){
34799             return null;
34800         }
34801         var e = {};
34802         this.fireEvent("beforeremove", this, panel, e);
34803         if(e.cancel === true){
34804             return null;
34805         }
34806         var panelId = panel.getId();
34807         this.panels.removeKey(panelId);
34808         return panel;
34809     },
34810     
34811     /**
34812      * Returns the panel specified or null if it's not in this region.
34813      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34814      * @return {Roo.ContentPanel}
34815      */
34816     getPanel : function(id){
34817         if(typeof id == "object"){ // must be panel obj
34818             return id;
34819         }
34820         return this.panels.get(id);
34821     },
34822     
34823     /**
34824      * Returns this regions position (north/south/east/west/center).
34825      * @return {String} 
34826      */
34827     getPosition: function(){
34828         return this.position;    
34829     }
34830 });/*
34831  * Based on:
34832  * Ext JS Library 1.1.1
34833  * Copyright(c) 2006-2007, Ext JS, LLC.
34834  *
34835  * Originally Released Under LGPL - original licence link has changed is not relivant.
34836  *
34837  * Fork - LGPL
34838  * <script type="text/javascript">
34839  */
34840  
34841 /**
34842  * @class Roo.bootstrap.layout.Region
34843  * @extends Roo.bootstrap.layout.Basic
34844  * This class represents a region in a layout manager.
34845  
34846  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34847  * @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})
34848  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34849  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34850  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34851  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34852  * @cfg {String}    title           The title for the region (overrides panel titles)
34853  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34854  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34855  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34856  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34857  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34858  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34859  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34860  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34861  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34862  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34863
34864  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34865  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34866  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34867  * @cfg {Number}    width           For East/West panels
34868  * @cfg {Number}    height          For North/South panels
34869  * @cfg {Boolean}   split           To show the splitter
34870  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34871  * 
34872  * @cfg {string}   cls             Extra CSS classes to add to region
34873  * 
34874  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34875  * @cfg {string}   region  the region that it inhabits..
34876  *
34877
34878  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34879  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34880
34881  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34882  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34883  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34884  */
34885 Roo.bootstrap.layout.Region = function(config)
34886 {
34887     this.applyConfig(config);
34888
34889     var mgr = config.mgr;
34890     var pos = config.region;
34891     config.skipConfig = true;
34892     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34893     
34894     if (mgr.el) {
34895         this.onRender(mgr.el);   
34896     }
34897      
34898     this.visible = true;
34899     this.collapsed = false;
34900     this.unrendered_panels = [];
34901 };
34902
34903 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34904
34905     position: '', // set by wrapper (eg. north/south etc..)
34906     unrendered_panels : null,  // unrendered panels.
34907     createBody : function(){
34908         /** This region's body element 
34909         * @type Roo.Element */
34910         this.bodyEl = this.el.createChild({
34911                 tag: "div",
34912                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34913         });
34914     },
34915
34916     onRender: function(ctr, pos)
34917     {
34918         var dh = Roo.DomHelper;
34919         /** This region's container element 
34920         * @type Roo.Element */
34921         this.el = dh.append(ctr.dom, {
34922                 tag: "div",
34923                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34924             }, true);
34925         /** This region's title element 
34926         * @type Roo.Element */
34927     
34928         this.titleEl = dh.append(this.el.dom,
34929             {
34930                     tag: "div",
34931                     unselectable: "on",
34932                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34933                     children:[
34934                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34935                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34936                     ]}, true);
34937         
34938         this.titleEl.enableDisplayMode();
34939         /** This region's title text element 
34940         * @type HTMLElement */
34941         this.titleTextEl = this.titleEl.dom.firstChild;
34942         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34943         /*
34944         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34945         this.closeBtn.enableDisplayMode();
34946         this.closeBtn.on("click", this.closeClicked, this);
34947         this.closeBtn.hide();
34948     */
34949         this.createBody(this.config);
34950         if(this.config.hideWhenEmpty){
34951             this.hide();
34952             this.on("paneladded", this.validateVisibility, this);
34953             this.on("panelremoved", this.validateVisibility, this);
34954         }
34955         if(this.autoScroll){
34956             this.bodyEl.setStyle("overflow", "auto");
34957         }else{
34958             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34959         }
34960         //if(c.titlebar !== false){
34961             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34962                 this.titleEl.hide();
34963             }else{
34964                 this.titleEl.show();
34965                 if(this.config.title){
34966                     this.titleTextEl.innerHTML = this.config.title;
34967                 }
34968             }
34969         //}
34970         if(this.config.collapsed){
34971             this.collapse(true);
34972         }
34973         if(this.config.hidden){
34974             this.hide();
34975         }
34976         
34977         if (this.unrendered_panels && this.unrendered_panels.length) {
34978             for (var i =0;i< this.unrendered_panels.length; i++) {
34979                 this.add(this.unrendered_panels[i]);
34980             }
34981             this.unrendered_panels = null;
34982             
34983         }
34984         
34985     },
34986     
34987     applyConfig : function(c)
34988     {
34989         /*
34990          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34991             var dh = Roo.DomHelper;
34992             if(c.titlebar !== false){
34993                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34994                 this.collapseBtn.on("click", this.collapse, this);
34995                 this.collapseBtn.enableDisplayMode();
34996                 /*
34997                 if(c.showPin === true || this.showPin){
34998                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34999                     this.stickBtn.enableDisplayMode();
35000                     this.stickBtn.on("click", this.expand, this);
35001                     this.stickBtn.hide();
35002                 }
35003                 
35004             }
35005             */
35006             /** This region's collapsed element
35007             * @type Roo.Element */
35008             /*
35009              *
35010             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35011                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35012             ]}, true);
35013             
35014             if(c.floatable !== false){
35015                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35016                this.collapsedEl.on("click", this.collapseClick, this);
35017             }
35018
35019             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35020                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35021                    id: "message", unselectable: "on", style:{"float":"left"}});
35022                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35023              }
35024             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35025             this.expandBtn.on("click", this.expand, this);
35026             
35027         }
35028         
35029         if(this.collapseBtn){
35030             this.collapseBtn.setVisible(c.collapsible == true);
35031         }
35032         
35033         this.cmargins = c.cmargins || this.cmargins ||
35034                          (this.position == "west" || this.position == "east" ?
35035                              {top: 0, left: 2, right:2, bottom: 0} :
35036                              {top: 2, left: 0, right:0, bottom: 2});
35037         */
35038         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35039         
35040         
35041         this.bottomTabs = c.tabPosition != "top";
35042         
35043         this.autoScroll = c.autoScroll || false;
35044         
35045         
35046        
35047         
35048         this.duration = c.duration || .30;
35049         this.slideDuration = c.slideDuration || .45;
35050         this.config = c;
35051        
35052     },
35053     /**
35054      * Returns true if this region is currently visible.
35055      * @return {Boolean}
35056      */
35057     isVisible : function(){
35058         return this.visible;
35059     },
35060
35061     /**
35062      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35063      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35064      */
35065     //setCollapsedTitle : function(title){
35066     //    title = title || "&#160;";
35067      //   if(this.collapsedTitleTextEl){
35068       //      this.collapsedTitleTextEl.innerHTML = title;
35069        // }
35070     //},
35071
35072     getBox : function(){
35073         var b;
35074       //  if(!this.collapsed){
35075             b = this.el.getBox(false, true);
35076        // }else{
35077           //  b = this.collapsedEl.getBox(false, true);
35078         //}
35079         return b;
35080     },
35081
35082     getMargins : function(){
35083         return this.margins;
35084         //return this.collapsed ? this.cmargins : this.margins;
35085     },
35086 /*
35087     highlight : function(){
35088         this.el.addClass("x-layout-panel-dragover");
35089     },
35090
35091     unhighlight : function(){
35092         this.el.removeClass("x-layout-panel-dragover");
35093     },
35094 */
35095     updateBox : function(box)
35096     {
35097         if (!this.bodyEl) {
35098             return; // not rendered yet..
35099         }
35100         
35101         this.box = box;
35102         if(!this.collapsed){
35103             this.el.dom.style.left = box.x + "px";
35104             this.el.dom.style.top = box.y + "px";
35105             this.updateBody(box.width, box.height);
35106         }else{
35107             this.collapsedEl.dom.style.left = box.x + "px";
35108             this.collapsedEl.dom.style.top = box.y + "px";
35109             this.collapsedEl.setSize(box.width, box.height);
35110         }
35111         if(this.tabs){
35112             this.tabs.autoSizeTabs();
35113         }
35114     },
35115
35116     updateBody : function(w, h)
35117     {
35118         if(w !== null){
35119             this.el.setWidth(w);
35120             w -= this.el.getBorderWidth("rl");
35121             if(this.config.adjustments){
35122                 w += this.config.adjustments[0];
35123             }
35124         }
35125         if(h !== null && h > 0){
35126             this.el.setHeight(h);
35127             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35128             h -= this.el.getBorderWidth("tb");
35129             if(this.config.adjustments){
35130                 h += this.config.adjustments[1];
35131             }
35132             this.bodyEl.setHeight(h);
35133             if(this.tabs){
35134                 h = this.tabs.syncHeight(h);
35135             }
35136         }
35137         if(this.panelSize){
35138             w = w !== null ? w : this.panelSize.width;
35139             h = h !== null ? h : this.panelSize.height;
35140         }
35141         if(this.activePanel){
35142             var el = this.activePanel.getEl();
35143             w = w !== null ? w : el.getWidth();
35144             h = h !== null ? h : el.getHeight();
35145             this.panelSize = {width: w, height: h};
35146             this.activePanel.setSize(w, h);
35147         }
35148         if(Roo.isIE && this.tabs){
35149             this.tabs.el.repaint();
35150         }
35151     },
35152
35153     /**
35154      * Returns the container element for this region.
35155      * @return {Roo.Element}
35156      */
35157     getEl : function(){
35158         return this.el;
35159     },
35160
35161     /**
35162      * Hides this region.
35163      */
35164     hide : function(){
35165         //if(!this.collapsed){
35166             this.el.dom.style.left = "-2000px";
35167             this.el.hide();
35168         //}else{
35169          //   this.collapsedEl.dom.style.left = "-2000px";
35170          //   this.collapsedEl.hide();
35171        // }
35172         this.visible = false;
35173         this.fireEvent("visibilitychange", this, false);
35174     },
35175
35176     /**
35177      * Shows this region if it was previously hidden.
35178      */
35179     show : function(){
35180         //if(!this.collapsed){
35181             this.el.show();
35182         //}else{
35183         //    this.collapsedEl.show();
35184        // }
35185         this.visible = true;
35186         this.fireEvent("visibilitychange", this, true);
35187     },
35188 /*
35189     closeClicked : function(){
35190         if(this.activePanel){
35191             this.remove(this.activePanel);
35192         }
35193     },
35194
35195     collapseClick : function(e){
35196         if(this.isSlid){
35197            e.stopPropagation();
35198            this.slideIn();
35199         }else{
35200            e.stopPropagation();
35201            this.slideOut();
35202         }
35203     },
35204 */
35205     /**
35206      * Collapses this region.
35207      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35208      */
35209     /*
35210     collapse : function(skipAnim, skipCheck = false){
35211         if(this.collapsed) {
35212             return;
35213         }
35214         
35215         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35216             
35217             this.collapsed = true;
35218             if(this.split){
35219                 this.split.el.hide();
35220             }
35221             if(this.config.animate && skipAnim !== true){
35222                 this.fireEvent("invalidated", this);
35223                 this.animateCollapse();
35224             }else{
35225                 this.el.setLocation(-20000,-20000);
35226                 this.el.hide();
35227                 this.collapsedEl.show();
35228                 this.fireEvent("collapsed", this);
35229                 this.fireEvent("invalidated", this);
35230             }
35231         }
35232         
35233     },
35234 */
35235     animateCollapse : function(){
35236         // overridden
35237     },
35238
35239     /**
35240      * Expands this region if it was previously collapsed.
35241      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35242      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35243      */
35244     /*
35245     expand : function(e, skipAnim){
35246         if(e) {
35247             e.stopPropagation();
35248         }
35249         if(!this.collapsed || this.el.hasActiveFx()) {
35250             return;
35251         }
35252         if(this.isSlid){
35253             this.afterSlideIn();
35254             skipAnim = true;
35255         }
35256         this.collapsed = false;
35257         if(this.config.animate && skipAnim !== true){
35258             this.animateExpand();
35259         }else{
35260             this.el.show();
35261             if(this.split){
35262                 this.split.el.show();
35263             }
35264             this.collapsedEl.setLocation(-2000,-2000);
35265             this.collapsedEl.hide();
35266             this.fireEvent("invalidated", this);
35267             this.fireEvent("expanded", this);
35268         }
35269     },
35270 */
35271     animateExpand : function(){
35272         // overridden
35273     },
35274
35275     initTabs : function()
35276     {
35277         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35278         
35279         var ts = new Roo.bootstrap.panel.Tabs({
35280                 el: this.bodyEl.dom,
35281                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35282                 disableTooltips: this.config.disableTabTips,
35283                 toolbar : this.config.toolbar
35284             });
35285         
35286         if(this.config.hideTabs){
35287             ts.stripWrap.setDisplayed(false);
35288         }
35289         this.tabs = ts;
35290         ts.resizeTabs = this.config.resizeTabs === true;
35291         ts.minTabWidth = this.config.minTabWidth || 40;
35292         ts.maxTabWidth = this.config.maxTabWidth || 250;
35293         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35294         ts.monitorResize = false;
35295         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35296         ts.bodyEl.addClass('roo-layout-tabs-body');
35297         this.panels.each(this.initPanelAsTab, this);
35298     },
35299
35300     initPanelAsTab : function(panel){
35301         var ti = this.tabs.addTab(
35302             panel.getEl().id,
35303             panel.getTitle(),
35304             null,
35305             this.config.closeOnTab && panel.isClosable(),
35306             panel.tpl
35307         );
35308         if(panel.tabTip !== undefined){
35309             ti.setTooltip(panel.tabTip);
35310         }
35311         ti.on("activate", function(){
35312               this.setActivePanel(panel);
35313         }, this);
35314         
35315         if(this.config.closeOnTab){
35316             ti.on("beforeclose", function(t, e){
35317                 e.cancel = true;
35318                 this.remove(panel);
35319             }, this);
35320         }
35321         
35322         panel.tabItem = ti;
35323         
35324         return ti;
35325     },
35326
35327     updatePanelTitle : function(panel, title)
35328     {
35329         if(this.activePanel == panel){
35330             this.updateTitle(title);
35331         }
35332         if(this.tabs){
35333             var ti = this.tabs.getTab(panel.getEl().id);
35334             ti.setText(title);
35335             if(panel.tabTip !== undefined){
35336                 ti.setTooltip(panel.tabTip);
35337             }
35338         }
35339     },
35340
35341     updateTitle : function(title){
35342         if(this.titleTextEl && !this.config.title){
35343             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35344         }
35345     },
35346
35347     setActivePanel : function(panel)
35348     {
35349         panel = this.getPanel(panel);
35350         if(this.activePanel && this.activePanel != panel){
35351             this.activePanel.setActiveState(false);
35352         }
35353         this.activePanel = panel;
35354         panel.setActiveState(true);
35355         if(this.panelSize){
35356             panel.setSize(this.panelSize.width, this.panelSize.height);
35357         }
35358         if(this.closeBtn){
35359             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35360         }
35361         this.updateTitle(panel.getTitle());
35362         if(this.tabs){
35363             this.fireEvent("invalidated", this);
35364         }
35365         this.fireEvent("panelactivated", this, panel);
35366     },
35367
35368     /**
35369      * Shows the specified panel.
35370      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35371      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35372      */
35373     showPanel : function(panel)
35374     {
35375         panel = this.getPanel(panel);
35376         if(panel){
35377             if(this.tabs){
35378                 var tab = this.tabs.getTab(panel.getEl().id);
35379                 if(tab.isHidden()){
35380                     this.tabs.unhideTab(tab.id);
35381                 }
35382                 tab.activate();
35383             }else{
35384                 this.setActivePanel(panel);
35385             }
35386         }
35387         return panel;
35388     },
35389
35390     /**
35391      * Get the active panel for this region.
35392      * @return {Roo.ContentPanel} The active panel or null
35393      */
35394     getActivePanel : function(){
35395         return this.activePanel;
35396     },
35397
35398     validateVisibility : function(){
35399         if(this.panels.getCount() < 1){
35400             this.updateTitle("&#160;");
35401             this.closeBtn.hide();
35402             this.hide();
35403         }else{
35404             if(!this.isVisible()){
35405                 this.show();
35406             }
35407         }
35408     },
35409
35410     /**
35411      * Adds the passed ContentPanel(s) to this region.
35412      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35413      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35414      */
35415     add : function(panel)
35416     {
35417         if(arguments.length > 1){
35418             for(var i = 0, len = arguments.length; i < len; i++) {
35419                 this.add(arguments[i]);
35420             }
35421             return null;
35422         }
35423         
35424         // if we have not been rendered yet, then we can not really do much of this..
35425         if (!this.bodyEl) {
35426             this.unrendered_panels.push(panel);
35427             return panel;
35428         }
35429         
35430         
35431         
35432         
35433         if(this.hasPanel(panel)){
35434             this.showPanel(panel);
35435             return panel;
35436         }
35437         panel.setRegion(this);
35438         this.panels.add(panel);
35439        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35440             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35441             // and hide them... ???
35442             this.bodyEl.dom.appendChild(panel.getEl().dom);
35443             if(panel.background !== true){
35444                 this.setActivePanel(panel);
35445             }
35446             this.fireEvent("paneladded", this, panel);
35447             return panel;
35448         }
35449         */
35450         if(!this.tabs){
35451             this.initTabs();
35452         }else{
35453             this.initPanelAsTab(panel);
35454         }
35455         
35456         
35457         if(panel.background !== true){
35458             this.tabs.activate(panel.getEl().id);
35459         }
35460         this.fireEvent("paneladded", this, panel);
35461         return panel;
35462     },
35463
35464     /**
35465      * Hides the tab for the specified panel.
35466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35467      */
35468     hidePanel : function(panel){
35469         if(this.tabs && (panel = this.getPanel(panel))){
35470             this.tabs.hideTab(panel.getEl().id);
35471         }
35472     },
35473
35474     /**
35475      * Unhides the tab for a previously hidden panel.
35476      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35477      */
35478     unhidePanel : function(panel){
35479         if(this.tabs && (panel = this.getPanel(panel))){
35480             this.tabs.unhideTab(panel.getEl().id);
35481         }
35482     },
35483
35484     clearPanels : function(){
35485         while(this.panels.getCount() > 0){
35486              this.remove(this.panels.first());
35487         }
35488     },
35489
35490     /**
35491      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35492      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35493      * @param {Boolean} preservePanel Overrides the config preservePanel option
35494      * @return {Roo.ContentPanel} The panel that was removed
35495      */
35496     remove : function(panel, preservePanel)
35497     {
35498         panel = this.getPanel(panel);
35499         if(!panel){
35500             return null;
35501         }
35502         var e = {};
35503         this.fireEvent("beforeremove", this, panel, e);
35504         if(e.cancel === true){
35505             return null;
35506         }
35507         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35508         var panelId = panel.getId();
35509         this.panels.removeKey(panelId);
35510         if(preservePanel){
35511             document.body.appendChild(panel.getEl().dom);
35512         }
35513         if(this.tabs){
35514             this.tabs.removeTab(panel.getEl().id);
35515         }else if (!preservePanel){
35516             this.bodyEl.dom.removeChild(panel.getEl().dom);
35517         }
35518         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35519             var p = this.panels.first();
35520             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35521             tempEl.appendChild(p.getEl().dom);
35522             this.bodyEl.update("");
35523             this.bodyEl.dom.appendChild(p.getEl().dom);
35524             tempEl = null;
35525             this.updateTitle(p.getTitle());
35526             this.tabs = null;
35527             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35528             this.setActivePanel(p);
35529         }
35530         panel.setRegion(null);
35531         if(this.activePanel == panel){
35532             this.activePanel = null;
35533         }
35534         if(this.config.autoDestroy !== false && preservePanel !== true){
35535             try{panel.destroy();}catch(e){}
35536         }
35537         this.fireEvent("panelremoved", this, panel);
35538         return panel;
35539     },
35540
35541     /**
35542      * Returns the TabPanel component used by this region
35543      * @return {Roo.TabPanel}
35544      */
35545     getTabs : function(){
35546         return this.tabs;
35547     },
35548
35549     createTool : function(parentEl, className){
35550         var btn = Roo.DomHelper.append(parentEl, {
35551             tag: "div",
35552             cls: "x-layout-tools-button",
35553             children: [ {
35554                 tag: "div",
35555                 cls: "roo-layout-tools-button-inner " + className,
35556                 html: "&#160;"
35557             }]
35558         }, true);
35559         btn.addClassOnOver("roo-layout-tools-button-over");
35560         return btn;
35561     }
35562 });/*
35563  * Based on:
35564  * Ext JS Library 1.1.1
35565  * Copyright(c) 2006-2007, Ext JS, LLC.
35566  *
35567  * Originally Released Under LGPL - original licence link has changed is not relivant.
35568  *
35569  * Fork - LGPL
35570  * <script type="text/javascript">
35571  */
35572  
35573
35574
35575 /**
35576  * @class Roo.SplitLayoutRegion
35577  * @extends Roo.LayoutRegion
35578  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35579  */
35580 Roo.bootstrap.layout.Split = function(config){
35581     this.cursor = config.cursor;
35582     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35583 };
35584
35585 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35586 {
35587     splitTip : "Drag to resize.",
35588     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35589     useSplitTips : false,
35590
35591     applyConfig : function(config){
35592         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35593     },
35594     
35595     onRender : function(ctr,pos) {
35596         
35597         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35598         if(!this.config.split){
35599             return;
35600         }
35601         if(!this.split){
35602             
35603             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35604                             tag: "div",
35605                             id: this.el.id + "-split",
35606                             cls: "roo-layout-split roo-layout-split-"+this.position,
35607                             html: "&#160;"
35608             });
35609             /** The SplitBar for this region 
35610             * @type Roo.SplitBar */
35611             // does not exist yet...
35612             Roo.log([this.position, this.orientation]);
35613             
35614             this.split = new Roo.bootstrap.SplitBar({
35615                 dragElement : splitEl,
35616                 resizingElement: this.el,
35617                 orientation : this.orientation
35618             });
35619             
35620             this.split.on("moved", this.onSplitMove, this);
35621             this.split.useShim = this.config.useShim === true;
35622             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35623             if(this.useSplitTips){
35624                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35625             }
35626             //if(config.collapsible){
35627             //    this.split.el.on("dblclick", this.collapse,  this);
35628             //}
35629         }
35630         if(typeof this.config.minSize != "undefined"){
35631             this.split.minSize = this.config.minSize;
35632         }
35633         if(typeof this.config.maxSize != "undefined"){
35634             this.split.maxSize = this.config.maxSize;
35635         }
35636         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35637             this.hideSplitter();
35638         }
35639         
35640     },
35641
35642     getHMaxSize : function(){
35643          var cmax = this.config.maxSize || 10000;
35644          var center = this.mgr.getRegion("center");
35645          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35646     },
35647
35648     getVMaxSize : function(){
35649          var cmax = this.config.maxSize || 10000;
35650          var center = this.mgr.getRegion("center");
35651          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35652     },
35653
35654     onSplitMove : function(split, newSize){
35655         this.fireEvent("resized", this, newSize);
35656     },
35657     
35658     /** 
35659      * Returns the {@link Roo.SplitBar} for this region.
35660      * @return {Roo.SplitBar}
35661      */
35662     getSplitBar : function(){
35663         return this.split;
35664     },
35665     
35666     hide : function(){
35667         this.hideSplitter();
35668         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35669     },
35670
35671     hideSplitter : function(){
35672         if(this.split){
35673             this.split.el.setLocation(-2000,-2000);
35674             this.split.el.hide();
35675         }
35676     },
35677
35678     show : function(){
35679         if(this.split){
35680             this.split.el.show();
35681         }
35682         Roo.bootstrap.layout.Split.superclass.show.call(this);
35683     },
35684     
35685     beforeSlide: function(){
35686         if(Roo.isGecko){// firefox overflow auto bug workaround
35687             this.bodyEl.clip();
35688             if(this.tabs) {
35689                 this.tabs.bodyEl.clip();
35690             }
35691             if(this.activePanel){
35692                 this.activePanel.getEl().clip();
35693                 
35694                 if(this.activePanel.beforeSlide){
35695                     this.activePanel.beforeSlide();
35696                 }
35697             }
35698         }
35699     },
35700     
35701     afterSlide : function(){
35702         if(Roo.isGecko){// firefox overflow auto bug workaround
35703             this.bodyEl.unclip();
35704             if(this.tabs) {
35705                 this.tabs.bodyEl.unclip();
35706             }
35707             if(this.activePanel){
35708                 this.activePanel.getEl().unclip();
35709                 if(this.activePanel.afterSlide){
35710                     this.activePanel.afterSlide();
35711                 }
35712             }
35713         }
35714     },
35715
35716     initAutoHide : function(){
35717         if(this.autoHide !== false){
35718             if(!this.autoHideHd){
35719                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35720                 this.autoHideHd = {
35721                     "mouseout": function(e){
35722                         if(!e.within(this.el, true)){
35723                             st.delay(500);
35724                         }
35725                     },
35726                     "mouseover" : function(e){
35727                         st.cancel();
35728                     },
35729                     scope : this
35730                 };
35731             }
35732             this.el.on(this.autoHideHd);
35733         }
35734     },
35735
35736     clearAutoHide : function(){
35737         if(this.autoHide !== false){
35738             this.el.un("mouseout", this.autoHideHd.mouseout);
35739             this.el.un("mouseover", this.autoHideHd.mouseover);
35740         }
35741     },
35742
35743     clearMonitor : function(){
35744         Roo.get(document).un("click", this.slideInIf, this);
35745     },
35746
35747     // these names are backwards but not changed for compat
35748     slideOut : function(){
35749         if(this.isSlid || this.el.hasActiveFx()){
35750             return;
35751         }
35752         this.isSlid = true;
35753         if(this.collapseBtn){
35754             this.collapseBtn.hide();
35755         }
35756         this.closeBtnState = this.closeBtn.getStyle('display');
35757         this.closeBtn.hide();
35758         if(this.stickBtn){
35759             this.stickBtn.show();
35760         }
35761         this.el.show();
35762         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35763         this.beforeSlide();
35764         this.el.setStyle("z-index", 10001);
35765         this.el.slideIn(this.getSlideAnchor(), {
35766             callback: function(){
35767                 this.afterSlide();
35768                 this.initAutoHide();
35769                 Roo.get(document).on("click", this.slideInIf, this);
35770                 this.fireEvent("slideshow", this);
35771             },
35772             scope: this,
35773             block: true
35774         });
35775     },
35776
35777     afterSlideIn : function(){
35778         this.clearAutoHide();
35779         this.isSlid = false;
35780         this.clearMonitor();
35781         this.el.setStyle("z-index", "");
35782         if(this.collapseBtn){
35783             this.collapseBtn.show();
35784         }
35785         this.closeBtn.setStyle('display', this.closeBtnState);
35786         if(this.stickBtn){
35787             this.stickBtn.hide();
35788         }
35789         this.fireEvent("slidehide", this);
35790     },
35791
35792     slideIn : function(cb){
35793         if(!this.isSlid || this.el.hasActiveFx()){
35794             Roo.callback(cb);
35795             return;
35796         }
35797         this.isSlid = false;
35798         this.beforeSlide();
35799         this.el.slideOut(this.getSlideAnchor(), {
35800             callback: function(){
35801                 this.el.setLeftTop(-10000, -10000);
35802                 this.afterSlide();
35803                 this.afterSlideIn();
35804                 Roo.callback(cb);
35805             },
35806             scope: this,
35807             block: true
35808         });
35809     },
35810     
35811     slideInIf : function(e){
35812         if(!e.within(this.el)){
35813             this.slideIn();
35814         }
35815     },
35816
35817     animateCollapse : function(){
35818         this.beforeSlide();
35819         this.el.setStyle("z-index", 20000);
35820         var anchor = this.getSlideAnchor();
35821         this.el.slideOut(anchor, {
35822             callback : function(){
35823                 this.el.setStyle("z-index", "");
35824                 this.collapsedEl.slideIn(anchor, {duration:.3});
35825                 this.afterSlide();
35826                 this.el.setLocation(-10000,-10000);
35827                 this.el.hide();
35828                 this.fireEvent("collapsed", this);
35829             },
35830             scope: this,
35831             block: true
35832         });
35833     },
35834
35835     animateExpand : function(){
35836         this.beforeSlide();
35837         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35838         this.el.setStyle("z-index", 20000);
35839         this.collapsedEl.hide({
35840             duration:.1
35841         });
35842         this.el.slideIn(this.getSlideAnchor(), {
35843             callback : function(){
35844                 this.el.setStyle("z-index", "");
35845                 this.afterSlide();
35846                 if(this.split){
35847                     this.split.el.show();
35848                 }
35849                 this.fireEvent("invalidated", this);
35850                 this.fireEvent("expanded", this);
35851             },
35852             scope: this,
35853             block: true
35854         });
35855     },
35856
35857     anchors : {
35858         "west" : "left",
35859         "east" : "right",
35860         "north" : "top",
35861         "south" : "bottom"
35862     },
35863
35864     sanchors : {
35865         "west" : "l",
35866         "east" : "r",
35867         "north" : "t",
35868         "south" : "b"
35869     },
35870
35871     canchors : {
35872         "west" : "tl-tr",
35873         "east" : "tr-tl",
35874         "north" : "tl-bl",
35875         "south" : "bl-tl"
35876     },
35877
35878     getAnchor : function(){
35879         return this.anchors[this.position];
35880     },
35881
35882     getCollapseAnchor : function(){
35883         return this.canchors[this.position];
35884     },
35885
35886     getSlideAnchor : function(){
35887         return this.sanchors[this.position];
35888     },
35889
35890     getAlignAdj : function(){
35891         var cm = this.cmargins;
35892         switch(this.position){
35893             case "west":
35894                 return [0, 0];
35895             break;
35896             case "east":
35897                 return [0, 0];
35898             break;
35899             case "north":
35900                 return [0, 0];
35901             break;
35902             case "south":
35903                 return [0, 0];
35904             break;
35905         }
35906     },
35907
35908     getExpandAdj : function(){
35909         var c = this.collapsedEl, cm = this.cmargins;
35910         switch(this.position){
35911             case "west":
35912                 return [-(cm.right+c.getWidth()+cm.left), 0];
35913             break;
35914             case "east":
35915                 return [cm.right+c.getWidth()+cm.left, 0];
35916             break;
35917             case "north":
35918                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35919             break;
35920             case "south":
35921                 return [0, cm.top+cm.bottom+c.getHeight()];
35922             break;
35923         }
35924     }
35925 });/*
35926  * Based on:
35927  * Ext JS Library 1.1.1
35928  * Copyright(c) 2006-2007, Ext JS, LLC.
35929  *
35930  * Originally Released Under LGPL - original licence link has changed is not relivant.
35931  *
35932  * Fork - LGPL
35933  * <script type="text/javascript">
35934  */
35935 /*
35936  * These classes are private internal classes
35937  */
35938 Roo.bootstrap.layout.Center = function(config){
35939     config.region = "center";
35940     Roo.bootstrap.layout.Region.call(this, config);
35941     this.visible = true;
35942     this.minWidth = config.minWidth || 20;
35943     this.minHeight = config.minHeight || 20;
35944 };
35945
35946 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35947     hide : function(){
35948         // center panel can't be hidden
35949     },
35950     
35951     show : function(){
35952         // center panel can't be hidden
35953     },
35954     
35955     getMinWidth: function(){
35956         return this.minWidth;
35957     },
35958     
35959     getMinHeight: function(){
35960         return this.minHeight;
35961     }
35962 });
35963
35964
35965
35966
35967  
35968
35969
35970
35971
35972
35973 Roo.bootstrap.layout.North = function(config)
35974 {
35975     config.region = 'north';
35976     config.cursor = 'n-resize';
35977     
35978     Roo.bootstrap.layout.Split.call(this, config);
35979     
35980     
35981     if(this.split){
35982         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35983         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35984         this.split.el.addClass("roo-layout-split-v");
35985     }
35986     var size = config.initialSize || config.height;
35987     if(typeof size != "undefined"){
35988         this.el.setHeight(size);
35989     }
35990 };
35991 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35992 {
35993     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35994     
35995     
35996     
35997     getBox : function(){
35998         if(this.collapsed){
35999             return this.collapsedEl.getBox();
36000         }
36001         var box = this.el.getBox();
36002         if(this.split){
36003             box.height += this.split.el.getHeight();
36004         }
36005         return box;
36006     },
36007     
36008     updateBox : function(box){
36009         if(this.split && !this.collapsed){
36010             box.height -= this.split.el.getHeight();
36011             this.split.el.setLeft(box.x);
36012             this.split.el.setTop(box.y+box.height);
36013             this.split.el.setWidth(box.width);
36014         }
36015         if(this.collapsed){
36016             this.updateBody(box.width, null);
36017         }
36018         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36019     }
36020 });
36021
36022
36023
36024
36025
36026 Roo.bootstrap.layout.South = function(config){
36027     config.region = 'south';
36028     config.cursor = 's-resize';
36029     Roo.bootstrap.layout.Split.call(this, config);
36030     if(this.split){
36031         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36032         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36033         this.split.el.addClass("roo-layout-split-v");
36034     }
36035     var size = config.initialSize || config.height;
36036     if(typeof size != "undefined"){
36037         this.el.setHeight(size);
36038     }
36039 };
36040
36041 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36042     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36043     getBox : function(){
36044         if(this.collapsed){
36045             return this.collapsedEl.getBox();
36046         }
36047         var box = this.el.getBox();
36048         if(this.split){
36049             var sh = this.split.el.getHeight();
36050             box.height += sh;
36051             box.y -= sh;
36052         }
36053         return box;
36054     },
36055     
36056     updateBox : function(box){
36057         if(this.split && !this.collapsed){
36058             var sh = this.split.el.getHeight();
36059             box.height -= sh;
36060             box.y += sh;
36061             this.split.el.setLeft(box.x);
36062             this.split.el.setTop(box.y-sh);
36063             this.split.el.setWidth(box.width);
36064         }
36065         if(this.collapsed){
36066             this.updateBody(box.width, null);
36067         }
36068         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36069     }
36070 });
36071
36072 Roo.bootstrap.layout.East = function(config){
36073     config.region = "east";
36074     config.cursor = "e-resize";
36075     Roo.bootstrap.layout.Split.call(this, config);
36076     if(this.split){
36077         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36078         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36079         this.split.el.addClass("roo-layout-split-h");
36080     }
36081     var size = config.initialSize || config.width;
36082     if(typeof size != "undefined"){
36083         this.el.setWidth(size);
36084     }
36085 };
36086 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36087     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36088     getBox : function(){
36089         if(this.collapsed){
36090             return this.collapsedEl.getBox();
36091         }
36092         var box = this.el.getBox();
36093         if(this.split){
36094             var sw = this.split.el.getWidth();
36095             box.width += sw;
36096             box.x -= sw;
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);
36106             this.split.el.setTop(box.y);
36107             this.split.el.setHeight(box.height);
36108             box.x += sw;
36109         }
36110         if(this.collapsed){
36111             this.updateBody(null, box.height);
36112         }
36113         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36114     }
36115 });
36116
36117 Roo.bootstrap.layout.West = function(config){
36118     config.region = "west";
36119     config.cursor = "w-resize";
36120     
36121     Roo.bootstrap.layout.Split.call(this, config);
36122     if(this.split){
36123         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36124         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36125         this.split.el.addClass("roo-layout-split-h");
36126     }
36127     
36128 };
36129 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36130     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36131     
36132     onRender: function(ctr, pos)
36133     {
36134         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36135         var size = this.config.initialSize || this.config.width;
36136         if(typeof size != "undefined"){
36137             this.el.setWidth(size);
36138         }
36139     },
36140     
36141     getBox : function(){
36142         if(this.collapsed){
36143             return this.collapsedEl.getBox();
36144         }
36145         var box = this.el.getBox();
36146         if(this.split){
36147             box.width += this.split.el.getWidth();
36148         }
36149         return box;
36150     },
36151     
36152     updateBox : function(box){
36153         if(this.split && !this.collapsed){
36154             var sw = this.split.el.getWidth();
36155             box.width -= sw;
36156             this.split.el.setLeft(box.x+box.width);
36157             this.split.el.setTop(box.y);
36158             this.split.el.setHeight(box.height);
36159         }
36160         if(this.collapsed){
36161             this.updateBody(null, box.height);
36162         }
36163         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36164     }
36165 });
36166 Roo.namespace("Roo.bootstrap.panel");/*
36167  * Based on:
36168  * Ext JS Library 1.1.1
36169  * Copyright(c) 2006-2007, Ext JS, LLC.
36170  *
36171  * Originally Released Under LGPL - original licence link has changed is not relivant.
36172  *
36173  * Fork - LGPL
36174  * <script type="text/javascript">
36175  */
36176 /**
36177  * @class Roo.ContentPanel
36178  * @extends Roo.util.Observable
36179  * A basic ContentPanel element.
36180  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36181  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36182  * @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
36183  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36184  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36185  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36186  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36187  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36188  * @cfg {String} title          The title for this panel
36189  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36190  * @cfg {String} url            Calls {@link #setUrl} with this value
36191  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36192  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36193  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36194  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36195  * @cfg {Boolean} badges render the badges
36196
36197  * @constructor
36198  * Create a new ContentPanel.
36199  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36200  * @param {String/Object} config A string to set only the title or a config object
36201  * @param {String} content (optional) Set the HTML content for this panel
36202  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36203  */
36204 Roo.bootstrap.panel.Content = function( config){
36205     
36206     this.tpl = config.tpl || false;
36207     
36208     var el = config.el;
36209     var content = config.content;
36210
36211     if(config.autoCreate){ // xtype is available if this is called from factory
36212         el = Roo.id();
36213     }
36214     this.el = Roo.get(el);
36215     if(!this.el && config && config.autoCreate){
36216         if(typeof config.autoCreate == "object"){
36217             if(!config.autoCreate.id){
36218                 config.autoCreate.id = config.id||el;
36219             }
36220             this.el = Roo.DomHelper.append(document.body,
36221                         config.autoCreate, true);
36222         }else{
36223             var elcfg =  {   tag: "div",
36224                             cls: "roo-layout-inactive-content",
36225                             id: config.id||el
36226                             };
36227             if (config.html) {
36228                 elcfg.html = config.html;
36229                 
36230             }
36231                         
36232             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36233         }
36234     } 
36235     this.closable = false;
36236     this.loaded = false;
36237     this.active = false;
36238    
36239       
36240     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36241         
36242         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36243         
36244         this.wrapEl = this.el; //this.el.wrap();
36245         var ti = [];
36246         if (config.toolbar.items) {
36247             ti = config.toolbar.items ;
36248             delete config.toolbar.items ;
36249         }
36250         
36251         var nitems = [];
36252         this.toolbar.render(this.wrapEl, 'before');
36253         for(var i =0;i < ti.length;i++) {
36254           //  Roo.log(['add child', items[i]]);
36255             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36256         }
36257         this.toolbar.items = nitems;
36258         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36259         delete config.toolbar;
36260         
36261     }
36262     /*
36263     // xtype created footer. - not sure if will work as we normally have to render first..
36264     if (this.footer && !this.footer.el && this.footer.xtype) {
36265         if (!this.wrapEl) {
36266             this.wrapEl = this.el.wrap();
36267         }
36268     
36269         this.footer.container = this.wrapEl.createChild();
36270          
36271         this.footer = Roo.factory(this.footer, Roo);
36272         
36273     }
36274     */
36275     
36276      if(typeof config == "string"){
36277         this.title = config;
36278     }else{
36279         Roo.apply(this, config);
36280     }
36281     
36282     if(this.resizeEl){
36283         this.resizeEl = Roo.get(this.resizeEl, true);
36284     }else{
36285         this.resizeEl = this.el;
36286     }
36287     // handle view.xtype
36288     
36289  
36290     
36291     
36292     this.addEvents({
36293         /**
36294          * @event activate
36295          * Fires when this panel is activated. 
36296          * @param {Roo.ContentPanel} this
36297          */
36298         "activate" : true,
36299         /**
36300          * @event deactivate
36301          * Fires when this panel is activated. 
36302          * @param {Roo.ContentPanel} this
36303          */
36304         "deactivate" : true,
36305
36306         /**
36307          * @event resize
36308          * Fires when this panel is resized if fitToFrame is true.
36309          * @param {Roo.ContentPanel} this
36310          * @param {Number} width The width after any component adjustments
36311          * @param {Number} height The height after any component adjustments
36312          */
36313         "resize" : true,
36314         
36315          /**
36316          * @event render
36317          * Fires when this tab is created
36318          * @param {Roo.ContentPanel} this
36319          */
36320         "render" : true
36321         
36322         
36323         
36324     });
36325     
36326
36327     
36328     
36329     if(this.autoScroll){
36330         this.resizeEl.setStyle("overflow", "auto");
36331     } else {
36332         // fix randome scrolling
36333         //this.el.on('scroll', function() {
36334         //    Roo.log('fix random scolling');
36335         //    this.scrollTo('top',0); 
36336         //});
36337     }
36338     content = content || this.content;
36339     if(content){
36340         this.setContent(content);
36341     }
36342     if(config && config.url){
36343         this.setUrl(this.url, this.params, this.loadOnce);
36344     }
36345     
36346     
36347     
36348     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36349     
36350     if (this.view && typeof(this.view.xtype) != 'undefined') {
36351         this.view.el = this.el.appendChild(document.createElement("div"));
36352         this.view = Roo.factory(this.view); 
36353         this.view.render  &&  this.view.render(false, '');  
36354     }
36355     
36356     
36357     this.fireEvent('render', this);
36358 };
36359
36360 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36361     
36362     tabTip : '',
36363     
36364     setRegion : function(region){
36365         this.region = region;
36366         this.setActiveClass(region && !this.background);
36367     },
36368     
36369     
36370     setActiveClass: function(state)
36371     {
36372         if(state){
36373            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36374            this.el.setStyle('position','relative');
36375         }else{
36376            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36377            this.el.setStyle('position', 'absolute');
36378         } 
36379     },
36380     
36381     /**
36382      * Returns the toolbar for this Panel if one was configured. 
36383      * @return {Roo.Toolbar} 
36384      */
36385     getToolbar : function(){
36386         return this.toolbar;
36387     },
36388     
36389     setActiveState : function(active)
36390     {
36391         this.active = active;
36392         this.setActiveClass(active);
36393         if(!active){
36394             this.fireEvent("deactivate", this);
36395         }else{
36396             this.fireEvent("activate", this);
36397         }
36398     },
36399     /**
36400      * Updates this panel's element
36401      * @param {String} content The new content
36402      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36403     */
36404     setContent : function(content, loadScripts){
36405         this.el.update(content, loadScripts);
36406     },
36407
36408     ignoreResize : function(w, h){
36409         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36410             return true;
36411         }else{
36412             this.lastSize = {width: w, height: h};
36413             return false;
36414         }
36415     },
36416     /**
36417      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36418      * @return {Roo.UpdateManager} The UpdateManager
36419      */
36420     getUpdateManager : function(){
36421         return this.el.getUpdateManager();
36422     },
36423      /**
36424      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36425      * @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:
36426 <pre><code>
36427 panel.load({
36428     url: "your-url.php",
36429     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36430     callback: yourFunction,
36431     scope: yourObject, //(optional scope)
36432     discardUrl: false,
36433     nocache: false,
36434     text: "Loading...",
36435     timeout: 30,
36436     scripts: false
36437 });
36438 </code></pre>
36439      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36440      * 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.
36441      * @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}
36442      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36443      * @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.
36444      * @return {Roo.ContentPanel} this
36445      */
36446     load : function(){
36447         var um = this.el.getUpdateManager();
36448         um.update.apply(um, arguments);
36449         return this;
36450     },
36451
36452
36453     /**
36454      * 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.
36455      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36456      * @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)
36457      * @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)
36458      * @return {Roo.UpdateManager} The UpdateManager
36459      */
36460     setUrl : function(url, params, loadOnce){
36461         if(this.refreshDelegate){
36462             this.removeListener("activate", this.refreshDelegate);
36463         }
36464         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36465         this.on("activate", this.refreshDelegate);
36466         return this.el.getUpdateManager();
36467     },
36468     
36469     _handleRefresh : function(url, params, loadOnce){
36470         if(!loadOnce || !this.loaded){
36471             var updater = this.el.getUpdateManager();
36472             updater.update(url, params, this._setLoaded.createDelegate(this));
36473         }
36474     },
36475     
36476     _setLoaded : function(){
36477         this.loaded = true;
36478     }, 
36479     
36480     /**
36481      * Returns this panel's id
36482      * @return {String} 
36483      */
36484     getId : function(){
36485         return this.el.id;
36486     },
36487     
36488     /** 
36489      * Returns this panel's element - used by regiosn to add.
36490      * @return {Roo.Element} 
36491      */
36492     getEl : function(){
36493         return this.wrapEl || this.el;
36494     },
36495     
36496    
36497     
36498     adjustForComponents : function(width, height)
36499     {
36500         //Roo.log('adjustForComponents ');
36501         if(this.resizeEl != this.el){
36502             width -= this.el.getFrameWidth('lr');
36503             height -= this.el.getFrameWidth('tb');
36504         }
36505         if(this.toolbar){
36506             var te = this.toolbar.getEl();
36507             te.setWidth(width);
36508             height -= te.getHeight();
36509         }
36510         if(this.footer){
36511             var te = this.footer.getEl();
36512             te.setWidth(width);
36513             height -= te.getHeight();
36514         }
36515         
36516         
36517         if(this.adjustments){
36518             width += this.adjustments[0];
36519             height += this.adjustments[1];
36520         }
36521         return {"width": width, "height": height};
36522     },
36523     
36524     setSize : function(width, height){
36525         if(this.fitToFrame && !this.ignoreResize(width, height)){
36526             if(this.fitContainer && this.resizeEl != this.el){
36527                 this.el.setSize(width, height);
36528             }
36529             var size = this.adjustForComponents(width, height);
36530             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36531             this.fireEvent('resize', this, size.width, size.height);
36532         }
36533     },
36534     
36535     /**
36536      * Returns this panel's title
36537      * @return {String} 
36538      */
36539     getTitle : function(){
36540         
36541         if (typeof(this.title) != 'object') {
36542             return this.title;
36543         }
36544         
36545         var t = '';
36546         for (var k in this.title) {
36547             if (!this.title.hasOwnProperty(k)) {
36548                 continue;
36549             }
36550             
36551             if (k.indexOf('-') >= 0) {
36552                 var s = k.split('-');
36553                 for (var i = 0; i<s.length; i++) {
36554                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36555                 }
36556             } else {
36557                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36558             }
36559         }
36560         return t;
36561     },
36562     
36563     /**
36564      * Set this panel's title
36565      * @param {String} title
36566      */
36567     setTitle : function(title){
36568         this.title = title;
36569         if(this.region){
36570             this.region.updatePanelTitle(this, title);
36571         }
36572     },
36573     
36574     /**
36575      * Returns true is this panel was configured to be closable
36576      * @return {Boolean} 
36577      */
36578     isClosable : function(){
36579         return this.closable;
36580     },
36581     
36582     beforeSlide : function(){
36583         this.el.clip();
36584         this.resizeEl.clip();
36585     },
36586     
36587     afterSlide : function(){
36588         this.el.unclip();
36589         this.resizeEl.unclip();
36590     },
36591     
36592     /**
36593      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36594      *   Will fail silently if the {@link #setUrl} method has not been called.
36595      *   This does not activate the panel, just updates its content.
36596      */
36597     refresh : function(){
36598         if(this.refreshDelegate){
36599            this.loaded = false;
36600            this.refreshDelegate();
36601         }
36602     },
36603     
36604     /**
36605      * Destroys this panel
36606      */
36607     destroy : function(){
36608         this.el.removeAllListeners();
36609         var tempEl = document.createElement("span");
36610         tempEl.appendChild(this.el.dom);
36611         tempEl.innerHTML = "";
36612         this.el.remove();
36613         this.el = null;
36614     },
36615     
36616     /**
36617      * form - if the content panel contains a form - this is a reference to it.
36618      * @type {Roo.form.Form}
36619      */
36620     form : false,
36621     /**
36622      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36623      *    This contains a reference to it.
36624      * @type {Roo.View}
36625      */
36626     view : false,
36627     
36628       /**
36629      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36630      * <pre><code>
36631
36632 layout.addxtype({
36633        xtype : 'Form',
36634        items: [ .... ]
36635    }
36636 );
36637
36638 </code></pre>
36639      * @param {Object} cfg Xtype definition of item to add.
36640      */
36641     
36642     
36643     getChildContainer: function () {
36644         return this.getEl();
36645     }
36646     
36647     
36648     /*
36649         var  ret = new Roo.factory(cfg);
36650         return ret;
36651         
36652         
36653         // add form..
36654         if (cfg.xtype.match(/^Form$/)) {
36655             
36656             var el;
36657             //if (this.footer) {
36658             //    el = this.footer.container.insertSibling(false, 'before');
36659             //} else {
36660                 el = this.el.createChild();
36661             //}
36662
36663             this.form = new  Roo.form.Form(cfg);
36664             
36665             
36666             if ( this.form.allItems.length) {
36667                 this.form.render(el.dom);
36668             }
36669             return this.form;
36670         }
36671         // should only have one of theses..
36672         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36673             // views.. should not be just added - used named prop 'view''
36674             
36675             cfg.el = this.el.appendChild(document.createElement("div"));
36676             // factory?
36677             
36678             var ret = new Roo.factory(cfg);
36679              
36680              ret.render && ret.render(false, ''); // render blank..
36681             this.view = ret;
36682             return ret;
36683         }
36684         return false;
36685     }
36686     \*/
36687 });
36688  
36689 /**
36690  * @class Roo.bootstrap.panel.Grid
36691  * @extends Roo.bootstrap.panel.Content
36692  * @constructor
36693  * Create a new GridPanel.
36694  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36695  * @param {Object} config A the config object
36696   
36697  */
36698
36699
36700
36701 Roo.bootstrap.panel.Grid = function(config)
36702 {
36703     
36704       
36705     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36706         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36707
36708     config.el = this.wrapper;
36709     //this.el = this.wrapper;
36710     
36711       if (config.container) {
36712         // ctor'ed from a Border/panel.grid
36713         
36714         
36715         this.wrapper.setStyle("overflow", "hidden");
36716         this.wrapper.addClass('roo-grid-container');
36717
36718     }
36719     
36720     
36721     if(config.toolbar){
36722         var tool_el = this.wrapper.createChild();    
36723         this.toolbar = Roo.factory(config.toolbar);
36724         var ti = [];
36725         if (config.toolbar.items) {
36726             ti = config.toolbar.items ;
36727             delete config.toolbar.items ;
36728         }
36729         
36730         var nitems = [];
36731         this.toolbar.render(tool_el);
36732         for(var i =0;i < ti.length;i++) {
36733           //  Roo.log(['add child', items[i]]);
36734             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36735         }
36736         this.toolbar.items = nitems;
36737         
36738         delete config.toolbar;
36739     }
36740     
36741     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36742     config.grid.scrollBody = true;;
36743     config.grid.monitorWindowResize = false; // turn off autosizing
36744     config.grid.autoHeight = false;
36745     config.grid.autoWidth = false;
36746     
36747     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36748     
36749     if (config.background) {
36750         // render grid on panel activation (if panel background)
36751         this.on('activate', function(gp) {
36752             if (!gp.grid.rendered) {
36753                 gp.grid.render(this.wrapper);
36754                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36755             }
36756         });
36757             
36758     } else {
36759         this.grid.render(this.wrapper);
36760         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36761
36762     }
36763     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36764     // ??? needed ??? config.el = this.wrapper;
36765     
36766     
36767     
36768   
36769     // xtype created footer. - not sure if will work as we normally have to render first..
36770     if (this.footer && !this.footer.el && this.footer.xtype) {
36771         
36772         var ctr = this.grid.getView().getFooterPanel(true);
36773         this.footer.dataSource = this.grid.dataSource;
36774         this.footer = Roo.factory(this.footer, Roo);
36775         this.footer.render(ctr);
36776         
36777     }
36778     
36779     
36780     
36781     
36782      
36783 };
36784
36785 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36786     getId : function(){
36787         return this.grid.id;
36788     },
36789     
36790     /**
36791      * Returns the grid for this panel
36792      * @return {Roo.bootstrap.Table} 
36793      */
36794     getGrid : function(){
36795         return this.grid;    
36796     },
36797     
36798     setSize : function(width, height){
36799         if(!this.ignoreResize(width, height)){
36800             var grid = this.grid;
36801             var size = this.adjustForComponents(width, height);
36802             var gridel = grid.getGridEl();
36803             gridel.setSize(size.width, size.height);
36804             /*
36805             var thd = grid.getGridEl().select('thead',true).first();
36806             var tbd = grid.getGridEl().select('tbody', true).first();
36807             if (tbd) {
36808                 tbd.setSize(width, height - thd.getHeight());
36809             }
36810             */
36811             grid.autoSize();
36812         }
36813     },
36814      
36815     
36816     
36817     beforeSlide : function(){
36818         this.grid.getView().scroller.clip();
36819     },
36820     
36821     afterSlide : function(){
36822         this.grid.getView().scroller.unclip();
36823     },
36824     
36825     destroy : function(){
36826         this.grid.destroy();
36827         delete this.grid;
36828         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36829     }
36830 });
36831
36832 /**
36833  * @class Roo.bootstrap.panel.Nest
36834  * @extends Roo.bootstrap.panel.Content
36835  * @constructor
36836  * Create a new Panel, that can contain a layout.Border.
36837  * 
36838  * 
36839  * @param {Roo.BorderLayout} layout The layout for this panel
36840  * @param {String/Object} config A string to set only the title or a config object
36841  */
36842 Roo.bootstrap.panel.Nest = function(config)
36843 {
36844     // construct with only one argument..
36845     /* FIXME - implement nicer consturctors
36846     if (layout.layout) {
36847         config = layout;
36848         layout = config.layout;
36849         delete config.layout;
36850     }
36851     if (layout.xtype && !layout.getEl) {
36852         // then layout needs constructing..
36853         layout = Roo.factory(layout, Roo);
36854     }
36855     */
36856     
36857     config.el =  config.layout.getEl();
36858     
36859     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36860     
36861     config.layout.monitorWindowResize = false; // turn off autosizing
36862     this.layout = config.layout;
36863     this.layout.getEl().addClass("roo-layout-nested-layout");
36864     
36865     
36866     
36867     
36868 };
36869
36870 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36871
36872     setSize : function(width, height){
36873         if(!this.ignoreResize(width, height)){
36874             var size = this.adjustForComponents(width, height);
36875             var el = this.layout.getEl();
36876             if (size.height < 1) {
36877                 el.setWidth(size.width);   
36878             } else {
36879                 el.setSize(size.width, size.height);
36880             }
36881             var touch = el.dom.offsetWidth;
36882             this.layout.layout();
36883             // ie requires a double layout on the first pass
36884             if(Roo.isIE && !this.initialized){
36885                 this.initialized = true;
36886                 this.layout.layout();
36887             }
36888         }
36889     },
36890     
36891     // activate all subpanels if not currently active..
36892     
36893     setActiveState : function(active){
36894         this.active = active;
36895         this.setActiveClass(active);
36896         
36897         if(!active){
36898             this.fireEvent("deactivate", this);
36899             return;
36900         }
36901         
36902         this.fireEvent("activate", this);
36903         // not sure if this should happen before or after..
36904         if (!this.layout) {
36905             return; // should not happen..
36906         }
36907         var reg = false;
36908         for (var r in this.layout.regions) {
36909             reg = this.layout.getRegion(r);
36910             if (reg.getActivePanel()) {
36911                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36912                 reg.setActivePanel(reg.getActivePanel());
36913                 continue;
36914             }
36915             if (!reg.panels.length) {
36916                 continue;
36917             }
36918             reg.showPanel(reg.getPanel(0));
36919         }
36920         
36921         
36922         
36923         
36924     },
36925     
36926     /**
36927      * Returns the nested BorderLayout for this panel
36928      * @return {Roo.BorderLayout} 
36929      */
36930     getLayout : function(){
36931         return this.layout;
36932     },
36933     
36934      /**
36935      * Adds a xtype elements to the layout of the nested panel
36936      * <pre><code>
36937
36938 panel.addxtype({
36939        xtype : 'ContentPanel',
36940        region: 'west',
36941        items: [ .... ]
36942    }
36943 );
36944
36945 panel.addxtype({
36946         xtype : 'NestedLayoutPanel',
36947         region: 'west',
36948         layout: {
36949            center: { },
36950            west: { }   
36951         },
36952         items : [ ... list of content panels or nested layout panels.. ]
36953    }
36954 );
36955 </code></pre>
36956      * @param {Object} cfg Xtype definition of item to add.
36957      */
36958     addxtype : function(cfg) {
36959         return this.layout.addxtype(cfg);
36960     
36961     }
36962 });        /*
36963  * Based on:
36964  * Ext JS Library 1.1.1
36965  * Copyright(c) 2006-2007, Ext JS, LLC.
36966  *
36967  * Originally Released Under LGPL - original licence link has changed is not relivant.
36968  *
36969  * Fork - LGPL
36970  * <script type="text/javascript">
36971  */
36972 /**
36973  * @class Roo.TabPanel
36974  * @extends Roo.util.Observable
36975  * A lightweight tab container.
36976  * <br><br>
36977  * Usage:
36978  * <pre><code>
36979 // basic tabs 1, built from existing content
36980 var tabs = new Roo.TabPanel("tabs1");
36981 tabs.addTab("script", "View Script");
36982 tabs.addTab("markup", "View Markup");
36983 tabs.activate("script");
36984
36985 // more advanced tabs, built from javascript
36986 var jtabs = new Roo.TabPanel("jtabs");
36987 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36988
36989 // set up the UpdateManager
36990 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36991 var updater = tab2.getUpdateManager();
36992 updater.setDefaultUrl("ajax1.htm");
36993 tab2.on('activate', updater.refresh, updater, true);
36994
36995 // Use setUrl for Ajax loading
36996 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36997 tab3.setUrl("ajax2.htm", null, true);
36998
36999 // Disabled tab
37000 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37001 tab4.disable();
37002
37003 jtabs.activate("jtabs-1");
37004  * </code></pre>
37005  * @constructor
37006  * Create a new TabPanel.
37007  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37008  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37009  */
37010 Roo.bootstrap.panel.Tabs = function(config){
37011     /**
37012     * The container element for this TabPanel.
37013     * @type Roo.Element
37014     */
37015     this.el = Roo.get(config.el);
37016     delete config.el;
37017     if(config){
37018         if(typeof config == "boolean"){
37019             this.tabPosition = config ? "bottom" : "top";
37020         }else{
37021             Roo.apply(this, config);
37022         }
37023     }
37024     
37025     if(this.tabPosition == "bottom"){
37026         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37027         this.el.addClass("roo-tabs-bottom");
37028     }
37029     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37030     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37031     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37032     if(Roo.isIE){
37033         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37034     }
37035     if(this.tabPosition != "bottom"){
37036         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37037          * @type Roo.Element
37038          */
37039         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37040         this.el.addClass("roo-tabs-top");
37041     }
37042     this.items = [];
37043
37044     this.bodyEl.setStyle("position", "relative");
37045
37046     this.active = null;
37047     this.activateDelegate = this.activate.createDelegate(this);
37048
37049     this.addEvents({
37050         /**
37051          * @event tabchange
37052          * Fires when the active tab changes
37053          * @param {Roo.TabPanel} this
37054          * @param {Roo.TabPanelItem} activePanel The new active tab
37055          */
37056         "tabchange": true,
37057         /**
37058          * @event beforetabchange
37059          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37060          * @param {Roo.TabPanel} this
37061          * @param {Object} e Set cancel to true on this object to cancel the tab change
37062          * @param {Roo.TabPanelItem} tab The tab being changed to
37063          */
37064         "beforetabchange" : true
37065     });
37066
37067     Roo.EventManager.onWindowResize(this.onResize, this);
37068     this.cpad = this.el.getPadding("lr");
37069     this.hiddenCount = 0;
37070
37071
37072     // toolbar on the tabbar support...
37073     if (this.toolbar) {
37074         alert("no toolbar support yet");
37075         this.toolbar  = false;
37076         /*
37077         var tcfg = this.toolbar;
37078         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37079         this.toolbar = new Roo.Toolbar(tcfg);
37080         if (Roo.isSafari) {
37081             var tbl = tcfg.container.child('table', true);
37082             tbl.setAttribute('width', '100%');
37083         }
37084         */
37085         
37086     }
37087    
37088
37089
37090     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37091 };
37092
37093 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37094     /*
37095      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37096      */
37097     tabPosition : "top",
37098     /*
37099      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37100      */
37101     currentTabWidth : 0,
37102     /*
37103      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37104      */
37105     minTabWidth : 40,
37106     /*
37107      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37108      */
37109     maxTabWidth : 250,
37110     /*
37111      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37112      */
37113     preferredTabWidth : 175,
37114     /*
37115      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37116      */
37117     resizeTabs : false,
37118     /*
37119      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37120      */
37121     monitorResize : true,
37122     /*
37123      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37124      */
37125     toolbar : false,
37126
37127     /**
37128      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37129      * @param {String} id The id of the div to use <b>or create</b>
37130      * @param {String} text The text for the tab
37131      * @param {String} content (optional) Content to put in the TabPanelItem body
37132      * @param {Boolean} closable (optional) True to create a close icon on the tab
37133      * @return {Roo.TabPanelItem} The created TabPanelItem
37134      */
37135     addTab : function(id, text, content, closable, tpl)
37136     {
37137         var item = new Roo.bootstrap.panel.TabItem({
37138             panel: this,
37139             id : id,
37140             text : text,
37141             closable : closable,
37142             tpl : tpl
37143         });
37144         this.addTabItem(item);
37145         if(content){
37146             item.setContent(content);
37147         }
37148         return item;
37149     },
37150
37151     /**
37152      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37153      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37154      * @return {Roo.TabPanelItem}
37155      */
37156     getTab : function(id){
37157         return this.items[id];
37158     },
37159
37160     /**
37161      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37162      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37163      */
37164     hideTab : function(id){
37165         var t = this.items[id];
37166         if(!t.isHidden()){
37167            t.setHidden(true);
37168            this.hiddenCount++;
37169            this.autoSizeTabs();
37170         }
37171     },
37172
37173     /**
37174      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37175      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37176      */
37177     unhideTab : function(id){
37178         var t = this.items[id];
37179         if(t.isHidden()){
37180            t.setHidden(false);
37181            this.hiddenCount--;
37182            this.autoSizeTabs();
37183         }
37184     },
37185
37186     /**
37187      * Adds an existing {@link Roo.TabPanelItem}.
37188      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37189      */
37190     addTabItem : function(item){
37191         this.items[item.id] = item;
37192         this.items.push(item);
37193       //  if(this.resizeTabs){
37194     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37195   //         this.autoSizeTabs();
37196 //        }else{
37197 //            item.autoSize();
37198        // }
37199     },
37200
37201     /**
37202      * Removes a {@link Roo.TabPanelItem}.
37203      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37204      */
37205     removeTab : function(id){
37206         var items = this.items;
37207         var tab = items[id];
37208         if(!tab) { return; }
37209         var index = items.indexOf(tab);
37210         if(this.active == tab && items.length > 1){
37211             var newTab = this.getNextAvailable(index);
37212             if(newTab) {
37213                 newTab.activate();
37214             }
37215         }
37216         this.stripEl.dom.removeChild(tab.pnode.dom);
37217         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37218             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37219         }
37220         items.splice(index, 1);
37221         delete this.items[tab.id];
37222         tab.fireEvent("close", tab);
37223         tab.purgeListeners();
37224         this.autoSizeTabs();
37225     },
37226
37227     getNextAvailable : function(start){
37228         var items = this.items;
37229         var index = start;
37230         // look for a next tab that will slide over to
37231         // replace the one being removed
37232         while(index < items.length){
37233             var item = items[++index];
37234             if(item && !item.isHidden()){
37235                 return item;
37236             }
37237         }
37238         // if one isn't found select the previous tab (on the left)
37239         index = start;
37240         while(index >= 0){
37241             var item = items[--index];
37242             if(item && !item.isHidden()){
37243                 return item;
37244             }
37245         }
37246         return null;
37247     },
37248
37249     /**
37250      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37251      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37252      */
37253     disableTab : function(id){
37254         var tab = this.items[id];
37255         if(tab && this.active != tab){
37256             tab.disable();
37257         }
37258     },
37259
37260     /**
37261      * Enables a {@link Roo.TabPanelItem} that is disabled.
37262      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37263      */
37264     enableTab : function(id){
37265         var tab = this.items[id];
37266         tab.enable();
37267     },
37268
37269     /**
37270      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37271      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37272      * @return {Roo.TabPanelItem} The TabPanelItem.
37273      */
37274     activate : function(id){
37275         var tab = this.items[id];
37276         if(!tab){
37277             return null;
37278         }
37279         if(tab == this.active || tab.disabled){
37280             return tab;
37281         }
37282         var e = {};
37283         this.fireEvent("beforetabchange", this, e, tab);
37284         if(e.cancel !== true && !tab.disabled){
37285             if(this.active){
37286                 this.active.hide();
37287             }
37288             this.active = this.items[id];
37289             this.active.show();
37290             this.fireEvent("tabchange", this, this.active);
37291         }
37292         return tab;
37293     },
37294
37295     /**
37296      * Gets the active {@link Roo.TabPanelItem}.
37297      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37298      */
37299     getActiveTab : function(){
37300         return this.active;
37301     },
37302
37303     /**
37304      * Updates the tab body element to fit the height of the container element
37305      * for overflow scrolling
37306      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37307      */
37308     syncHeight : function(targetHeight){
37309         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37310         var bm = this.bodyEl.getMargins();
37311         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37312         this.bodyEl.setHeight(newHeight);
37313         return newHeight;
37314     },
37315
37316     onResize : function(){
37317         if(this.monitorResize){
37318             this.autoSizeTabs();
37319         }
37320     },
37321
37322     /**
37323      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37324      */
37325     beginUpdate : function(){
37326         this.updating = true;
37327     },
37328
37329     /**
37330      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37331      */
37332     endUpdate : function(){
37333         this.updating = false;
37334         this.autoSizeTabs();
37335     },
37336
37337     /**
37338      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37339      */
37340     autoSizeTabs : function(){
37341         var count = this.items.length;
37342         var vcount = count - this.hiddenCount;
37343         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37344             return;
37345         }
37346         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37347         var availWidth = Math.floor(w / vcount);
37348         var b = this.stripBody;
37349         if(b.getWidth() > w){
37350             var tabs = this.items;
37351             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37352             if(availWidth < this.minTabWidth){
37353                 /*if(!this.sleft){    // incomplete scrolling code
37354                     this.createScrollButtons();
37355                 }
37356                 this.showScroll();
37357                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37358             }
37359         }else{
37360             if(this.currentTabWidth < this.preferredTabWidth){
37361                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37362             }
37363         }
37364     },
37365
37366     /**
37367      * Returns the number of tabs in this TabPanel.
37368      * @return {Number}
37369      */
37370      getCount : function(){
37371          return this.items.length;
37372      },
37373
37374     /**
37375      * Resizes all the tabs to the passed width
37376      * @param {Number} The new width
37377      */
37378     setTabWidth : function(width){
37379         this.currentTabWidth = width;
37380         for(var i = 0, len = this.items.length; i < len; i++) {
37381                 if(!this.items[i].isHidden()) {
37382                 this.items[i].setWidth(width);
37383             }
37384         }
37385     },
37386
37387     /**
37388      * Destroys this TabPanel
37389      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37390      */
37391     destroy : function(removeEl){
37392         Roo.EventManager.removeResizeListener(this.onResize, this);
37393         for(var i = 0, len = this.items.length; i < len; i++){
37394             this.items[i].purgeListeners();
37395         }
37396         if(removeEl === true){
37397             this.el.update("");
37398             this.el.remove();
37399         }
37400     },
37401     
37402     createStrip : function(container)
37403     {
37404         var strip = document.createElement("nav");
37405         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37406         container.appendChild(strip);
37407         return strip;
37408     },
37409     
37410     createStripList : function(strip)
37411     {
37412         // div wrapper for retard IE
37413         // returns the "tr" element.
37414         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37415         //'<div class="x-tabs-strip-wrap">'+
37416           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37417           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37418         return strip.firstChild; //.firstChild.firstChild.firstChild;
37419     },
37420     createBody : function(container)
37421     {
37422         var body = document.createElement("div");
37423         Roo.id(body, "tab-body");
37424         //Roo.fly(body).addClass("x-tabs-body");
37425         Roo.fly(body).addClass("tab-content");
37426         container.appendChild(body);
37427         return body;
37428     },
37429     createItemBody :function(bodyEl, id){
37430         var body = Roo.getDom(id);
37431         if(!body){
37432             body = document.createElement("div");
37433             body.id = id;
37434         }
37435         //Roo.fly(body).addClass("x-tabs-item-body");
37436         Roo.fly(body).addClass("tab-pane");
37437          bodyEl.insertBefore(body, bodyEl.firstChild);
37438         return body;
37439     },
37440     /** @private */
37441     createStripElements :  function(stripEl, text, closable, tpl)
37442     {
37443         var td = document.createElement("li"); // was td..
37444         
37445         
37446         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37447         
37448         
37449         stripEl.appendChild(td);
37450         /*if(closable){
37451             td.className = "x-tabs-closable";
37452             if(!this.closeTpl){
37453                 this.closeTpl = new Roo.Template(
37454                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37455                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37456                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37457                 );
37458             }
37459             var el = this.closeTpl.overwrite(td, {"text": text});
37460             var close = el.getElementsByTagName("div")[0];
37461             var inner = el.getElementsByTagName("em")[0];
37462             return {"el": el, "close": close, "inner": inner};
37463         } else {
37464         */
37465         // not sure what this is..
37466 //            if(!this.tabTpl){
37467                 //this.tabTpl = new Roo.Template(
37468                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37469                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37470                 //);
37471 //                this.tabTpl = new Roo.Template(
37472 //                   '<a href="#">' +
37473 //                   '<span unselectable="on"' +
37474 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37475 //                            ' >{text}</span></a>'
37476 //                );
37477 //                
37478 //            }
37479
37480
37481             var template = tpl || this.tabTpl || false;
37482             
37483             if(!template){
37484                 
37485                 template = new Roo.Template(
37486                    '<a href="#">' +
37487                    '<span unselectable="on"' +
37488                             (this.disableTooltips ? '' : ' title="{text}"') +
37489                             ' >{text}</span></a>'
37490                 );
37491             }
37492             
37493             switch (typeof(template)) {
37494                 case 'object' :
37495                     break;
37496                 case 'string' :
37497                     template = new Roo.Template(template);
37498                     break;
37499                 default :
37500                     break;
37501             }
37502             
37503             var el = template.overwrite(td, {"text": text});
37504             
37505             var inner = el.getElementsByTagName("span")[0];
37506             
37507             return {"el": el, "inner": inner};
37508             
37509     }
37510         
37511     
37512 });
37513
37514 /**
37515  * @class Roo.TabPanelItem
37516  * @extends Roo.util.Observable
37517  * Represents an individual item (tab plus body) in a TabPanel.
37518  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37519  * @param {String} id The id of this TabPanelItem
37520  * @param {String} text The text for the tab of this TabPanelItem
37521  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37522  */
37523 Roo.bootstrap.panel.TabItem = function(config){
37524     /**
37525      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37526      * @type Roo.TabPanel
37527      */
37528     this.tabPanel = config.panel;
37529     /**
37530      * The id for this TabPanelItem
37531      * @type String
37532      */
37533     this.id = config.id;
37534     /** @private */
37535     this.disabled = false;
37536     /** @private */
37537     this.text = config.text;
37538     /** @private */
37539     this.loaded = false;
37540     this.closable = config.closable;
37541
37542     /**
37543      * The body element for this TabPanelItem.
37544      * @type Roo.Element
37545      */
37546     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37547     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37548     this.bodyEl.setStyle("display", "block");
37549     this.bodyEl.setStyle("zoom", "1");
37550     //this.hideAction();
37551
37552     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37553     /** @private */
37554     this.el = Roo.get(els.el);
37555     this.inner = Roo.get(els.inner, true);
37556     this.textEl = Roo.get(this.el.dom.firstChild, true);
37557     this.pnode = Roo.get(els.el.parentNode, true);
37558     this.el.on("mousedown", this.onTabMouseDown, this);
37559     this.el.on("click", this.onTabClick, this);
37560     /** @private */
37561     if(config.closable){
37562         var c = Roo.get(els.close, true);
37563         c.dom.title = this.closeText;
37564         c.addClassOnOver("close-over");
37565         c.on("click", this.closeClick, this);
37566      }
37567
37568     this.addEvents({
37569          /**
37570          * @event activate
37571          * Fires when this tab becomes the active tab.
37572          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37573          * @param {Roo.TabPanelItem} this
37574          */
37575         "activate": true,
37576         /**
37577          * @event beforeclose
37578          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37579          * @param {Roo.TabPanelItem} this
37580          * @param {Object} e Set cancel to true on this object to cancel the close.
37581          */
37582         "beforeclose": true,
37583         /**
37584          * @event close
37585          * Fires when this tab is closed.
37586          * @param {Roo.TabPanelItem} this
37587          */
37588          "close": true,
37589         /**
37590          * @event deactivate
37591          * Fires when this tab is no longer the active tab.
37592          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37593          * @param {Roo.TabPanelItem} this
37594          */
37595          "deactivate" : true
37596     });
37597     this.hidden = false;
37598
37599     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37600 };
37601
37602 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37603            {
37604     purgeListeners : function(){
37605        Roo.util.Observable.prototype.purgeListeners.call(this);
37606        this.el.removeAllListeners();
37607     },
37608     /**
37609      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37610      */
37611     show : function(){
37612         this.pnode.addClass("active");
37613         this.showAction();
37614         if(Roo.isOpera){
37615             this.tabPanel.stripWrap.repaint();
37616         }
37617         this.fireEvent("activate", this.tabPanel, this);
37618     },
37619
37620     /**
37621      * Returns true if this tab is the active tab.
37622      * @return {Boolean}
37623      */
37624     isActive : function(){
37625         return this.tabPanel.getActiveTab() == this;
37626     },
37627
37628     /**
37629      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37630      */
37631     hide : function(){
37632         this.pnode.removeClass("active");
37633         this.hideAction();
37634         this.fireEvent("deactivate", this.tabPanel, this);
37635     },
37636
37637     hideAction : function(){
37638         this.bodyEl.hide();
37639         this.bodyEl.setStyle("position", "absolute");
37640         this.bodyEl.setLeft("-20000px");
37641         this.bodyEl.setTop("-20000px");
37642     },
37643
37644     showAction : function(){
37645         this.bodyEl.setStyle("position", "relative");
37646         this.bodyEl.setTop("");
37647         this.bodyEl.setLeft("");
37648         this.bodyEl.show();
37649     },
37650
37651     /**
37652      * Set the tooltip for the tab.
37653      * @param {String} tooltip The tab's tooltip
37654      */
37655     setTooltip : function(text){
37656         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37657             this.textEl.dom.qtip = text;
37658             this.textEl.dom.removeAttribute('title');
37659         }else{
37660             this.textEl.dom.title = text;
37661         }
37662     },
37663
37664     onTabClick : function(e){
37665         e.preventDefault();
37666         this.tabPanel.activate(this.id);
37667     },
37668
37669     onTabMouseDown : function(e){
37670         e.preventDefault();
37671         this.tabPanel.activate(this.id);
37672     },
37673 /*
37674     getWidth : function(){
37675         return this.inner.getWidth();
37676     },
37677
37678     setWidth : function(width){
37679         var iwidth = width - this.pnode.getPadding("lr");
37680         this.inner.setWidth(iwidth);
37681         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37682         this.pnode.setWidth(width);
37683     },
37684 */
37685     /**
37686      * Show or hide the tab
37687      * @param {Boolean} hidden True to hide or false to show.
37688      */
37689     setHidden : function(hidden){
37690         this.hidden = hidden;
37691         this.pnode.setStyle("display", hidden ? "none" : "");
37692     },
37693
37694     /**
37695      * Returns true if this tab is "hidden"
37696      * @return {Boolean}
37697      */
37698     isHidden : function(){
37699         return this.hidden;
37700     },
37701
37702     /**
37703      * Returns the text for this tab
37704      * @return {String}
37705      */
37706     getText : function(){
37707         return this.text;
37708     },
37709     /*
37710     autoSize : function(){
37711         //this.el.beginMeasure();
37712         this.textEl.setWidth(1);
37713         /*
37714          *  #2804 [new] Tabs in Roojs
37715          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37716          */
37717         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37718         //this.el.endMeasure();
37719     //},
37720
37721     /**
37722      * Sets the text for the tab (Note: this also sets the tooltip text)
37723      * @param {String} text The tab's text and tooltip
37724      */
37725     setText : function(text){
37726         this.text = text;
37727         this.textEl.update(text);
37728         this.setTooltip(text);
37729         //if(!this.tabPanel.resizeTabs){
37730         //    this.autoSize();
37731         //}
37732     },
37733     /**
37734      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37735      */
37736     activate : function(){
37737         this.tabPanel.activate(this.id);
37738     },
37739
37740     /**
37741      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37742      */
37743     disable : function(){
37744         if(this.tabPanel.active != this){
37745             this.disabled = true;
37746             this.pnode.addClass("disabled");
37747         }
37748     },
37749
37750     /**
37751      * Enables this TabPanelItem if it was previously disabled.
37752      */
37753     enable : function(){
37754         this.disabled = false;
37755         this.pnode.removeClass("disabled");
37756     },
37757
37758     /**
37759      * Sets the content for this TabPanelItem.
37760      * @param {String} content The content
37761      * @param {Boolean} loadScripts true to look for and load scripts
37762      */
37763     setContent : function(content, loadScripts){
37764         this.bodyEl.update(content, loadScripts);
37765     },
37766
37767     /**
37768      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37769      * @return {Roo.UpdateManager} The UpdateManager
37770      */
37771     getUpdateManager : function(){
37772         return this.bodyEl.getUpdateManager();
37773     },
37774
37775     /**
37776      * Set a URL to be used to load the content for this TabPanelItem.
37777      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37778      * @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)
37779      * @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)
37780      * @return {Roo.UpdateManager} The UpdateManager
37781      */
37782     setUrl : function(url, params, loadOnce){
37783         if(this.refreshDelegate){
37784             this.un('activate', this.refreshDelegate);
37785         }
37786         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37787         this.on("activate", this.refreshDelegate);
37788         return this.bodyEl.getUpdateManager();
37789     },
37790
37791     /** @private */
37792     _handleRefresh : function(url, params, loadOnce){
37793         if(!loadOnce || !this.loaded){
37794             var updater = this.bodyEl.getUpdateManager();
37795             updater.update(url, params, this._setLoaded.createDelegate(this));
37796         }
37797     },
37798
37799     /**
37800      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37801      *   Will fail silently if the setUrl method has not been called.
37802      *   This does not activate the panel, just updates its content.
37803      */
37804     refresh : function(){
37805         if(this.refreshDelegate){
37806            this.loaded = false;
37807            this.refreshDelegate();
37808         }
37809     },
37810
37811     /** @private */
37812     _setLoaded : function(){
37813         this.loaded = true;
37814     },
37815
37816     /** @private */
37817     closeClick : function(e){
37818         var o = {};
37819         e.stopEvent();
37820         this.fireEvent("beforeclose", this, o);
37821         if(o.cancel !== true){
37822             this.tabPanel.removeTab(this.id);
37823         }
37824     },
37825     /**
37826      * The text displayed in the tooltip for the close icon.
37827      * @type String
37828      */
37829     closeText : "Close this tab"
37830 });