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                            cn : [
12939                                {
12940                                    tag : 'span',
12941                                    html : this.fieldLabel
12942                                },
12943                                {
12944                                   tag : 'i',
12945                                   cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12946                                   tooltip : 'This field is required'
12947                                }
12948                            ]
12949
12950                         },
12951                         combobox
12952
12953                     ];
12954                 
12955                 }
12956
12957         } else {
12958             
12959 //                Roo.log(" no label && no align");
12960                 cfg = combobox
12961                      
12962                 
12963         }
12964          
12965         var settings=this;
12966         ['xs','sm','md','lg'].map(function(size){
12967             if (settings[size]) {
12968                 cfg.cls += ' col-' + size + '-' + settings[size];
12969             }
12970         });
12971         
12972         return cfg;
12973         
12974     },
12975     
12976     _initEventsCalled : false,
12977     
12978     // private
12979     initEvents: function()
12980     {   
12981         if (this._initEventsCalled) { // as we call render... prevent looping...
12982             return;
12983         }
12984         this._initEventsCalled = true;
12985         
12986         if (!this.store) {
12987             throw "can not find store for combo";
12988         }
12989         
12990         this.store = Roo.factory(this.store, Roo.data);
12991         this.store.parent = this;
12992         
12993         // if we are building from html. then this element is so complex, that we can not really
12994         // use the rendered HTML.
12995         // so we have to trash and replace the previous code.
12996         if (Roo.XComponent.build_from_html) {
12997             
12998             // remove this element....
12999             var e = this.el.dom, k=0;
13000             while (e ) { e = e.previousSibling;  ++k;}
13001
13002             this.el.remove();
13003             
13004             this.el=false;
13005             this.rendered = false;
13006             
13007             this.render(this.parent().getChildContainer(true), k);
13008             
13009             
13010             
13011         }
13012         
13013         if(Roo.isIOS && this.useNativeIOS){
13014             this.initIOSView();
13015             return;
13016         }
13017         
13018         /*
13019          * Touch Devices
13020          */
13021         
13022         if(Roo.isTouch && this.mobileTouchView){
13023             this.initTouchView();
13024             return;
13025         }
13026         
13027         if(this.tickable){
13028             this.initTickableEvents();
13029             return;
13030         }
13031         
13032         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13033         
13034         if(this.hiddenName){
13035             
13036             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13037             
13038             this.hiddenField.dom.value =
13039                 this.hiddenValue !== undefined ? this.hiddenValue :
13040                 this.value !== undefined ? this.value : '';
13041
13042             // prevent input submission
13043             this.el.dom.removeAttribute('name');
13044             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13045              
13046              
13047         }
13048         //if(Roo.isGecko){
13049         //    this.el.dom.setAttribute('autocomplete', 'off');
13050         //}
13051         
13052         var cls = 'x-combo-list';
13053         
13054         //this.list = new Roo.Layer({
13055         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13056         //});
13057         
13058         var _this = this;
13059         
13060         (function(){
13061             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13062             _this.list.setWidth(lw);
13063         }).defer(100);
13064         
13065         this.list.on('mouseover', this.onViewOver, this);
13066         this.list.on('mousemove', this.onViewMove, this);
13067         
13068         this.list.on('scroll', this.onViewScroll, this);
13069         
13070         /*
13071         this.list.swallowEvent('mousewheel');
13072         this.assetHeight = 0;
13073
13074         if(this.title){
13075             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13076             this.assetHeight += this.header.getHeight();
13077         }
13078
13079         this.innerList = this.list.createChild({cls:cls+'-inner'});
13080         this.innerList.on('mouseover', this.onViewOver, this);
13081         this.innerList.on('mousemove', this.onViewMove, this);
13082         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13083         
13084         if(this.allowBlank && !this.pageSize && !this.disableClear){
13085             this.footer = this.list.createChild({cls:cls+'-ft'});
13086             this.pageTb = new Roo.Toolbar(this.footer);
13087            
13088         }
13089         if(this.pageSize){
13090             this.footer = this.list.createChild({cls:cls+'-ft'});
13091             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13092                     {pageSize: this.pageSize});
13093             
13094         }
13095         
13096         if (this.pageTb && this.allowBlank && !this.disableClear) {
13097             var _this = this;
13098             this.pageTb.add(new Roo.Toolbar.Fill(), {
13099                 cls: 'x-btn-icon x-btn-clear',
13100                 text: '&#160;',
13101                 handler: function()
13102                 {
13103                     _this.collapse();
13104                     _this.clearValue();
13105                     _this.onSelect(false, -1);
13106                 }
13107             });
13108         }
13109         if (this.footer) {
13110             this.assetHeight += this.footer.getHeight();
13111         }
13112         */
13113             
13114         if(!this.tpl){
13115             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13116         }
13117
13118         this.view = new Roo.View(this.list, this.tpl, {
13119             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13120         });
13121         //this.view.wrapEl.setDisplayed(false);
13122         this.view.on('click', this.onViewClick, this);
13123         
13124         
13125         this.store.on('beforeload', this.onBeforeLoad, this);
13126         this.store.on('load', this.onLoad, this);
13127         this.store.on('loadexception', this.onLoadException, this);
13128         /*
13129         if(this.resizable){
13130             this.resizer = new Roo.Resizable(this.list,  {
13131                pinned:true, handles:'se'
13132             });
13133             this.resizer.on('resize', function(r, w, h){
13134                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13135                 this.listWidth = w;
13136                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13137                 this.restrictHeight();
13138             }, this);
13139             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13140         }
13141         */
13142         if(!this.editable){
13143             this.editable = true;
13144             this.setEditable(false);
13145         }
13146         
13147         /*
13148         
13149         if (typeof(this.events.add.listeners) != 'undefined') {
13150             
13151             this.addicon = this.wrap.createChild(
13152                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13153        
13154             this.addicon.on('click', function(e) {
13155                 this.fireEvent('add', this);
13156             }, this);
13157         }
13158         if (typeof(this.events.edit.listeners) != 'undefined') {
13159             
13160             this.editicon = this.wrap.createChild(
13161                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13162             if (this.addicon) {
13163                 this.editicon.setStyle('margin-left', '40px');
13164             }
13165             this.editicon.on('click', function(e) {
13166                 
13167                 // we fire even  if inothing is selected..
13168                 this.fireEvent('edit', this, this.lastData );
13169                 
13170             }, this);
13171         }
13172         */
13173         
13174         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13175             "up" : function(e){
13176                 this.inKeyMode = true;
13177                 this.selectPrev();
13178             },
13179
13180             "down" : function(e){
13181                 if(!this.isExpanded()){
13182                     this.onTriggerClick();
13183                 }else{
13184                     this.inKeyMode = true;
13185                     this.selectNext();
13186                 }
13187             },
13188
13189             "enter" : function(e){
13190 //                this.onViewClick();
13191                 //return true;
13192                 this.collapse();
13193                 
13194                 if(this.fireEvent("specialkey", this, e)){
13195                     this.onViewClick(false);
13196                 }
13197                 
13198                 return true;
13199             },
13200
13201             "esc" : function(e){
13202                 this.collapse();
13203             },
13204
13205             "tab" : function(e){
13206                 this.collapse();
13207                 
13208                 if(this.fireEvent("specialkey", this, e)){
13209                     this.onViewClick(false);
13210                 }
13211                 
13212                 return true;
13213             },
13214
13215             scope : this,
13216
13217             doRelay : function(foo, bar, hname){
13218                 if(hname == 'down' || this.scope.isExpanded()){
13219                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13220                 }
13221                 return true;
13222             },
13223
13224             forceKeyDown: true
13225         });
13226         
13227         
13228         this.queryDelay = Math.max(this.queryDelay || 10,
13229                 this.mode == 'local' ? 10 : 250);
13230         
13231         
13232         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13233         
13234         if(this.typeAhead){
13235             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13236         }
13237         if(this.editable !== false){
13238             this.inputEl().on("keyup", this.onKeyUp, this);
13239         }
13240         if(this.forceSelection){
13241             this.inputEl().on('blur', this.doForce, this);
13242         }
13243         
13244         if(this.multiple){
13245             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13246             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13247         }
13248     },
13249     
13250     initTickableEvents: function()
13251     {   
13252         this.createList();
13253         
13254         if(this.hiddenName){
13255             
13256             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13257             
13258             this.hiddenField.dom.value =
13259                 this.hiddenValue !== undefined ? this.hiddenValue :
13260                 this.value !== undefined ? this.value : '';
13261
13262             // prevent input submission
13263             this.el.dom.removeAttribute('name');
13264             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13265              
13266              
13267         }
13268         
13269 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13270         
13271         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13272         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13273         if(this.triggerList){
13274             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13275         }
13276          
13277         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13278         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13279         
13280         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13281         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13282         
13283         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13284         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13285         
13286         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13287         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13288         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13289         
13290         this.okBtn.hide();
13291         this.cancelBtn.hide();
13292         
13293         var _this = this;
13294         
13295         (function(){
13296             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13297             _this.list.setWidth(lw);
13298         }).defer(100);
13299         
13300         this.list.on('mouseover', this.onViewOver, this);
13301         this.list.on('mousemove', this.onViewMove, this);
13302         
13303         this.list.on('scroll', this.onViewScroll, this);
13304         
13305         if(!this.tpl){
13306             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>';
13307         }
13308
13309         this.view = new Roo.View(this.list, this.tpl, {
13310             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13311         });
13312         
13313         //this.view.wrapEl.setDisplayed(false);
13314         this.view.on('click', this.onViewClick, this);
13315         
13316         
13317         
13318         this.store.on('beforeload', this.onBeforeLoad, this);
13319         this.store.on('load', this.onLoad, this);
13320         this.store.on('loadexception', this.onLoadException, this);
13321         
13322         if(this.editable){
13323             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13324                 "up" : function(e){
13325                     this.inKeyMode = true;
13326                     this.selectPrev();
13327                 },
13328
13329                 "down" : function(e){
13330                     this.inKeyMode = true;
13331                     this.selectNext();
13332                 },
13333
13334                 "enter" : function(e){
13335                     if(this.fireEvent("specialkey", this, e)){
13336                         this.onViewClick(false);
13337                     }
13338                     
13339                     return true;
13340                 },
13341
13342                 "esc" : function(e){
13343                     this.onTickableFooterButtonClick(e, false, false);
13344                 },
13345
13346                 "tab" : function(e){
13347                     this.fireEvent("specialkey", this, e);
13348                     
13349                     this.onTickableFooterButtonClick(e, false, false);
13350                     
13351                     return true;
13352                 },
13353
13354                 scope : this,
13355
13356                 doRelay : function(e, fn, key){
13357                     if(this.scope.isExpanded()){
13358                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13359                     }
13360                     return true;
13361                 },
13362
13363                 forceKeyDown: true
13364             });
13365         }
13366         
13367         this.queryDelay = Math.max(this.queryDelay || 10,
13368                 this.mode == 'local' ? 10 : 250);
13369         
13370         
13371         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13372         
13373         if(this.typeAhead){
13374             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13375         }
13376         
13377         if(this.editable !== false){
13378             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13379         }
13380         
13381         this.indicator = this.indicatorEl();
13382         
13383         if(this.indicator){
13384             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13385             this.indicator.hide();
13386         }
13387         
13388     },
13389
13390     onDestroy : function(){
13391         if(this.view){
13392             this.view.setStore(null);
13393             this.view.el.removeAllListeners();
13394             this.view.el.remove();
13395             this.view.purgeListeners();
13396         }
13397         if(this.list){
13398             this.list.dom.innerHTML  = '';
13399         }
13400         
13401         if(this.store){
13402             this.store.un('beforeload', this.onBeforeLoad, this);
13403             this.store.un('load', this.onLoad, this);
13404             this.store.un('loadexception', this.onLoadException, this);
13405         }
13406         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13407     },
13408
13409     // private
13410     fireKey : function(e){
13411         if(e.isNavKeyPress() && !this.list.isVisible()){
13412             this.fireEvent("specialkey", this, e);
13413         }
13414     },
13415
13416     // private
13417     onResize: function(w, h){
13418 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13419 //        
13420 //        if(typeof w != 'number'){
13421 //            // we do not handle it!?!?
13422 //            return;
13423 //        }
13424 //        var tw = this.trigger.getWidth();
13425 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13426 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13427 //        var x = w - tw;
13428 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13429 //            
13430 //        //this.trigger.setStyle('left', x+'px');
13431 //        
13432 //        if(this.list && this.listWidth === undefined){
13433 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13434 //            this.list.setWidth(lw);
13435 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13436 //        }
13437         
13438     
13439         
13440     },
13441
13442     /**
13443      * Allow or prevent the user from directly editing the field text.  If false is passed,
13444      * the user will only be able to select from the items defined in the dropdown list.  This method
13445      * is the runtime equivalent of setting the 'editable' config option at config time.
13446      * @param {Boolean} value True to allow the user to directly edit the field text
13447      */
13448     setEditable : function(value){
13449         if(value == this.editable){
13450             return;
13451         }
13452         this.editable = value;
13453         if(!value){
13454             this.inputEl().dom.setAttribute('readOnly', true);
13455             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13456             this.inputEl().addClass('x-combo-noedit');
13457         }else{
13458             this.inputEl().dom.setAttribute('readOnly', false);
13459             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13460             this.inputEl().removeClass('x-combo-noedit');
13461         }
13462     },
13463
13464     // private
13465     
13466     onBeforeLoad : function(combo,opts){
13467         if(!this.hasFocus){
13468             return;
13469         }
13470          if (!opts.add) {
13471             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13472          }
13473         this.restrictHeight();
13474         this.selectedIndex = -1;
13475     },
13476
13477     // private
13478     onLoad : function(){
13479         
13480         this.hasQuery = false;
13481         
13482         if(!this.hasFocus){
13483             return;
13484         }
13485         
13486         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13487             this.loading.hide();
13488         }
13489         
13490         if(this.store.getCount() > 0){
13491             
13492             this.expand();
13493             this.restrictHeight();
13494             if(this.lastQuery == this.allQuery){
13495                 if(this.editable && !this.tickable){
13496                     this.inputEl().dom.select();
13497                 }
13498                 
13499                 if(
13500                     !this.selectByValue(this.value, true) &&
13501                     this.autoFocus && 
13502                     (
13503                         !this.store.lastOptions ||
13504                         typeof(this.store.lastOptions.add) == 'undefined' || 
13505                         this.store.lastOptions.add != true
13506                     )
13507                 ){
13508                     this.select(0, true);
13509                 }
13510             }else{
13511                 if(this.autoFocus){
13512                     this.selectNext();
13513                 }
13514                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13515                     this.taTask.delay(this.typeAheadDelay);
13516                 }
13517             }
13518         }else{
13519             this.onEmptyResults();
13520         }
13521         
13522         //this.el.focus();
13523     },
13524     // private
13525     onLoadException : function()
13526     {
13527         this.hasQuery = false;
13528         
13529         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13530             this.loading.hide();
13531         }
13532         
13533         if(this.tickable && this.editable){
13534             return;
13535         }
13536         
13537         this.collapse();
13538         // only causes errors at present
13539         //Roo.log(this.store.reader.jsonData);
13540         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13541             // fixme
13542             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13543         //}
13544         
13545         
13546     },
13547     // private
13548     onTypeAhead : function(){
13549         if(this.store.getCount() > 0){
13550             var r = this.store.getAt(0);
13551             var newValue = r.data[this.displayField];
13552             var len = newValue.length;
13553             var selStart = this.getRawValue().length;
13554             
13555             if(selStart != len){
13556                 this.setRawValue(newValue);
13557                 this.selectText(selStart, newValue.length);
13558             }
13559         }
13560     },
13561
13562     // private
13563     onSelect : function(record, index){
13564         
13565         if(this.fireEvent('beforeselect', this, record, index) !== false){
13566         
13567             this.setFromData(index > -1 ? record.data : false);
13568             
13569             this.collapse();
13570             this.fireEvent('select', this, record, index);
13571         }
13572     },
13573
13574     /**
13575      * Returns the currently selected field value or empty string if no value is set.
13576      * @return {String} value The selected value
13577      */
13578     getValue : function()
13579     {
13580         if(Roo.isIOS && this.useNativeIOS){
13581             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13582         }
13583         
13584         if(this.multiple){
13585             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13586         }
13587         
13588         if(this.valueField){
13589             return typeof this.value != 'undefined' ? this.value : '';
13590         }else{
13591             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13592         }
13593     },
13594     
13595     getRawValue : function()
13596     {
13597         if(Roo.isIOS && this.useNativeIOS){
13598             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13599         }
13600         
13601         var v = this.inputEl().getValue();
13602         
13603         return v;
13604     },
13605
13606     /**
13607      * Clears any text/value currently set in the field
13608      */
13609     clearValue : function(){
13610         
13611         if(this.hiddenField){
13612             this.hiddenField.dom.value = '';
13613         }
13614         this.value = '';
13615         this.setRawValue('');
13616         this.lastSelectionText = '';
13617         this.lastData = false;
13618         
13619         var close = this.closeTriggerEl();
13620         
13621         if(close){
13622             close.hide();
13623         }
13624         
13625         this.validate();
13626         
13627     },
13628
13629     /**
13630      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13631      * will be displayed in the field.  If the value does not match the data value of an existing item,
13632      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13633      * Otherwise the field will be blank (although the value will still be set).
13634      * @param {String} value The value to match
13635      */
13636     setValue : function(v)
13637     {
13638         if(Roo.isIOS && this.useNativeIOS){
13639             this.setIOSValue(v);
13640             return;
13641         }
13642         
13643         if(this.multiple){
13644             this.syncValue();
13645             return;
13646         }
13647         
13648         var text = v;
13649         if(this.valueField){
13650             var r = this.findRecord(this.valueField, v);
13651             if(r){
13652                 text = r.data[this.displayField];
13653             }else if(this.valueNotFoundText !== undefined){
13654                 text = this.valueNotFoundText;
13655             }
13656         }
13657         this.lastSelectionText = text;
13658         if(this.hiddenField){
13659             this.hiddenField.dom.value = v;
13660         }
13661         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13662         this.value = v;
13663         
13664         var close = this.closeTriggerEl();
13665         
13666         if(close){
13667             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13668         }
13669         
13670         this.validate();
13671     },
13672     /**
13673      * @property {Object} the last set data for the element
13674      */
13675     
13676     lastData : false,
13677     /**
13678      * Sets the value of the field based on a object which is related to the record format for the store.
13679      * @param {Object} value the value to set as. or false on reset?
13680      */
13681     setFromData : function(o){
13682         
13683         if(this.multiple){
13684             this.addItem(o);
13685             return;
13686         }
13687             
13688         var dv = ''; // display value
13689         var vv = ''; // value value..
13690         this.lastData = o;
13691         if (this.displayField) {
13692             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13693         } else {
13694             // this is an error condition!!!
13695             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13696         }
13697         
13698         if(this.valueField){
13699             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13700         }
13701         
13702         var close = this.closeTriggerEl();
13703         
13704         if(close){
13705             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13706         }
13707         
13708         if(this.hiddenField){
13709             this.hiddenField.dom.value = vv;
13710             
13711             this.lastSelectionText = dv;
13712             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13713             this.value = vv;
13714             return;
13715         }
13716         // no hidden field.. - we store the value in 'value', but still display
13717         // display field!!!!
13718         this.lastSelectionText = dv;
13719         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13720         this.value = vv;
13721         
13722         
13723         
13724     },
13725     // private
13726     reset : function(){
13727         // overridden so that last data is reset..
13728         
13729         if(this.multiple){
13730             this.clearItem();
13731             return;
13732         }
13733         
13734         this.setValue(this.originalValue);
13735         //this.clearInvalid();
13736         this.lastData = false;
13737         if (this.view) {
13738             this.view.clearSelections();
13739         }
13740         
13741         this.validate();
13742     },
13743     // private
13744     findRecord : function(prop, value){
13745         var record;
13746         if(this.store.getCount() > 0){
13747             this.store.each(function(r){
13748                 if(r.data[prop] == value){
13749                     record = r;
13750                     return false;
13751                 }
13752                 return true;
13753             });
13754         }
13755         return record;
13756     },
13757     
13758     getName: function()
13759     {
13760         // returns hidden if it's set..
13761         if (!this.rendered) {return ''};
13762         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13763         
13764     },
13765     // private
13766     onViewMove : function(e, t){
13767         this.inKeyMode = false;
13768     },
13769
13770     // private
13771     onViewOver : function(e, t){
13772         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13773             return;
13774         }
13775         var item = this.view.findItemFromChild(t);
13776         
13777         if(item){
13778             var index = this.view.indexOf(item);
13779             this.select(index, false);
13780         }
13781     },
13782
13783     // private
13784     onViewClick : function(view, doFocus, el, e)
13785     {
13786         var index = this.view.getSelectedIndexes()[0];
13787         
13788         var r = this.store.getAt(index);
13789         
13790         if(this.tickable){
13791             
13792             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13793                 return;
13794             }
13795             
13796             var rm = false;
13797             var _this = this;
13798             
13799             Roo.each(this.tickItems, function(v,k){
13800                 
13801                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13802                     Roo.log(v);
13803                     _this.tickItems.splice(k, 1);
13804                     
13805                     if(typeof(e) == 'undefined' && view == false){
13806                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13807                     }
13808                     
13809                     rm = true;
13810                     return;
13811                 }
13812             });
13813             
13814             if(rm){
13815                 return;
13816             }
13817             
13818             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13819                 this.tickItems.push(r.data);
13820             }
13821             
13822             if(typeof(e) == 'undefined' && view == false){
13823                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13824             }
13825                     
13826             return;
13827         }
13828         
13829         if(r){
13830             this.onSelect(r, index);
13831         }
13832         if(doFocus !== false && !this.blockFocus){
13833             this.inputEl().focus();
13834         }
13835     },
13836
13837     // private
13838     restrictHeight : function(){
13839         //this.innerList.dom.style.height = '';
13840         //var inner = this.innerList.dom;
13841         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13842         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13843         //this.list.beginUpdate();
13844         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13845         this.list.alignTo(this.inputEl(), this.listAlign);
13846         this.list.alignTo(this.inputEl(), this.listAlign);
13847         //this.list.endUpdate();
13848     },
13849
13850     // private
13851     onEmptyResults : function(){
13852         
13853         if(this.tickable && this.editable){
13854             this.restrictHeight();
13855             return;
13856         }
13857         
13858         this.collapse();
13859     },
13860
13861     /**
13862      * Returns true if the dropdown list is expanded, else false.
13863      */
13864     isExpanded : function(){
13865         return this.list.isVisible();
13866     },
13867
13868     /**
13869      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13870      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13871      * @param {String} value The data value of the item to select
13872      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13873      * selected item if it is not currently in view (defaults to true)
13874      * @return {Boolean} True if the value matched an item in the list, else false
13875      */
13876     selectByValue : function(v, scrollIntoView){
13877         if(v !== undefined && v !== null){
13878             var r = this.findRecord(this.valueField || this.displayField, v);
13879             if(r){
13880                 this.select(this.store.indexOf(r), scrollIntoView);
13881                 return true;
13882             }
13883         }
13884         return false;
13885     },
13886
13887     /**
13888      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13890      * @param {Number} index The zero-based index of the list item to select
13891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13892      * selected item if it is not currently in view (defaults to true)
13893      */
13894     select : function(index, scrollIntoView){
13895         this.selectedIndex = index;
13896         this.view.select(index);
13897         if(scrollIntoView !== false){
13898             var el = this.view.getNode(index);
13899             /*
13900              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13901              */
13902             if(el){
13903                 this.list.scrollChildIntoView(el, false);
13904             }
13905         }
13906     },
13907
13908     // private
13909     selectNext : function(){
13910         var ct = this.store.getCount();
13911         if(ct > 0){
13912             if(this.selectedIndex == -1){
13913                 this.select(0);
13914             }else if(this.selectedIndex < ct-1){
13915                 this.select(this.selectedIndex+1);
13916             }
13917         }
13918     },
13919
13920     // private
13921     selectPrev : function(){
13922         var ct = this.store.getCount();
13923         if(ct > 0){
13924             if(this.selectedIndex == -1){
13925                 this.select(0);
13926             }else if(this.selectedIndex != 0){
13927                 this.select(this.selectedIndex-1);
13928             }
13929         }
13930     },
13931
13932     // private
13933     onKeyUp : function(e){
13934         if(this.editable !== false && !e.isSpecialKey()){
13935             this.lastKey = e.getKey();
13936             this.dqTask.delay(this.queryDelay);
13937         }
13938     },
13939
13940     // private
13941     validateBlur : function(){
13942         return !this.list || !this.list.isVisible();   
13943     },
13944
13945     // private
13946     initQuery : function(){
13947         
13948         var v = this.getRawValue();
13949         
13950         if(this.tickable && this.editable){
13951             v = this.tickableInputEl().getValue();
13952         }
13953         
13954         this.doQuery(v);
13955     },
13956
13957     // private
13958     doForce : function(){
13959         if(this.inputEl().dom.value.length > 0){
13960             this.inputEl().dom.value =
13961                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13962              
13963         }
13964     },
13965
13966     /**
13967      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13968      * query allowing the query action to be canceled if needed.
13969      * @param {String} query The SQL query to execute
13970      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13971      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13972      * saved in the current store (defaults to false)
13973      */
13974     doQuery : function(q, forceAll){
13975         
13976         if(q === undefined || q === null){
13977             q = '';
13978         }
13979         var qe = {
13980             query: q,
13981             forceAll: forceAll,
13982             combo: this,
13983             cancel:false
13984         };
13985         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13986             return false;
13987         }
13988         q = qe.query;
13989         
13990         forceAll = qe.forceAll;
13991         if(forceAll === true || (q.length >= this.minChars)){
13992             
13993             this.hasQuery = true;
13994             
13995             if(this.lastQuery != q || this.alwaysQuery){
13996                 this.lastQuery = q;
13997                 if(this.mode == 'local'){
13998                     this.selectedIndex = -1;
13999                     if(forceAll){
14000                         this.store.clearFilter();
14001                     }else{
14002                         
14003                         if(this.specialFilter){
14004                             this.fireEvent('specialfilter', this);
14005                             this.onLoad();
14006                             return;
14007                         }
14008                         
14009                         this.store.filter(this.displayField, q);
14010                     }
14011                     
14012                     this.store.fireEvent("datachanged", this.store);
14013                     
14014                     this.onLoad();
14015                     
14016                     
14017                 }else{
14018                     
14019                     this.store.baseParams[this.queryParam] = q;
14020                     
14021                     var options = {params : this.getParams(q)};
14022                     
14023                     if(this.loadNext){
14024                         options.add = true;
14025                         options.params.start = this.page * this.pageSize;
14026                     }
14027                     
14028                     this.store.load(options);
14029                     
14030                     /*
14031                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14032                      *  we should expand the list on onLoad
14033                      *  so command out it
14034                      */
14035 //                    this.expand();
14036                 }
14037             }else{
14038                 this.selectedIndex = -1;
14039                 this.onLoad();   
14040             }
14041         }
14042         
14043         this.loadNext = false;
14044     },
14045     
14046     // private
14047     getParams : function(q){
14048         var p = {};
14049         //p[this.queryParam] = q;
14050         
14051         if(this.pageSize){
14052             p.start = 0;
14053             p.limit = this.pageSize;
14054         }
14055         return p;
14056     },
14057
14058     /**
14059      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14060      */
14061     collapse : function(){
14062         if(!this.isExpanded()){
14063             return;
14064         }
14065         
14066         this.list.hide();
14067         
14068         this.hasFocus = false;
14069         
14070         if(this.tickable){
14071             this.okBtn.hide();
14072             this.cancelBtn.hide();
14073             this.trigger.show();
14074             
14075             if(this.editable){
14076                 this.tickableInputEl().dom.value = '';
14077                 this.tickableInputEl().blur();
14078             }
14079             
14080         }
14081         
14082         Roo.get(document).un('mousedown', this.collapseIf, this);
14083         Roo.get(document).un('mousewheel', this.collapseIf, this);
14084         if (!this.editable) {
14085             Roo.get(document).un('keydown', this.listKeyPress, this);
14086         }
14087         this.fireEvent('collapse', this);
14088         
14089         this.validate();
14090     },
14091
14092     // private
14093     collapseIf : function(e){
14094         var in_combo  = e.within(this.el);
14095         var in_list =  e.within(this.list);
14096         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14097         
14098         if (in_combo || in_list || is_list) {
14099             //e.stopPropagation();
14100             return;
14101         }
14102         
14103         if(this.tickable){
14104             this.onTickableFooterButtonClick(e, false, false);
14105         }
14106
14107         this.collapse();
14108         
14109     },
14110
14111     /**
14112      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14113      */
14114     expand : function(){
14115        
14116         if(this.isExpanded() || !this.hasFocus){
14117             return;
14118         }
14119         
14120         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14121         this.list.setWidth(lw);
14122         
14123         Roo.log('expand');
14124         
14125         this.list.show();
14126         
14127         this.restrictHeight();
14128         
14129         if(this.tickable){
14130             
14131             this.tickItems = Roo.apply([], this.item);
14132             
14133             this.okBtn.show();
14134             this.cancelBtn.show();
14135             this.trigger.hide();
14136             
14137             if(this.editable){
14138                 this.tickableInputEl().focus();
14139             }
14140             
14141         }
14142         
14143         Roo.get(document).on('mousedown', this.collapseIf, this);
14144         Roo.get(document).on('mousewheel', this.collapseIf, this);
14145         if (!this.editable) {
14146             Roo.get(document).on('keydown', this.listKeyPress, this);
14147         }
14148         
14149         this.fireEvent('expand', this);
14150     },
14151
14152     // private
14153     // Implements the default empty TriggerField.onTriggerClick function
14154     onTriggerClick : function(e)
14155     {
14156         Roo.log('trigger click');
14157         
14158         if(this.disabled || !this.triggerList){
14159             return;
14160         }
14161         
14162         this.page = 0;
14163         this.loadNext = false;
14164         
14165         if(this.isExpanded()){
14166             this.collapse();
14167             if (!this.blockFocus) {
14168                 this.inputEl().focus();
14169             }
14170             
14171         }else {
14172             this.hasFocus = true;
14173             if(this.triggerAction == 'all') {
14174                 this.doQuery(this.allQuery, true);
14175             } else {
14176                 this.doQuery(this.getRawValue());
14177             }
14178             if (!this.blockFocus) {
14179                 this.inputEl().focus();
14180             }
14181         }
14182     },
14183     
14184     onTickableTriggerClick : function(e)
14185     {
14186         if(this.disabled){
14187             return;
14188         }
14189         
14190         this.page = 0;
14191         this.loadNext = false;
14192         this.hasFocus = true;
14193         
14194         if(this.triggerAction == 'all') {
14195             this.doQuery(this.allQuery, true);
14196         } else {
14197             this.doQuery(this.getRawValue());
14198         }
14199     },
14200     
14201     onSearchFieldClick : function(e)
14202     {
14203         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14204             this.onTickableFooterButtonClick(e, false, false);
14205             return;
14206         }
14207         
14208         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14209             return;
14210         }
14211         
14212         this.page = 0;
14213         this.loadNext = false;
14214         this.hasFocus = true;
14215         
14216         if(this.triggerAction == 'all') {
14217             this.doQuery(this.allQuery, true);
14218         } else {
14219             this.doQuery(this.getRawValue());
14220         }
14221     },
14222     
14223     listKeyPress : function(e)
14224     {
14225         //Roo.log('listkeypress');
14226         // scroll to first matching element based on key pres..
14227         if (e.isSpecialKey()) {
14228             return false;
14229         }
14230         var k = String.fromCharCode(e.getKey()).toUpperCase();
14231         //Roo.log(k);
14232         var match  = false;
14233         var csel = this.view.getSelectedNodes();
14234         var cselitem = false;
14235         if (csel.length) {
14236             var ix = this.view.indexOf(csel[0]);
14237             cselitem  = this.store.getAt(ix);
14238             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14239                 cselitem = false;
14240             }
14241             
14242         }
14243         
14244         this.store.each(function(v) { 
14245             if (cselitem) {
14246                 // start at existing selection.
14247                 if (cselitem.id == v.id) {
14248                     cselitem = false;
14249                 }
14250                 return true;
14251             }
14252                 
14253             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14254                 match = this.store.indexOf(v);
14255                 return false;
14256             }
14257             return true;
14258         }, this);
14259         
14260         if (match === false) {
14261             return true; // no more action?
14262         }
14263         // scroll to?
14264         this.view.select(match);
14265         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14266         sn.scrollIntoView(sn.dom.parentNode, false);
14267     },
14268     
14269     onViewScroll : function(e, t){
14270         
14271         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){
14272             return;
14273         }
14274         
14275         this.hasQuery = true;
14276         
14277         this.loading = this.list.select('.loading', true).first();
14278         
14279         if(this.loading === null){
14280             this.list.createChild({
14281                 tag: 'div',
14282                 cls: 'loading roo-select2-more-results roo-select2-active',
14283                 html: 'Loading more results...'
14284             });
14285             
14286             this.loading = this.list.select('.loading', true).first();
14287             
14288             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14289             
14290             this.loading.hide();
14291         }
14292         
14293         this.loading.show();
14294         
14295         var _combo = this;
14296         
14297         this.page++;
14298         this.loadNext = true;
14299         
14300         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14301         
14302         return;
14303     },
14304     
14305     addItem : function(o)
14306     {   
14307         var dv = ''; // display value
14308         
14309         if (this.displayField) {
14310             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14311         } else {
14312             // this is an error condition!!!
14313             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14314         }
14315         
14316         if(!dv.length){
14317             return;
14318         }
14319         
14320         var choice = this.choices.createChild({
14321             tag: 'li',
14322             cls: 'roo-select2-search-choice',
14323             cn: [
14324                 {
14325                     tag: 'div',
14326                     html: dv
14327                 },
14328                 {
14329                     tag: 'a',
14330                     href: '#',
14331                     cls: 'roo-select2-search-choice-close fa fa-times',
14332                     tabindex: '-1'
14333                 }
14334             ]
14335             
14336         }, this.searchField);
14337         
14338         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14339         
14340         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14341         
14342         this.item.push(o);
14343         
14344         this.lastData = o;
14345         
14346         this.syncValue();
14347         
14348         this.inputEl().dom.value = '';
14349         
14350         this.validate();
14351     },
14352     
14353     onRemoveItem : function(e, _self, o)
14354     {
14355         e.preventDefault();
14356         
14357         this.lastItem = Roo.apply([], this.item);
14358         
14359         var index = this.item.indexOf(o.data) * 1;
14360         
14361         if( index < 0){
14362             Roo.log('not this item?!');
14363             return;
14364         }
14365         
14366         this.item.splice(index, 1);
14367         o.item.remove();
14368         
14369         this.syncValue();
14370         
14371         this.fireEvent('remove', this, e);
14372         
14373         this.validate();
14374         
14375     },
14376     
14377     syncValue : function()
14378     {
14379         if(!this.item.length){
14380             this.clearValue();
14381             return;
14382         }
14383             
14384         var value = [];
14385         var _this = this;
14386         Roo.each(this.item, function(i){
14387             if(_this.valueField){
14388                 value.push(i[_this.valueField]);
14389                 return;
14390             }
14391
14392             value.push(i);
14393         });
14394
14395         this.value = value.join(',');
14396
14397         if(this.hiddenField){
14398             this.hiddenField.dom.value = this.value;
14399         }
14400         
14401         this.store.fireEvent("datachanged", this.store);
14402         
14403         this.validate();
14404     },
14405     
14406     clearItem : function()
14407     {
14408         if(!this.multiple){
14409             return;
14410         }
14411         
14412         this.item = [];
14413         
14414         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14415            c.remove();
14416         });
14417         
14418         this.syncValue();
14419         
14420         this.validate();
14421         
14422         if(this.tickable && !Roo.isTouch){
14423             this.view.refresh();
14424         }
14425     },
14426     
14427     inputEl: function ()
14428     {
14429         if(Roo.isIOS && this.useNativeIOS){
14430             return this.el.select('select.roo-ios-select', true).first();
14431         }
14432         
14433         if(Roo.isTouch && this.mobileTouchView){
14434             return this.el.select('input.form-control',true).first();
14435         }
14436         
14437         if(this.tickable){
14438             return this.searchField;
14439         }
14440         
14441         return this.el.select('input.form-control',true).first();
14442     },
14443     
14444     onTickableFooterButtonClick : function(e, btn, el)
14445     {
14446         e.preventDefault();
14447         
14448         this.lastItem = Roo.apply([], this.item);
14449         
14450         if(btn && btn.name == 'cancel'){
14451             this.tickItems = Roo.apply([], this.item);
14452             this.collapse();
14453             return;
14454         }
14455         
14456         this.clearItem();
14457         
14458         var _this = this;
14459         
14460         Roo.each(this.tickItems, function(o){
14461             _this.addItem(o);
14462         });
14463         
14464         this.collapse();
14465         
14466     },
14467     
14468     validate : function()
14469     {
14470         var v = this.getRawValue();
14471         
14472         if(this.multiple){
14473             v = this.getValue();
14474         }
14475         
14476         if(this.disabled || this.allowBlank || v.length){
14477             this.markValid();
14478             return true;
14479         }
14480         
14481         this.markInvalid();
14482         return false;
14483     },
14484     
14485     tickableInputEl : function()
14486     {
14487         if(!this.tickable || !this.editable){
14488             return this.inputEl();
14489         }
14490         
14491         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14492     },
14493     
14494     
14495     getAutoCreateTouchView : function()
14496     {
14497         var id = Roo.id();
14498         
14499         var cfg = {
14500             cls: 'form-group' //input-group
14501         };
14502         
14503         var input =  {
14504             tag: 'input',
14505             id : id,
14506             type : this.inputType,
14507             cls : 'form-control x-combo-noedit',
14508             autocomplete: 'new-password',
14509             placeholder : this.placeholder || '',
14510             readonly : true
14511         };
14512         
14513         if (this.name) {
14514             input.name = this.name;
14515         }
14516         
14517         if (this.size) {
14518             input.cls += ' input-' + this.size;
14519         }
14520         
14521         if (this.disabled) {
14522             input.disabled = true;
14523         }
14524         
14525         var inputblock = {
14526             cls : '',
14527             cn : [
14528                 input
14529             ]
14530         };
14531         
14532         if(this.before){
14533             inputblock.cls += ' input-group';
14534             
14535             inputblock.cn.unshift({
14536                 tag :'span',
14537                 cls : 'input-group-addon',
14538                 html : this.before
14539             });
14540         }
14541         
14542         if(this.removable && !this.multiple){
14543             inputblock.cls += ' roo-removable';
14544             
14545             inputblock.cn.push({
14546                 tag: 'button',
14547                 html : 'x',
14548                 cls : 'roo-combo-removable-btn close'
14549             });
14550         }
14551
14552         if(this.hasFeedback && !this.allowBlank){
14553             
14554             inputblock.cls += ' has-feedback';
14555             
14556             inputblock.cn.push({
14557                 tag: 'span',
14558                 cls: 'glyphicon form-control-feedback'
14559             });
14560             
14561         }
14562         
14563         if (this.after) {
14564             
14565             inputblock.cls += (this.before) ? '' : ' input-group';
14566             
14567             inputblock.cn.push({
14568                 tag :'span',
14569                 cls : 'input-group-addon',
14570                 html : this.after
14571             });
14572         }
14573
14574         var box = {
14575             tag: 'div',
14576             cn: [
14577                 {
14578                     tag: 'input',
14579                     type : 'hidden',
14580                     cls: 'form-hidden-field'
14581                 },
14582                 inputblock
14583             ]
14584             
14585         };
14586         
14587         if(this.multiple){
14588             box = {
14589                 tag: 'div',
14590                 cn: [
14591                     {
14592                         tag: 'input',
14593                         type : 'hidden',
14594                         cls: 'form-hidden-field'
14595                     },
14596                     {
14597                         tag: 'ul',
14598                         cls: 'roo-select2-choices',
14599                         cn:[
14600                             {
14601                                 tag: 'li',
14602                                 cls: 'roo-select2-search-field',
14603                                 cn: [
14604
14605                                     inputblock
14606                                 ]
14607                             }
14608                         ]
14609                     }
14610                 ]
14611             }
14612         };
14613         
14614         var combobox = {
14615             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14616             cn: [
14617                 box
14618             ]
14619         };
14620         
14621         if(!this.multiple && this.showToggleBtn){
14622             
14623             var caret = {
14624                         tag: 'span',
14625                         cls: 'caret'
14626             };
14627             
14628             if (this.caret != false) {
14629                 caret = {
14630                      tag: 'i',
14631                      cls: 'fa fa-' + this.caret
14632                 };
14633                 
14634             }
14635             
14636             combobox.cn.push({
14637                 tag :'span',
14638                 cls : 'input-group-addon btn dropdown-toggle',
14639                 cn : [
14640                     caret,
14641                     {
14642                         tag: 'span',
14643                         cls: 'combobox-clear',
14644                         cn  : [
14645                             {
14646                                 tag : 'i',
14647                                 cls: 'icon-remove'
14648                             }
14649                         ]
14650                     }
14651                 ]
14652
14653             })
14654         }
14655         
14656         if(this.multiple){
14657             combobox.cls += ' roo-select2-container-multi';
14658         }
14659         
14660         var align = this.labelAlign || this.parentLabelAlign();
14661         
14662         if (align ==='left' && this.fieldLabel.length) {
14663
14664             cfg.cn = [
14665                 {
14666                    tag : 'i',
14667                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14668                    tooltip : 'This field is required'
14669                 },
14670                 {
14671                     tag: 'label',
14672                     cls : 'control-label',
14673                     html : this.fieldLabel
14674
14675                 },
14676                 {
14677                     cls : '', 
14678                     cn: [
14679                         combobox
14680                     ]
14681                 }
14682             ];
14683             
14684             var labelCfg = cfg.cn[1];
14685             var contentCfg = cfg.cn[2];
14686             
14687
14688             if(this.indicatorpos == 'right'){
14689                 cfg.cn = [
14690                     {
14691                         tag: 'label',
14692                         cls : 'control-label',
14693                         html : this.fieldLabel,
14694                         cn : [
14695                             {
14696                                tag : 'i',
14697                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14698                                tooltip : 'This field is required'
14699                             }
14700                         ]
14701                     },
14702                     {
14703                         cls : '', 
14704                         cn: [
14705                             combobox
14706                         ]
14707                     }
14708                 ];
14709             }
14710             
14711             labelCfg = cfg.cn[0];
14712             contentCfg = cfg.cn[2];
14713             
14714             if(this.labelWidth > 12){
14715                 labelCfg.style = "width: " + this.labelWidth + 'px';
14716             }
14717             
14718             if(this.labelWidth < 13 && this.labelmd == 0){
14719                 this.labelmd = this.labelWidth;
14720             }
14721             
14722             if(this.labellg > 0){
14723                 labelCfg.cls += ' col-lg-' + this.labellg;
14724                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14725             }
14726             
14727             if(this.labelmd > 0){
14728                 labelCfg.cls += ' col-md-' + this.labelmd;
14729                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14730             }
14731             
14732             if(this.labelsm > 0){
14733                 labelCfg.cls += ' col-sm-' + this.labelsm;
14734                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14735             }
14736             
14737             if(this.labelxs > 0){
14738                 labelCfg.cls += ' col-xs-' + this.labelxs;
14739                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14740             }
14741                 
14742                 
14743         } else if ( this.fieldLabel.length) {
14744             cfg.cn = [
14745                 {
14746                    tag : 'i',
14747                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14748                    tooltip : 'This field is required'
14749                 },
14750                 {
14751                     tag: 'label',
14752                     cls : 'control-label',
14753                     html : this.fieldLabel
14754
14755                 },
14756                 {
14757                     cls : '', 
14758                     cn: [
14759                         combobox
14760                     ]
14761                 }
14762             ];
14763             
14764             if(this.indicatorpos == 'right'){
14765                 cfg.cn = [
14766                     {
14767                         tag: 'label',
14768                         cls : 'control-label',
14769                         html : this.fieldLabel,
14770                         cn : [
14771                             {
14772                                tag : 'i',
14773                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14774                                tooltip : 'This field is required'
14775                             }
14776                         ]
14777                     },
14778                     {
14779                         cls : '', 
14780                         cn: [
14781                             combobox
14782                         ]
14783                     }
14784                 ];
14785             }
14786         } else {
14787             cfg.cn = combobox;    
14788         }
14789         
14790         
14791         var settings = this;
14792         
14793         ['xs','sm','md','lg'].map(function(size){
14794             if (settings[size]) {
14795                 cfg.cls += ' col-' + size + '-' + settings[size];
14796             }
14797         });
14798         
14799         return cfg;
14800     },
14801     
14802     initTouchView : function()
14803     {
14804         this.renderTouchView();
14805         
14806         this.touchViewEl.on('scroll', function(){
14807             this.el.dom.scrollTop = 0;
14808         }, this);
14809         
14810         this.originalValue = this.getValue();
14811         
14812         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14813         
14814         this.inputEl().on("click", this.showTouchView, this);
14815         if (this.triggerEl) {
14816             this.triggerEl.on("click", this.showTouchView, this);
14817         }
14818         
14819         
14820         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14821         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14822         
14823         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14824         
14825         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14826         this.store.on('load', this.onTouchViewLoad, this);
14827         this.store.on('loadexception', this.onTouchViewLoadException, this);
14828         
14829         if(this.hiddenName){
14830             
14831             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14832             
14833             this.hiddenField.dom.value =
14834                 this.hiddenValue !== undefined ? this.hiddenValue :
14835                 this.value !== undefined ? this.value : '';
14836         
14837             this.el.dom.removeAttribute('name');
14838             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14839         }
14840         
14841         if(this.multiple){
14842             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14843             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14844         }
14845         
14846         if(this.removable && !this.multiple){
14847             var close = this.closeTriggerEl();
14848             if(close){
14849                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14850                 close.on('click', this.removeBtnClick, this, close);
14851             }
14852         }
14853         /*
14854          * fix the bug in Safari iOS8
14855          */
14856         this.inputEl().on("focus", function(e){
14857             document.activeElement.blur();
14858         }, this);
14859         
14860         return;
14861         
14862         
14863     },
14864     
14865     renderTouchView : function()
14866     {
14867         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14868         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14869         
14870         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14871         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14872         
14873         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14874         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14875         this.touchViewBodyEl.setStyle('overflow', 'auto');
14876         
14877         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14878         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14879         
14880         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14881         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14882         
14883     },
14884     
14885     showTouchView : function()
14886     {
14887         if(this.disabled){
14888             return;
14889         }
14890         
14891         this.touchViewHeaderEl.hide();
14892
14893         if(this.modalTitle.length){
14894             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14895             this.touchViewHeaderEl.show();
14896         }
14897
14898         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14899         this.touchViewEl.show();
14900
14901         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14902         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14903                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14904
14905         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14906
14907         if(this.modalTitle.length){
14908             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14909         }
14910         
14911         this.touchViewBodyEl.setHeight(bodyHeight);
14912
14913         if(this.animate){
14914             var _this = this;
14915             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14916         }else{
14917             this.touchViewEl.addClass('in');
14918         }
14919
14920         this.doTouchViewQuery();
14921         
14922     },
14923     
14924     hideTouchView : function()
14925     {
14926         this.touchViewEl.removeClass('in');
14927
14928         if(this.animate){
14929             var _this = this;
14930             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14931         }else{
14932             this.touchViewEl.setStyle('display', 'none');
14933         }
14934         
14935     },
14936     
14937     setTouchViewValue : function()
14938     {
14939         if(this.multiple){
14940             this.clearItem();
14941         
14942             var _this = this;
14943
14944             Roo.each(this.tickItems, function(o){
14945                 this.addItem(o);
14946             }, this);
14947         }
14948         
14949         this.hideTouchView();
14950     },
14951     
14952     doTouchViewQuery : function()
14953     {
14954         var qe = {
14955             query: '',
14956             forceAll: true,
14957             combo: this,
14958             cancel:false
14959         };
14960         
14961         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14962             return false;
14963         }
14964         
14965         if(!this.alwaysQuery || this.mode == 'local'){
14966             this.onTouchViewLoad();
14967             return;
14968         }
14969         
14970         this.store.load();
14971     },
14972     
14973     onTouchViewBeforeLoad : function(combo,opts)
14974     {
14975         return;
14976     },
14977
14978     // private
14979     onTouchViewLoad : function()
14980     {
14981         if(this.store.getCount() < 1){
14982             this.onTouchViewEmptyResults();
14983             return;
14984         }
14985         
14986         this.clearTouchView();
14987         
14988         var rawValue = this.getRawValue();
14989         
14990         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14991         
14992         this.tickItems = [];
14993         
14994         this.store.data.each(function(d, rowIndex){
14995             var row = this.touchViewListGroup.createChild(template);
14996             
14997             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14998                 row.addClass(d.data.cls);
14999             }
15000             
15001             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15002                 var cfg = {
15003                     data : d.data,
15004                     html : d.data[this.displayField]
15005                 };
15006                 
15007                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15008                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15009                 }
15010             }
15011             row.removeClass('selected');
15012             if(!this.multiple && this.valueField &&
15013                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15014             {
15015                 // radio buttons..
15016                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15017                 row.addClass('selected');
15018             }
15019             
15020             if(this.multiple && this.valueField &&
15021                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15022             {
15023                 
15024                 // checkboxes...
15025                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15026                 this.tickItems.push(d.data);
15027             }
15028             
15029             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15030             
15031         }, this);
15032         
15033         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15034         
15035         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15036
15037         if(this.modalTitle.length){
15038             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15039         }
15040
15041         var listHeight = this.touchViewListGroup.getHeight();
15042         
15043         var _this = this;
15044         
15045         if(firstChecked && listHeight > bodyHeight){
15046             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15047         }
15048         
15049     },
15050     
15051     onTouchViewLoadException : function()
15052     {
15053         this.hideTouchView();
15054     },
15055     
15056     onTouchViewEmptyResults : function()
15057     {
15058         this.clearTouchView();
15059         
15060         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15061         
15062         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15063         
15064     },
15065     
15066     clearTouchView : function()
15067     {
15068         this.touchViewListGroup.dom.innerHTML = '';
15069     },
15070     
15071     onTouchViewClick : function(e, el, o)
15072     {
15073         e.preventDefault();
15074         
15075         var row = o.row;
15076         var rowIndex = o.rowIndex;
15077         
15078         var r = this.store.getAt(rowIndex);
15079         
15080         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15081             
15082             if(!this.multiple){
15083                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15084                     c.dom.removeAttribute('checked');
15085                 }, this);
15086
15087                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15088
15089                 this.setFromData(r.data);
15090
15091                 var close = this.closeTriggerEl();
15092
15093                 if(close){
15094                     close.show();
15095                 }
15096
15097                 this.hideTouchView();
15098
15099                 this.fireEvent('select', this, r, rowIndex);
15100
15101                 return;
15102             }
15103
15104             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15105                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15106                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15107                 return;
15108             }
15109
15110             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15111             this.addItem(r.data);
15112             this.tickItems.push(r.data);
15113         }
15114     },
15115     
15116     getAutoCreateNativeIOS : function()
15117     {
15118         var cfg = {
15119             cls: 'form-group' //input-group,
15120         };
15121         
15122         var combobox =  {
15123             tag: 'select',
15124             cls : 'roo-ios-select'
15125         };
15126         
15127         if (this.name) {
15128             combobox.name = this.name;
15129         }
15130         
15131         if (this.disabled) {
15132             combobox.disabled = true;
15133         }
15134         
15135         var settings = this;
15136         
15137         ['xs','sm','md','lg'].map(function(size){
15138             if (settings[size]) {
15139                 cfg.cls += ' col-' + size + '-' + settings[size];
15140             }
15141         });
15142         
15143         cfg.cn = combobox;
15144         
15145         return cfg;
15146         
15147     },
15148     
15149     initIOSView : function()
15150     {
15151         this.store.on('load', this.onIOSViewLoad, this);
15152         
15153         return;
15154     },
15155     
15156     onIOSViewLoad : function()
15157     {
15158         if(this.store.getCount() < 1){
15159             return;
15160         }
15161         
15162         this.clearIOSView();
15163         
15164         if(this.allowBlank) {
15165             
15166             var default_text = '-- SELECT --';
15167             
15168             var opt = this.inputEl().createChild({
15169                 tag: 'option',
15170                 value : 0,
15171                 html : default_text
15172             });
15173             
15174             var o = {};
15175             o[this.valueField] = 0;
15176             o[this.displayField] = default_text;
15177             
15178             this.ios_options.push({
15179                 data : o,
15180                 el : opt
15181             });
15182             
15183         }
15184         
15185         this.store.data.each(function(d, rowIndex){
15186             
15187             var html = '';
15188             
15189             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15190                 html = d.data[this.displayField];
15191             }
15192             
15193             var value = '';
15194             
15195             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15196                 value = d.data[this.valueField];
15197             }
15198             
15199             var option = {
15200                 tag: 'option',
15201                 value : value,
15202                 html : html
15203             };
15204             
15205             if(this.value == d.data[this.valueField]){
15206                 option['selected'] = true;
15207             }
15208             
15209             var opt = this.inputEl().createChild(option);
15210             
15211             this.ios_options.push({
15212                 data : d.data,
15213                 el : opt
15214             });
15215             
15216         }, this);
15217         
15218         this.inputEl().on('change', function(){
15219            this.fireEvent('select', this);
15220         }, this);
15221         
15222     },
15223     
15224     clearIOSView: function()
15225     {
15226         this.inputEl().dom.innerHTML = '';
15227         
15228         this.ios_options = [];
15229     },
15230     
15231     setIOSValue: function(v)
15232     {
15233         this.value = v;
15234         
15235         if(!this.ios_options){
15236             return;
15237         }
15238         
15239         Roo.each(this.ios_options, function(opts){
15240            
15241            opts.el.dom.removeAttribute('selected');
15242            
15243            if(opts.data[this.valueField] != v){
15244                return;
15245            }
15246            
15247            opts.el.dom.setAttribute('selected', true);
15248            
15249         }, this);
15250     }
15251
15252     /** 
15253     * @cfg {Boolean} grow 
15254     * @hide 
15255     */
15256     /** 
15257     * @cfg {Number} growMin 
15258     * @hide 
15259     */
15260     /** 
15261     * @cfg {Number} growMax 
15262     * @hide 
15263     */
15264     /**
15265      * @hide
15266      * @method autoSize
15267      */
15268 });
15269
15270 Roo.apply(Roo.bootstrap.ComboBox,  {
15271     
15272     header : {
15273         tag: 'div',
15274         cls: 'modal-header',
15275         cn: [
15276             {
15277                 tag: 'h4',
15278                 cls: 'modal-title'
15279             }
15280         ]
15281     },
15282     
15283     body : {
15284         tag: 'div',
15285         cls: 'modal-body',
15286         cn: [
15287             {
15288                 tag: 'ul',
15289                 cls: 'list-group'
15290             }
15291         ]
15292     },
15293     
15294     listItemRadio : {
15295         tag: 'li',
15296         cls: 'list-group-item',
15297         cn: [
15298             {
15299                 tag: 'span',
15300                 cls: 'roo-combobox-list-group-item-value'
15301             },
15302             {
15303                 tag: 'div',
15304                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15305                 cn: [
15306                     {
15307                         tag: 'input',
15308                         type: 'radio'
15309                     },
15310                     {
15311                         tag: 'label'
15312                     }
15313                 ]
15314             }
15315         ]
15316     },
15317     
15318     listItemCheckbox : {
15319         tag: 'li',
15320         cls: 'list-group-item',
15321         cn: [
15322             {
15323                 tag: 'span',
15324                 cls: 'roo-combobox-list-group-item-value'
15325             },
15326             {
15327                 tag: 'div',
15328                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15329                 cn: [
15330                     {
15331                         tag: 'input',
15332                         type: 'checkbox'
15333                     },
15334                     {
15335                         tag: 'label'
15336                     }
15337                 ]
15338             }
15339         ]
15340     },
15341     
15342     emptyResult : {
15343         tag: 'div',
15344         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15345     },
15346     
15347     footer : {
15348         tag: 'div',
15349         cls: 'modal-footer',
15350         cn: [
15351             {
15352                 tag: 'div',
15353                 cls: 'row',
15354                 cn: [
15355                     {
15356                         tag: 'div',
15357                         cls: 'col-xs-6 text-left',
15358                         cn: {
15359                             tag: 'button',
15360                             cls: 'btn btn-danger roo-touch-view-cancel',
15361                             html: 'Cancel'
15362                         }
15363                     },
15364                     {
15365                         tag: 'div',
15366                         cls: 'col-xs-6 text-right',
15367                         cn: {
15368                             tag: 'button',
15369                             cls: 'btn btn-success roo-touch-view-ok',
15370                             html: 'OK'
15371                         }
15372                     }
15373                 ]
15374             }
15375         ]
15376         
15377     }
15378 });
15379
15380 Roo.apply(Roo.bootstrap.ComboBox,  {
15381     
15382     touchViewTemplate : {
15383         tag: 'div',
15384         cls: 'modal fade roo-combobox-touch-view',
15385         cn: [
15386             {
15387                 tag: 'div',
15388                 cls: 'modal-dialog',
15389                 style : 'position:fixed', // we have to fix position....
15390                 cn: [
15391                     {
15392                         tag: 'div',
15393                         cls: 'modal-content',
15394                         cn: [
15395                             Roo.bootstrap.ComboBox.header,
15396                             Roo.bootstrap.ComboBox.body,
15397                             Roo.bootstrap.ComboBox.footer
15398                         ]
15399                     }
15400                 ]
15401             }
15402         ]
15403     }
15404 });/*
15405  * Based on:
15406  * Ext JS Library 1.1.1
15407  * Copyright(c) 2006-2007, Ext JS, LLC.
15408  *
15409  * Originally Released Under LGPL - original licence link has changed is not relivant.
15410  *
15411  * Fork - LGPL
15412  * <script type="text/javascript">
15413  */
15414
15415 /**
15416  * @class Roo.View
15417  * @extends Roo.util.Observable
15418  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15419  * This class also supports single and multi selection modes. <br>
15420  * Create a data model bound view:
15421  <pre><code>
15422  var store = new Roo.data.Store(...);
15423
15424  var view = new Roo.View({
15425     el : "my-element",
15426     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15427  
15428     singleSelect: true,
15429     selectedClass: "ydataview-selected",
15430     store: store
15431  });
15432
15433  // listen for node click?
15434  view.on("click", function(vw, index, node, e){
15435  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15436  });
15437
15438  // load XML data
15439  dataModel.load("foobar.xml");
15440  </code></pre>
15441  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15442  * <br><br>
15443  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15444  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15445  * 
15446  * Note: old style constructor is still suported (container, template, config)
15447  * 
15448  * @constructor
15449  * Create a new View
15450  * @param {Object} config The config object
15451  * 
15452  */
15453 Roo.View = function(config, depreciated_tpl, depreciated_config){
15454     
15455     this.parent = false;
15456     
15457     if (typeof(depreciated_tpl) == 'undefined') {
15458         // new way.. - universal constructor.
15459         Roo.apply(this, config);
15460         this.el  = Roo.get(this.el);
15461     } else {
15462         // old format..
15463         this.el  = Roo.get(config);
15464         this.tpl = depreciated_tpl;
15465         Roo.apply(this, depreciated_config);
15466     }
15467     this.wrapEl  = this.el.wrap().wrap();
15468     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15469     
15470     
15471     if(typeof(this.tpl) == "string"){
15472         this.tpl = new Roo.Template(this.tpl);
15473     } else {
15474         // support xtype ctors..
15475         this.tpl = new Roo.factory(this.tpl, Roo);
15476     }
15477     
15478     
15479     this.tpl.compile();
15480     
15481     /** @private */
15482     this.addEvents({
15483         /**
15484          * @event beforeclick
15485          * Fires before a click is processed. Returns false to cancel the default action.
15486          * @param {Roo.View} this
15487          * @param {Number} index The index of the target node
15488          * @param {HTMLElement} node The target node
15489          * @param {Roo.EventObject} e The raw event object
15490          */
15491             "beforeclick" : true,
15492         /**
15493          * @event click
15494          * Fires when a template node is clicked.
15495          * @param {Roo.View} this
15496          * @param {Number} index The index of the target node
15497          * @param {HTMLElement} node The target node
15498          * @param {Roo.EventObject} e The raw event object
15499          */
15500             "click" : true,
15501         /**
15502          * @event dblclick
15503          * Fires when a template node is double clicked.
15504          * @param {Roo.View} this
15505          * @param {Number} index The index of the target node
15506          * @param {HTMLElement} node The target node
15507          * @param {Roo.EventObject} e The raw event object
15508          */
15509             "dblclick" : true,
15510         /**
15511          * @event contextmenu
15512          * Fires when a template node is right clicked.
15513          * @param {Roo.View} this
15514          * @param {Number} index The index of the target node
15515          * @param {HTMLElement} node The target node
15516          * @param {Roo.EventObject} e The raw event object
15517          */
15518             "contextmenu" : true,
15519         /**
15520          * @event selectionchange
15521          * Fires when the selected nodes change.
15522          * @param {Roo.View} this
15523          * @param {Array} selections Array of the selected nodes
15524          */
15525             "selectionchange" : true,
15526     
15527         /**
15528          * @event beforeselect
15529          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15530          * @param {Roo.View} this
15531          * @param {HTMLElement} node The node to be selected
15532          * @param {Array} selections Array of currently selected nodes
15533          */
15534             "beforeselect" : true,
15535         /**
15536          * @event preparedata
15537          * Fires on every row to render, to allow you to change the data.
15538          * @param {Roo.View} this
15539          * @param {Object} data to be rendered (change this)
15540          */
15541           "preparedata" : true
15542           
15543           
15544         });
15545
15546
15547
15548     this.el.on({
15549         "click": this.onClick,
15550         "dblclick": this.onDblClick,
15551         "contextmenu": this.onContextMenu,
15552         scope:this
15553     });
15554
15555     this.selections = [];
15556     this.nodes = [];
15557     this.cmp = new Roo.CompositeElementLite([]);
15558     if(this.store){
15559         this.store = Roo.factory(this.store, Roo.data);
15560         this.setStore(this.store, true);
15561     }
15562     
15563     if ( this.footer && this.footer.xtype) {
15564            
15565          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15566         
15567         this.footer.dataSource = this.store;
15568         this.footer.container = fctr;
15569         this.footer = Roo.factory(this.footer, Roo);
15570         fctr.insertFirst(this.el);
15571         
15572         // this is a bit insane - as the paging toolbar seems to detach the el..
15573 //        dom.parentNode.parentNode.parentNode
15574          // they get detached?
15575     }
15576     
15577     
15578     Roo.View.superclass.constructor.call(this);
15579     
15580     
15581 };
15582
15583 Roo.extend(Roo.View, Roo.util.Observable, {
15584     
15585      /**
15586      * @cfg {Roo.data.Store} store Data store to load data from.
15587      */
15588     store : false,
15589     
15590     /**
15591      * @cfg {String|Roo.Element} el The container element.
15592      */
15593     el : '',
15594     
15595     /**
15596      * @cfg {String|Roo.Template} tpl The template used by this View 
15597      */
15598     tpl : false,
15599     /**
15600      * @cfg {String} dataName the named area of the template to use as the data area
15601      *                          Works with domtemplates roo-name="name"
15602      */
15603     dataName: false,
15604     /**
15605      * @cfg {String} selectedClass The css class to add to selected nodes
15606      */
15607     selectedClass : "x-view-selected",
15608      /**
15609      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15610      */
15611     emptyText : "",
15612     
15613     /**
15614      * @cfg {String} text to display on mask (default Loading)
15615      */
15616     mask : false,
15617     /**
15618      * @cfg {Boolean} multiSelect Allow multiple selection
15619      */
15620     multiSelect : false,
15621     /**
15622      * @cfg {Boolean} singleSelect Allow single selection
15623      */
15624     singleSelect:  false,
15625     
15626     /**
15627      * @cfg {Boolean} toggleSelect - selecting 
15628      */
15629     toggleSelect : false,
15630     
15631     /**
15632      * @cfg {Boolean} tickable - selecting 
15633      */
15634     tickable : false,
15635     
15636     /**
15637      * Returns the element this view is bound to.
15638      * @return {Roo.Element}
15639      */
15640     getEl : function(){
15641         return this.wrapEl;
15642     },
15643     
15644     
15645
15646     /**
15647      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15648      */
15649     refresh : function(){
15650         //Roo.log('refresh');
15651         var t = this.tpl;
15652         
15653         // if we are using something like 'domtemplate', then
15654         // the what gets used is:
15655         // t.applySubtemplate(NAME, data, wrapping data..)
15656         // the outer template then get' applied with
15657         //     the store 'extra data'
15658         // and the body get's added to the
15659         //      roo-name="data" node?
15660         //      <span class='roo-tpl-{name}'></span> ?????
15661         
15662         
15663         
15664         this.clearSelections();
15665         this.el.update("");
15666         var html = [];
15667         var records = this.store.getRange();
15668         if(records.length < 1) {
15669             
15670             // is this valid??  = should it render a template??
15671             
15672             this.el.update(this.emptyText);
15673             return;
15674         }
15675         var el = this.el;
15676         if (this.dataName) {
15677             this.el.update(t.apply(this.store.meta)); //????
15678             el = this.el.child('.roo-tpl-' + this.dataName);
15679         }
15680         
15681         for(var i = 0, len = records.length; i < len; i++){
15682             var data = this.prepareData(records[i].data, i, records[i]);
15683             this.fireEvent("preparedata", this, data, i, records[i]);
15684             
15685             var d = Roo.apply({}, data);
15686             
15687             if(this.tickable){
15688                 Roo.apply(d, {'roo-id' : Roo.id()});
15689                 
15690                 var _this = this;
15691             
15692                 Roo.each(this.parent.item, function(item){
15693                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15694                         return;
15695                     }
15696                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15697                 });
15698             }
15699             
15700             html[html.length] = Roo.util.Format.trim(
15701                 this.dataName ?
15702                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15703                     t.apply(d)
15704             );
15705         }
15706         
15707         
15708         
15709         el.update(html.join(""));
15710         this.nodes = el.dom.childNodes;
15711         this.updateIndexes(0);
15712     },
15713     
15714
15715     /**
15716      * Function to override to reformat the data that is sent to
15717      * the template for each node.
15718      * DEPRICATED - use the preparedata event handler.
15719      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15720      * a JSON object for an UpdateManager bound view).
15721      */
15722     prepareData : function(data, index, record)
15723     {
15724         this.fireEvent("preparedata", this, data, index, record);
15725         return data;
15726     },
15727
15728     onUpdate : function(ds, record){
15729         // Roo.log('on update');   
15730         this.clearSelections();
15731         var index = this.store.indexOf(record);
15732         var n = this.nodes[index];
15733         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15734         n.parentNode.removeChild(n);
15735         this.updateIndexes(index, index);
15736     },
15737
15738     
15739     
15740 // --------- FIXME     
15741     onAdd : function(ds, records, index)
15742     {
15743         //Roo.log(['on Add', ds, records, index] );        
15744         this.clearSelections();
15745         if(this.nodes.length == 0){
15746             this.refresh();
15747             return;
15748         }
15749         var n = this.nodes[index];
15750         for(var i = 0, len = records.length; i < len; i++){
15751             var d = this.prepareData(records[i].data, i, records[i]);
15752             if(n){
15753                 this.tpl.insertBefore(n, d);
15754             }else{
15755                 
15756                 this.tpl.append(this.el, d);
15757             }
15758         }
15759         this.updateIndexes(index);
15760     },
15761
15762     onRemove : function(ds, record, index){
15763        // Roo.log('onRemove');
15764         this.clearSelections();
15765         var el = this.dataName  ?
15766             this.el.child('.roo-tpl-' + this.dataName) :
15767             this.el; 
15768         
15769         el.dom.removeChild(this.nodes[index]);
15770         this.updateIndexes(index);
15771     },
15772
15773     /**
15774      * Refresh an individual node.
15775      * @param {Number} index
15776      */
15777     refreshNode : function(index){
15778         this.onUpdate(this.store, this.store.getAt(index));
15779     },
15780
15781     updateIndexes : function(startIndex, endIndex){
15782         var ns = this.nodes;
15783         startIndex = startIndex || 0;
15784         endIndex = endIndex || ns.length - 1;
15785         for(var i = startIndex; i <= endIndex; i++){
15786             ns[i].nodeIndex = i;
15787         }
15788     },
15789
15790     /**
15791      * Changes the data store this view uses and refresh the view.
15792      * @param {Store} store
15793      */
15794     setStore : function(store, initial){
15795         if(!initial && this.store){
15796             this.store.un("datachanged", this.refresh);
15797             this.store.un("add", this.onAdd);
15798             this.store.un("remove", this.onRemove);
15799             this.store.un("update", this.onUpdate);
15800             this.store.un("clear", this.refresh);
15801             this.store.un("beforeload", this.onBeforeLoad);
15802             this.store.un("load", this.onLoad);
15803             this.store.un("loadexception", this.onLoad);
15804         }
15805         if(store){
15806           
15807             store.on("datachanged", this.refresh, this);
15808             store.on("add", this.onAdd, this);
15809             store.on("remove", this.onRemove, this);
15810             store.on("update", this.onUpdate, this);
15811             store.on("clear", this.refresh, this);
15812             store.on("beforeload", this.onBeforeLoad, this);
15813             store.on("load", this.onLoad, this);
15814             store.on("loadexception", this.onLoad, this);
15815         }
15816         
15817         if(store){
15818             this.refresh();
15819         }
15820     },
15821     /**
15822      * onbeforeLoad - masks the loading area.
15823      *
15824      */
15825     onBeforeLoad : function(store,opts)
15826     {
15827          //Roo.log('onBeforeLoad');   
15828         if (!opts.add) {
15829             this.el.update("");
15830         }
15831         this.el.mask(this.mask ? this.mask : "Loading" ); 
15832     },
15833     onLoad : function ()
15834     {
15835         this.el.unmask();
15836     },
15837     
15838
15839     /**
15840      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15841      * @param {HTMLElement} node
15842      * @return {HTMLElement} The template node
15843      */
15844     findItemFromChild : function(node){
15845         var el = this.dataName  ?
15846             this.el.child('.roo-tpl-' + this.dataName,true) :
15847             this.el.dom; 
15848         
15849         if(!node || node.parentNode == el){
15850                     return node;
15851             }
15852             var p = node.parentNode;
15853             while(p && p != el){
15854             if(p.parentNode == el){
15855                 return p;
15856             }
15857             p = p.parentNode;
15858         }
15859             return null;
15860     },
15861
15862     /** @ignore */
15863     onClick : function(e){
15864         var item = this.findItemFromChild(e.getTarget());
15865         if(item){
15866             var index = this.indexOf(item);
15867             if(this.onItemClick(item, index, e) !== false){
15868                 this.fireEvent("click", this, index, item, e);
15869             }
15870         }else{
15871             this.clearSelections();
15872         }
15873     },
15874
15875     /** @ignore */
15876     onContextMenu : function(e){
15877         var item = this.findItemFromChild(e.getTarget());
15878         if(item){
15879             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15880         }
15881     },
15882
15883     /** @ignore */
15884     onDblClick : function(e){
15885         var item = this.findItemFromChild(e.getTarget());
15886         if(item){
15887             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15888         }
15889     },
15890
15891     onItemClick : function(item, index, e)
15892     {
15893         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15894             return false;
15895         }
15896         if (this.toggleSelect) {
15897             var m = this.isSelected(item) ? 'unselect' : 'select';
15898             //Roo.log(m);
15899             var _t = this;
15900             _t[m](item, true, false);
15901             return true;
15902         }
15903         if(this.multiSelect || this.singleSelect){
15904             if(this.multiSelect && e.shiftKey && this.lastSelection){
15905                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15906             }else{
15907                 this.select(item, this.multiSelect && e.ctrlKey);
15908                 this.lastSelection = item;
15909             }
15910             
15911             if(!this.tickable){
15912                 e.preventDefault();
15913             }
15914             
15915         }
15916         return true;
15917     },
15918
15919     /**
15920      * Get the number of selected nodes.
15921      * @return {Number}
15922      */
15923     getSelectionCount : function(){
15924         return this.selections.length;
15925     },
15926
15927     /**
15928      * Get the currently selected nodes.
15929      * @return {Array} An array of HTMLElements
15930      */
15931     getSelectedNodes : function(){
15932         return this.selections;
15933     },
15934
15935     /**
15936      * Get the indexes of the selected nodes.
15937      * @return {Array}
15938      */
15939     getSelectedIndexes : function(){
15940         var indexes = [], s = this.selections;
15941         for(var i = 0, len = s.length; i < len; i++){
15942             indexes.push(s[i].nodeIndex);
15943         }
15944         return indexes;
15945     },
15946
15947     /**
15948      * Clear all selections
15949      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15950      */
15951     clearSelections : function(suppressEvent){
15952         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15953             this.cmp.elements = this.selections;
15954             this.cmp.removeClass(this.selectedClass);
15955             this.selections = [];
15956             if(!suppressEvent){
15957                 this.fireEvent("selectionchange", this, this.selections);
15958             }
15959         }
15960     },
15961
15962     /**
15963      * Returns true if the passed node is selected
15964      * @param {HTMLElement/Number} node The node or node index
15965      * @return {Boolean}
15966      */
15967     isSelected : function(node){
15968         var s = this.selections;
15969         if(s.length < 1){
15970             return false;
15971         }
15972         node = this.getNode(node);
15973         return s.indexOf(node) !== -1;
15974     },
15975
15976     /**
15977      * Selects nodes.
15978      * @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
15979      * @param {Boolean} keepExisting (optional) true to keep existing selections
15980      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15981      */
15982     select : function(nodeInfo, keepExisting, suppressEvent){
15983         if(nodeInfo instanceof Array){
15984             if(!keepExisting){
15985                 this.clearSelections(true);
15986             }
15987             for(var i = 0, len = nodeInfo.length; i < len; i++){
15988                 this.select(nodeInfo[i], true, true);
15989             }
15990             return;
15991         } 
15992         var node = this.getNode(nodeInfo);
15993         if(!node || this.isSelected(node)){
15994             return; // already selected.
15995         }
15996         if(!keepExisting){
15997             this.clearSelections(true);
15998         }
15999         
16000         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16001             Roo.fly(node).addClass(this.selectedClass);
16002             this.selections.push(node);
16003             if(!suppressEvent){
16004                 this.fireEvent("selectionchange", this, this.selections);
16005             }
16006         }
16007         
16008         
16009     },
16010       /**
16011      * Unselects nodes.
16012      * @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
16013      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16014      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16015      */
16016     unselect : function(nodeInfo, keepExisting, suppressEvent)
16017     {
16018         if(nodeInfo instanceof Array){
16019             Roo.each(this.selections, function(s) {
16020                 this.unselect(s, nodeInfo);
16021             }, this);
16022             return;
16023         }
16024         var node = this.getNode(nodeInfo);
16025         if(!node || !this.isSelected(node)){
16026             //Roo.log("not selected");
16027             return; // not selected.
16028         }
16029         // fireevent???
16030         var ns = [];
16031         Roo.each(this.selections, function(s) {
16032             if (s == node ) {
16033                 Roo.fly(node).removeClass(this.selectedClass);
16034
16035                 return;
16036             }
16037             ns.push(s);
16038         },this);
16039         
16040         this.selections= ns;
16041         this.fireEvent("selectionchange", this, this.selections);
16042     },
16043
16044     /**
16045      * Gets a template node.
16046      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16047      * @return {HTMLElement} The node or null if it wasn't found
16048      */
16049     getNode : function(nodeInfo){
16050         if(typeof nodeInfo == "string"){
16051             return document.getElementById(nodeInfo);
16052         }else if(typeof nodeInfo == "number"){
16053             return this.nodes[nodeInfo];
16054         }
16055         return nodeInfo;
16056     },
16057
16058     /**
16059      * Gets a range template nodes.
16060      * @param {Number} startIndex
16061      * @param {Number} endIndex
16062      * @return {Array} An array of nodes
16063      */
16064     getNodes : function(start, end){
16065         var ns = this.nodes;
16066         start = start || 0;
16067         end = typeof end == "undefined" ? ns.length - 1 : end;
16068         var nodes = [];
16069         if(start <= end){
16070             for(var i = start; i <= end; i++){
16071                 nodes.push(ns[i]);
16072             }
16073         } else{
16074             for(var i = start; i >= end; i--){
16075                 nodes.push(ns[i]);
16076             }
16077         }
16078         return nodes;
16079     },
16080
16081     /**
16082      * Finds the index of the passed node
16083      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16084      * @return {Number} The index of the node or -1
16085      */
16086     indexOf : function(node){
16087         node = this.getNode(node);
16088         if(typeof node.nodeIndex == "number"){
16089             return node.nodeIndex;
16090         }
16091         var ns = this.nodes;
16092         for(var i = 0, len = ns.length; i < len; i++){
16093             if(ns[i] == node){
16094                 return i;
16095             }
16096         }
16097         return -1;
16098     }
16099 });
16100 /*
16101  * - LGPL
16102  *
16103  * based on jquery fullcalendar
16104  * 
16105  */
16106
16107 Roo.bootstrap = Roo.bootstrap || {};
16108 /**
16109  * @class Roo.bootstrap.Calendar
16110  * @extends Roo.bootstrap.Component
16111  * Bootstrap Calendar class
16112  * @cfg {Boolean} loadMask (true|false) default false
16113  * @cfg {Object} header generate the user specific header of the calendar, default false
16114
16115  * @constructor
16116  * Create a new Container
16117  * @param {Object} config The config object
16118  */
16119
16120
16121
16122 Roo.bootstrap.Calendar = function(config){
16123     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16124      this.addEvents({
16125         /**
16126              * @event select
16127              * Fires when a date is selected
16128              * @param {DatePicker} this
16129              * @param {Date} date The selected date
16130              */
16131         'select': true,
16132         /**
16133              * @event monthchange
16134              * Fires when the displayed month changes 
16135              * @param {DatePicker} this
16136              * @param {Date} date The selected month
16137              */
16138         'monthchange': true,
16139         /**
16140              * @event evententer
16141              * Fires when mouse over an event
16142              * @param {Calendar} this
16143              * @param {event} Event
16144              */
16145         'evententer': true,
16146         /**
16147              * @event eventleave
16148              * Fires when the mouse leaves an
16149              * @param {Calendar} this
16150              * @param {event}
16151              */
16152         'eventleave': true,
16153         /**
16154              * @event eventclick
16155              * Fires when the mouse click an
16156              * @param {Calendar} this
16157              * @param {event}
16158              */
16159         'eventclick': true
16160         
16161     });
16162
16163 };
16164
16165 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16166     
16167      /**
16168      * @cfg {Number} startDay
16169      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16170      */
16171     startDay : 0,
16172     
16173     loadMask : false,
16174     
16175     header : false,
16176       
16177     getAutoCreate : function(){
16178         
16179         
16180         var fc_button = function(name, corner, style, content ) {
16181             return Roo.apply({},{
16182                 tag : 'span',
16183                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16184                          (corner.length ?
16185                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16186                             ''
16187                         ),
16188                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16189                 unselectable: 'on'
16190             });
16191         };
16192         
16193         var header = {};
16194         
16195         if(!this.header){
16196             header = {
16197                 tag : 'table',
16198                 cls : 'fc-header',
16199                 style : 'width:100%',
16200                 cn : [
16201                     {
16202                         tag: 'tr',
16203                         cn : [
16204                             {
16205                                 tag : 'td',
16206                                 cls : 'fc-header-left',
16207                                 cn : [
16208                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16209                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16210                                     { tag: 'span', cls: 'fc-header-space' },
16211                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16212
16213
16214                                 ]
16215                             },
16216
16217                             {
16218                                 tag : 'td',
16219                                 cls : 'fc-header-center',
16220                                 cn : [
16221                                     {
16222                                         tag: 'span',
16223                                         cls: 'fc-header-title',
16224                                         cn : {
16225                                             tag: 'H2',
16226                                             html : 'month / year'
16227                                         }
16228                                     }
16229
16230                                 ]
16231                             },
16232                             {
16233                                 tag : 'td',
16234                                 cls : 'fc-header-right',
16235                                 cn : [
16236                               /*      fc_button('month', 'left', '', 'month' ),
16237                                     fc_button('week', '', '', 'week' ),
16238                                     fc_button('day', 'right', '', 'day' )
16239                                 */    
16240
16241                                 ]
16242                             }
16243
16244                         ]
16245                     }
16246                 ]
16247             };
16248         }
16249         
16250         header = this.header;
16251         
16252        
16253         var cal_heads = function() {
16254             var ret = [];
16255             // fixme - handle this.
16256             
16257             for (var i =0; i < Date.dayNames.length; i++) {
16258                 var d = Date.dayNames[i];
16259                 ret.push({
16260                     tag: 'th',
16261                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16262                     html : d.substring(0,3)
16263                 });
16264                 
16265             }
16266             ret[0].cls += ' fc-first';
16267             ret[6].cls += ' fc-last';
16268             return ret;
16269         };
16270         var cal_cell = function(n) {
16271             return  {
16272                 tag: 'td',
16273                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16274                 cn : [
16275                     {
16276                         cn : [
16277                             {
16278                                 cls: 'fc-day-number',
16279                                 html: 'D'
16280                             },
16281                             {
16282                                 cls: 'fc-day-content',
16283                              
16284                                 cn : [
16285                                      {
16286                                         style: 'position: relative;' // height: 17px;
16287                                     }
16288                                 ]
16289                             }
16290                             
16291                             
16292                         ]
16293                     }
16294                 ]
16295                 
16296             }
16297         };
16298         var cal_rows = function() {
16299             
16300             var ret = [];
16301             for (var r = 0; r < 6; r++) {
16302                 var row= {
16303                     tag : 'tr',
16304                     cls : 'fc-week',
16305                     cn : []
16306                 };
16307                 
16308                 for (var i =0; i < Date.dayNames.length; i++) {
16309                     var d = Date.dayNames[i];
16310                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16311
16312                 }
16313                 row.cn[0].cls+=' fc-first';
16314                 row.cn[0].cn[0].style = 'min-height:90px';
16315                 row.cn[6].cls+=' fc-last';
16316                 ret.push(row);
16317                 
16318             }
16319             ret[0].cls += ' fc-first';
16320             ret[4].cls += ' fc-prev-last';
16321             ret[5].cls += ' fc-last';
16322             return ret;
16323             
16324         };
16325         
16326         var cal_table = {
16327             tag: 'table',
16328             cls: 'fc-border-separate',
16329             style : 'width:100%',
16330             cellspacing  : 0,
16331             cn : [
16332                 { 
16333                     tag: 'thead',
16334                     cn : [
16335                         { 
16336                             tag: 'tr',
16337                             cls : 'fc-first fc-last',
16338                             cn : cal_heads()
16339                         }
16340                     ]
16341                 },
16342                 { 
16343                     tag: 'tbody',
16344                     cn : cal_rows()
16345                 }
16346                   
16347             ]
16348         };
16349          
16350          var cfg = {
16351             cls : 'fc fc-ltr',
16352             cn : [
16353                 header,
16354                 {
16355                     cls : 'fc-content',
16356                     style : "position: relative;",
16357                     cn : [
16358                         {
16359                             cls : 'fc-view fc-view-month fc-grid',
16360                             style : 'position: relative',
16361                             unselectable : 'on',
16362                             cn : [
16363                                 {
16364                                     cls : 'fc-event-container',
16365                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16366                                 },
16367                                 cal_table
16368                             ]
16369                         }
16370                     ]
16371     
16372                 }
16373            ] 
16374             
16375         };
16376         
16377          
16378         
16379         return cfg;
16380     },
16381     
16382     
16383     initEvents : function()
16384     {
16385         if(!this.store){
16386             throw "can not find store for calendar";
16387         }
16388         
16389         var mark = {
16390             tag: "div",
16391             cls:"x-dlg-mask",
16392             style: "text-align:center",
16393             cn: [
16394                 {
16395                     tag: "div",
16396                     style: "background-color:white;width:50%;margin:250 auto",
16397                     cn: [
16398                         {
16399                             tag: "img",
16400                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16401                         },
16402                         {
16403                             tag: "span",
16404                             html: "Loading"
16405                         }
16406                         
16407                     ]
16408                 }
16409             ]
16410         };
16411         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16412         
16413         var size = this.el.select('.fc-content', true).first().getSize();
16414         this.maskEl.setSize(size.width, size.height);
16415         this.maskEl.enableDisplayMode("block");
16416         if(!this.loadMask){
16417             this.maskEl.hide();
16418         }
16419         
16420         this.store = Roo.factory(this.store, Roo.data);
16421         this.store.on('load', this.onLoad, this);
16422         this.store.on('beforeload', this.onBeforeLoad, this);
16423         
16424         this.resize();
16425         
16426         this.cells = this.el.select('.fc-day',true);
16427         //Roo.log(this.cells);
16428         this.textNodes = this.el.query('.fc-day-number');
16429         this.cells.addClassOnOver('fc-state-hover');
16430         
16431         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16432         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16433         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16434         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16435         
16436         this.on('monthchange', this.onMonthChange, this);
16437         
16438         this.update(new Date().clearTime());
16439     },
16440     
16441     resize : function() {
16442         var sz  = this.el.getSize();
16443         
16444         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16445         this.el.select('.fc-day-content div',true).setHeight(34);
16446     },
16447     
16448     
16449     // private
16450     showPrevMonth : function(e){
16451         this.update(this.activeDate.add("mo", -1));
16452     },
16453     showToday : function(e){
16454         this.update(new Date().clearTime());
16455     },
16456     // private
16457     showNextMonth : function(e){
16458         this.update(this.activeDate.add("mo", 1));
16459     },
16460
16461     // private
16462     showPrevYear : function(){
16463         this.update(this.activeDate.add("y", -1));
16464     },
16465
16466     // private
16467     showNextYear : function(){
16468         this.update(this.activeDate.add("y", 1));
16469     },
16470
16471     
16472    // private
16473     update : function(date)
16474     {
16475         var vd = this.activeDate;
16476         this.activeDate = date;
16477 //        if(vd && this.el){
16478 //            var t = date.getTime();
16479 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16480 //                Roo.log('using add remove');
16481 //                
16482 //                this.fireEvent('monthchange', this, date);
16483 //                
16484 //                this.cells.removeClass("fc-state-highlight");
16485 //                this.cells.each(function(c){
16486 //                   if(c.dateValue == t){
16487 //                       c.addClass("fc-state-highlight");
16488 //                       setTimeout(function(){
16489 //                            try{c.dom.firstChild.focus();}catch(e){}
16490 //                       }, 50);
16491 //                       return false;
16492 //                   }
16493 //                   return true;
16494 //                });
16495 //                return;
16496 //            }
16497 //        }
16498         
16499         var days = date.getDaysInMonth();
16500         
16501         var firstOfMonth = date.getFirstDateOfMonth();
16502         var startingPos = firstOfMonth.getDay()-this.startDay;
16503         
16504         if(startingPos < this.startDay){
16505             startingPos += 7;
16506         }
16507         
16508         var pm = date.add(Date.MONTH, -1);
16509         var prevStart = pm.getDaysInMonth()-startingPos;
16510 //        
16511         this.cells = this.el.select('.fc-day',true);
16512         this.textNodes = this.el.query('.fc-day-number');
16513         this.cells.addClassOnOver('fc-state-hover');
16514         
16515         var cells = this.cells.elements;
16516         var textEls = this.textNodes;
16517         
16518         Roo.each(cells, function(cell){
16519             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16520         });
16521         
16522         days += startingPos;
16523
16524         // convert everything to numbers so it's fast
16525         var day = 86400000;
16526         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16527         //Roo.log(d);
16528         //Roo.log(pm);
16529         //Roo.log(prevStart);
16530         
16531         var today = new Date().clearTime().getTime();
16532         var sel = date.clearTime().getTime();
16533         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16534         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16535         var ddMatch = this.disabledDatesRE;
16536         var ddText = this.disabledDatesText;
16537         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16538         var ddaysText = this.disabledDaysText;
16539         var format = this.format;
16540         
16541         var setCellClass = function(cal, cell){
16542             cell.row = 0;
16543             cell.events = [];
16544             cell.more = [];
16545             //Roo.log('set Cell Class');
16546             cell.title = "";
16547             var t = d.getTime();
16548             
16549             //Roo.log(d);
16550             
16551             cell.dateValue = t;
16552             if(t == today){
16553                 cell.className += " fc-today";
16554                 cell.className += " fc-state-highlight";
16555                 cell.title = cal.todayText;
16556             }
16557             if(t == sel){
16558                 // disable highlight in other month..
16559                 //cell.className += " fc-state-highlight";
16560                 
16561             }
16562             // disabling
16563             if(t < min) {
16564                 cell.className = " fc-state-disabled";
16565                 cell.title = cal.minText;
16566                 return;
16567             }
16568             if(t > max) {
16569                 cell.className = " fc-state-disabled";
16570                 cell.title = cal.maxText;
16571                 return;
16572             }
16573             if(ddays){
16574                 if(ddays.indexOf(d.getDay()) != -1){
16575                     cell.title = ddaysText;
16576                     cell.className = " fc-state-disabled";
16577                 }
16578             }
16579             if(ddMatch && format){
16580                 var fvalue = d.dateFormat(format);
16581                 if(ddMatch.test(fvalue)){
16582                     cell.title = ddText.replace("%0", fvalue);
16583                     cell.className = " fc-state-disabled";
16584                 }
16585             }
16586             
16587             if (!cell.initialClassName) {
16588                 cell.initialClassName = cell.dom.className;
16589             }
16590             
16591             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16592         };
16593
16594         var i = 0;
16595         
16596         for(; i < startingPos; i++) {
16597             textEls[i].innerHTML = (++prevStart);
16598             d.setDate(d.getDate()+1);
16599             
16600             cells[i].className = "fc-past fc-other-month";
16601             setCellClass(this, cells[i]);
16602         }
16603         
16604         var intDay = 0;
16605         
16606         for(; i < days; i++){
16607             intDay = i - startingPos + 1;
16608             textEls[i].innerHTML = (intDay);
16609             d.setDate(d.getDate()+1);
16610             
16611             cells[i].className = ''; // "x-date-active";
16612             setCellClass(this, cells[i]);
16613         }
16614         var extraDays = 0;
16615         
16616         for(; i < 42; i++) {
16617             textEls[i].innerHTML = (++extraDays);
16618             d.setDate(d.getDate()+1);
16619             
16620             cells[i].className = "fc-future fc-other-month";
16621             setCellClass(this, cells[i]);
16622         }
16623         
16624         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16625         
16626         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16627         
16628         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16629         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16630         
16631         if(totalRows != 6){
16632             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16633             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16634         }
16635         
16636         this.fireEvent('monthchange', this, date);
16637         
16638         
16639         /*
16640         if(!this.internalRender){
16641             var main = this.el.dom.firstChild;
16642             var w = main.offsetWidth;
16643             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16644             Roo.fly(main).setWidth(w);
16645             this.internalRender = true;
16646             // opera does not respect the auto grow header center column
16647             // then, after it gets a width opera refuses to recalculate
16648             // without a second pass
16649             if(Roo.isOpera && !this.secondPass){
16650                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16651                 this.secondPass = true;
16652                 this.update.defer(10, this, [date]);
16653             }
16654         }
16655         */
16656         
16657     },
16658     
16659     findCell : function(dt) {
16660         dt = dt.clearTime().getTime();
16661         var ret = false;
16662         this.cells.each(function(c){
16663             //Roo.log("check " +c.dateValue + '?=' + dt);
16664             if(c.dateValue == dt){
16665                 ret = c;
16666                 return false;
16667             }
16668             return true;
16669         });
16670         
16671         return ret;
16672     },
16673     
16674     findCells : function(ev) {
16675         var s = ev.start.clone().clearTime().getTime();
16676        // Roo.log(s);
16677         var e= ev.end.clone().clearTime().getTime();
16678        // Roo.log(e);
16679         var ret = [];
16680         this.cells.each(function(c){
16681              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16682             
16683             if(c.dateValue > e){
16684                 return ;
16685             }
16686             if(c.dateValue < s){
16687                 return ;
16688             }
16689             ret.push(c);
16690         });
16691         
16692         return ret;    
16693     },
16694     
16695 //    findBestRow: function(cells)
16696 //    {
16697 //        var ret = 0;
16698 //        
16699 //        for (var i =0 ; i < cells.length;i++) {
16700 //            ret  = Math.max(cells[i].rows || 0,ret);
16701 //        }
16702 //        return ret;
16703 //        
16704 //    },
16705     
16706     
16707     addItem : function(ev)
16708     {
16709         // look for vertical location slot in
16710         var cells = this.findCells(ev);
16711         
16712 //        ev.row = this.findBestRow(cells);
16713         
16714         // work out the location.
16715         
16716         var crow = false;
16717         var rows = [];
16718         for(var i =0; i < cells.length; i++) {
16719             
16720             cells[i].row = cells[0].row;
16721             
16722             if(i == 0){
16723                 cells[i].row = cells[i].row + 1;
16724             }
16725             
16726             if (!crow) {
16727                 crow = {
16728                     start : cells[i],
16729                     end :  cells[i]
16730                 };
16731                 continue;
16732             }
16733             if (crow.start.getY() == cells[i].getY()) {
16734                 // on same row.
16735                 crow.end = cells[i];
16736                 continue;
16737             }
16738             // different row.
16739             rows.push(crow);
16740             crow = {
16741                 start: cells[i],
16742                 end : cells[i]
16743             };
16744             
16745         }
16746         
16747         rows.push(crow);
16748         ev.els = [];
16749         ev.rows = rows;
16750         ev.cells = cells;
16751         
16752         cells[0].events.push(ev);
16753         
16754         this.calevents.push(ev);
16755     },
16756     
16757     clearEvents: function() {
16758         
16759         if(!this.calevents){
16760             return;
16761         }
16762         
16763         Roo.each(this.cells.elements, function(c){
16764             c.row = 0;
16765             c.events = [];
16766             c.more = [];
16767         });
16768         
16769         Roo.each(this.calevents, function(e) {
16770             Roo.each(e.els, function(el) {
16771                 el.un('mouseenter' ,this.onEventEnter, this);
16772                 el.un('mouseleave' ,this.onEventLeave, this);
16773                 el.remove();
16774             },this);
16775         },this);
16776         
16777         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16778             e.remove();
16779         });
16780         
16781     },
16782     
16783     renderEvents: function()
16784     {   
16785         var _this = this;
16786         
16787         this.cells.each(function(c) {
16788             
16789             if(c.row < 5){
16790                 return;
16791             }
16792             
16793             var ev = c.events;
16794             
16795             var r = 4;
16796             if(c.row != c.events.length){
16797                 r = 4 - (4 - (c.row - c.events.length));
16798             }
16799             
16800             c.events = ev.slice(0, r);
16801             c.more = ev.slice(r);
16802             
16803             if(c.more.length && c.more.length == 1){
16804                 c.events.push(c.more.pop());
16805             }
16806             
16807             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16808             
16809         });
16810             
16811         this.cells.each(function(c) {
16812             
16813             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16814             
16815             
16816             for (var e = 0; e < c.events.length; e++){
16817                 var ev = c.events[e];
16818                 var rows = ev.rows;
16819                 
16820                 for(var i = 0; i < rows.length; i++) {
16821                 
16822                     // how many rows should it span..
16823
16824                     var  cfg = {
16825                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16826                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16827
16828                         unselectable : "on",
16829                         cn : [
16830                             {
16831                                 cls: 'fc-event-inner',
16832                                 cn : [
16833     //                                {
16834     //                                  tag:'span',
16835     //                                  cls: 'fc-event-time',
16836     //                                  html : cells.length > 1 ? '' : ev.time
16837     //                                },
16838                                     {
16839                                       tag:'span',
16840                                       cls: 'fc-event-title',
16841                                       html : String.format('{0}', ev.title)
16842                                     }
16843
16844
16845                                 ]
16846                             },
16847                             {
16848                                 cls: 'ui-resizable-handle ui-resizable-e',
16849                                 html : '&nbsp;&nbsp;&nbsp'
16850                             }
16851
16852                         ]
16853                     };
16854
16855                     if (i == 0) {
16856                         cfg.cls += ' fc-event-start';
16857                     }
16858                     if ((i+1) == rows.length) {
16859                         cfg.cls += ' fc-event-end';
16860                     }
16861
16862                     var ctr = _this.el.select('.fc-event-container',true).first();
16863                     var cg = ctr.createChild(cfg);
16864
16865                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16866                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16867
16868                     var r = (c.more.length) ? 1 : 0;
16869                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16870                     cg.setWidth(ebox.right - sbox.x -2);
16871
16872                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16873                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16874                     cg.on('click', _this.onEventClick, _this, ev);
16875
16876                     ev.els.push(cg);
16877                     
16878                 }
16879                 
16880             }
16881             
16882             
16883             if(c.more.length){
16884                 var  cfg = {
16885                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16886                     style : 'position: absolute',
16887                     unselectable : "on",
16888                     cn : [
16889                         {
16890                             cls: 'fc-event-inner',
16891                             cn : [
16892                                 {
16893                                   tag:'span',
16894                                   cls: 'fc-event-title',
16895                                   html : 'More'
16896                                 }
16897
16898
16899                             ]
16900                         },
16901                         {
16902                             cls: 'ui-resizable-handle ui-resizable-e',
16903                             html : '&nbsp;&nbsp;&nbsp'
16904                         }
16905
16906                     ]
16907                 };
16908
16909                 var ctr = _this.el.select('.fc-event-container',true).first();
16910                 var cg = ctr.createChild(cfg);
16911
16912                 var sbox = c.select('.fc-day-content',true).first().getBox();
16913                 var ebox = c.select('.fc-day-content',true).first().getBox();
16914                 //Roo.log(cg);
16915                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16916                 cg.setWidth(ebox.right - sbox.x -2);
16917
16918                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16919                 
16920             }
16921             
16922         });
16923         
16924         
16925         
16926     },
16927     
16928     onEventEnter: function (e, el,event,d) {
16929         this.fireEvent('evententer', this, el, event);
16930     },
16931     
16932     onEventLeave: function (e, el,event,d) {
16933         this.fireEvent('eventleave', this, el, event);
16934     },
16935     
16936     onEventClick: function (e, el,event,d) {
16937         this.fireEvent('eventclick', this, el, event);
16938     },
16939     
16940     onMonthChange: function () {
16941         this.store.load();
16942     },
16943     
16944     onMoreEventClick: function(e, el, more)
16945     {
16946         var _this = this;
16947         
16948         this.calpopover.placement = 'right';
16949         this.calpopover.setTitle('More');
16950         
16951         this.calpopover.setContent('');
16952         
16953         var ctr = this.calpopover.el.select('.popover-content', true).first();
16954         
16955         Roo.each(more, function(m){
16956             var cfg = {
16957                 cls : 'fc-event-hori fc-event-draggable',
16958                 html : m.title
16959             };
16960             var cg = ctr.createChild(cfg);
16961             
16962             cg.on('click', _this.onEventClick, _this, m);
16963         });
16964         
16965         this.calpopover.show(el);
16966         
16967         
16968     },
16969     
16970     onLoad: function () 
16971     {   
16972         this.calevents = [];
16973         var cal = this;
16974         
16975         if(this.store.getCount() > 0){
16976             this.store.data.each(function(d){
16977                cal.addItem({
16978                     id : d.data.id,
16979                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16980                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16981                     time : d.data.start_time,
16982                     title : d.data.title,
16983                     description : d.data.description,
16984                     venue : d.data.venue
16985                 });
16986             });
16987         }
16988         
16989         this.renderEvents();
16990         
16991         if(this.calevents.length && this.loadMask){
16992             this.maskEl.hide();
16993         }
16994     },
16995     
16996     onBeforeLoad: function()
16997     {
16998         this.clearEvents();
16999         if(this.loadMask){
17000             this.maskEl.show();
17001         }
17002     }
17003 });
17004
17005  
17006  /*
17007  * - LGPL
17008  *
17009  * element
17010  * 
17011  */
17012
17013 /**
17014  * @class Roo.bootstrap.Popover
17015  * @extends Roo.bootstrap.Component
17016  * Bootstrap Popover class
17017  * @cfg {String} html contents of the popover   (or false to use children..)
17018  * @cfg {String} title of popover (or false to hide)
17019  * @cfg {String} placement how it is placed
17020  * @cfg {String} trigger click || hover (or false to trigger manually)
17021  * @cfg {String} over what (parent or false to trigger manually.)
17022  * @cfg {Number} delay - delay before showing
17023  
17024  * @constructor
17025  * Create a new Popover
17026  * @param {Object} config The config object
17027  */
17028
17029 Roo.bootstrap.Popover = function(config){
17030     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17031     
17032     this.addEvents({
17033         // raw events
17034          /**
17035          * @event show
17036          * After the popover show
17037          * 
17038          * @param {Roo.bootstrap.Popover} this
17039          */
17040         "show" : true,
17041         /**
17042          * @event hide
17043          * After the popover hide
17044          * 
17045          * @param {Roo.bootstrap.Popover} this
17046          */
17047         "hide" : true
17048     });
17049 };
17050
17051 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17052     
17053     title: 'Fill in a title',
17054     html: false,
17055     
17056     placement : 'right',
17057     trigger : 'hover', // hover
17058     
17059     delay : 0,
17060     
17061     over: 'parent',
17062     
17063     can_build_overlaid : false,
17064     
17065     getChildContainer : function()
17066     {
17067         return this.el.select('.popover-content',true).first();
17068     },
17069     
17070     getAutoCreate : function(){
17071          
17072         var cfg = {
17073            cls : 'popover roo-dynamic',
17074            style: 'display:block',
17075            cn : [
17076                 {
17077                     cls : 'arrow'
17078                 },
17079                 {
17080                     cls : 'popover-inner',
17081                     cn : [
17082                         {
17083                             tag: 'h3',
17084                             cls: 'popover-title',
17085                             html : this.title
17086                         },
17087                         {
17088                             cls : 'popover-content',
17089                             html : this.html
17090                         }
17091                     ]
17092                     
17093                 }
17094            ]
17095         };
17096         
17097         return cfg;
17098     },
17099     setTitle: function(str)
17100     {
17101         this.title = str;
17102         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17103     },
17104     setContent: function(str)
17105     {
17106         this.html = str;
17107         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17108     },
17109     // as it get's added to the bottom of the page.
17110     onRender : function(ct, position)
17111     {
17112         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17113         if(!this.el){
17114             var cfg = Roo.apply({},  this.getAutoCreate());
17115             cfg.id = Roo.id();
17116             
17117             if (this.cls) {
17118                 cfg.cls += ' ' + this.cls;
17119             }
17120             if (this.style) {
17121                 cfg.style = this.style;
17122             }
17123             //Roo.log("adding to ");
17124             this.el = Roo.get(document.body).createChild(cfg, position);
17125 //            Roo.log(this.el);
17126         }
17127         this.initEvents();
17128     },
17129     
17130     initEvents : function()
17131     {
17132         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17133         this.el.enableDisplayMode('block');
17134         this.el.hide();
17135         if (this.over === false) {
17136             return; 
17137         }
17138         if (this.triggers === false) {
17139             return;
17140         }
17141         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17142         var triggers = this.trigger ? this.trigger.split(' ') : [];
17143         Roo.each(triggers, function(trigger) {
17144         
17145             if (trigger == 'click') {
17146                 on_el.on('click', this.toggle, this);
17147             } else if (trigger != 'manual') {
17148                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17149                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17150       
17151                 on_el.on(eventIn  ,this.enter, this);
17152                 on_el.on(eventOut, this.leave, this);
17153             }
17154         }, this);
17155         
17156     },
17157     
17158     
17159     // private
17160     timeout : null,
17161     hoverState : null,
17162     
17163     toggle : function () {
17164         this.hoverState == 'in' ? this.leave() : this.enter();
17165     },
17166     
17167     enter : function () {
17168         
17169         clearTimeout(this.timeout);
17170     
17171         this.hoverState = 'in';
17172     
17173         if (!this.delay || !this.delay.show) {
17174             this.show();
17175             return;
17176         }
17177         var _t = this;
17178         this.timeout = setTimeout(function () {
17179             if (_t.hoverState == 'in') {
17180                 _t.show();
17181             }
17182         }, this.delay.show)
17183     },
17184     
17185     leave : function() {
17186         clearTimeout(this.timeout);
17187     
17188         this.hoverState = 'out';
17189     
17190         if (!this.delay || !this.delay.hide) {
17191             this.hide();
17192             return;
17193         }
17194         var _t = this;
17195         this.timeout = setTimeout(function () {
17196             if (_t.hoverState == 'out') {
17197                 _t.hide();
17198             }
17199         }, this.delay.hide)
17200     },
17201     
17202     show : function (on_el)
17203     {
17204         if (!on_el) {
17205             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17206         }
17207         
17208         // set content.
17209         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17210         if (this.html !== false) {
17211             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17212         }
17213         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17214         if (!this.title.length) {
17215             this.el.select('.popover-title',true).hide();
17216         }
17217         
17218         var placement = typeof this.placement == 'function' ?
17219             this.placement.call(this, this.el, on_el) :
17220             this.placement;
17221             
17222         var autoToken = /\s?auto?\s?/i;
17223         var autoPlace = autoToken.test(placement);
17224         if (autoPlace) {
17225             placement = placement.replace(autoToken, '') || 'top';
17226         }
17227         
17228         //this.el.detach()
17229         //this.el.setXY([0,0]);
17230         this.el.show();
17231         this.el.dom.style.display='block';
17232         this.el.addClass(placement);
17233         
17234         //this.el.appendTo(on_el);
17235         
17236         var p = this.getPosition();
17237         var box = this.el.getBox();
17238         
17239         if (autoPlace) {
17240             // fixme..
17241         }
17242         var align = Roo.bootstrap.Popover.alignment[placement];
17243         this.el.alignTo(on_el, align[0],align[1]);
17244         //var arrow = this.el.select('.arrow',true).first();
17245         //arrow.set(align[2], 
17246         
17247         this.el.addClass('in');
17248         
17249         
17250         if (this.el.hasClass('fade')) {
17251             // fade it?
17252         }
17253         
17254         this.hoverState = 'in';
17255         
17256         this.fireEvent('show', this);
17257         
17258     },
17259     hide : function()
17260     {
17261         this.el.setXY([0,0]);
17262         this.el.removeClass('in');
17263         this.el.hide();
17264         this.hoverState = null;
17265         
17266         this.fireEvent('hide', this);
17267     }
17268     
17269 });
17270
17271 Roo.bootstrap.Popover.alignment = {
17272     'left' : ['r-l', [-10,0], 'right'],
17273     'right' : ['l-r', [10,0], 'left'],
17274     'bottom' : ['t-b', [0,10], 'top'],
17275     'top' : [ 'b-t', [0,-10], 'bottom']
17276 };
17277
17278  /*
17279  * - LGPL
17280  *
17281  * Progress
17282  * 
17283  */
17284
17285 /**
17286  * @class Roo.bootstrap.Progress
17287  * @extends Roo.bootstrap.Component
17288  * Bootstrap Progress class
17289  * @cfg {Boolean} striped striped of the progress bar
17290  * @cfg {Boolean} active animated of the progress bar
17291  * 
17292  * 
17293  * @constructor
17294  * Create a new Progress
17295  * @param {Object} config The config object
17296  */
17297
17298 Roo.bootstrap.Progress = function(config){
17299     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17300 };
17301
17302 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17303     
17304     striped : false,
17305     active: false,
17306     
17307     getAutoCreate : function(){
17308         var cfg = {
17309             tag: 'div',
17310             cls: 'progress'
17311         };
17312         
17313         
17314         if(this.striped){
17315             cfg.cls += ' progress-striped';
17316         }
17317       
17318         if(this.active){
17319             cfg.cls += ' active';
17320         }
17321         
17322         
17323         return cfg;
17324     }
17325    
17326 });
17327
17328  
17329
17330  /*
17331  * - LGPL
17332  *
17333  * ProgressBar
17334  * 
17335  */
17336
17337 /**
17338  * @class Roo.bootstrap.ProgressBar
17339  * @extends Roo.bootstrap.Component
17340  * Bootstrap ProgressBar class
17341  * @cfg {Number} aria_valuenow aria-value now
17342  * @cfg {Number} aria_valuemin aria-value min
17343  * @cfg {Number} aria_valuemax aria-value max
17344  * @cfg {String} label label for the progress bar
17345  * @cfg {String} panel (success | info | warning | danger )
17346  * @cfg {String} role role of the progress bar
17347  * @cfg {String} sr_only text
17348  * 
17349  * 
17350  * @constructor
17351  * Create a new ProgressBar
17352  * @param {Object} config The config object
17353  */
17354
17355 Roo.bootstrap.ProgressBar = function(config){
17356     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17357 };
17358
17359 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17360     
17361     aria_valuenow : 0,
17362     aria_valuemin : 0,
17363     aria_valuemax : 100,
17364     label : false,
17365     panel : false,
17366     role : false,
17367     sr_only: false,
17368     
17369     getAutoCreate : function()
17370     {
17371         
17372         var cfg = {
17373             tag: 'div',
17374             cls: 'progress-bar',
17375             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17376         };
17377         
17378         if(this.sr_only){
17379             cfg.cn = {
17380                 tag: 'span',
17381                 cls: 'sr-only',
17382                 html: this.sr_only
17383             }
17384         }
17385         
17386         if(this.role){
17387             cfg.role = this.role;
17388         }
17389         
17390         if(this.aria_valuenow){
17391             cfg['aria-valuenow'] = this.aria_valuenow;
17392         }
17393         
17394         if(this.aria_valuemin){
17395             cfg['aria-valuemin'] = this.aria_valuemin;
17396         }
17397         
17398         if(this.aria_valuemax){
17399             cfg['aria-valuemax'] = this.aria_valuemax;
17400         }
17401         
17402         if(this.label && !this.sr_only){
17403             cfg.html = this.label;
17404         }
17405         
17406         if(this.panel){
17407             cfg.cls += ' progress-bar-' + this.panel;
17408         }
17409         
17410         return cfg;
17411     },
17412     
17413     update : function(aria_valuenow)
17414     {
17415         this.aria_valuenow = aria_valuenow;
17416         
17417         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17418     }
17419    
17420 });
17421
17422  
17423
17424  /*
17425  * - LGPL
17426  *
17427  * column
17428  * 
17429  */
17430
17431 /**
17432  * @class Roo.bootstrap.TabGroup
17433  * @extends Roo.bootstrap.Column
17434  * Bootstrap Column class
17435  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17436  * @cfg {Boolean} carousel true to make the group behave like a carousel
17437  * @cfg {Boolean} bullets show bullets for the panels
17438  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17439  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17440  * @cfg {Boolean} showarrow (true|false) show arrow default true
17441  * 
17442  * @constructor
17443  * Create a new TabGroup
17444  * @param {Object} config The config object
17445  */
17446
17447 Roo.bootstrap.TabGroup = function(config){
17448     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17449     if (!this.navId) {
17450         this.navId = Roo.id();
17451     }
17452     this.tabs = [];
17453     Roo.bootstrap.TabGroup.register(this);
17454     
17455 };
17456
17457 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17458     
17459     carousel : false,
17460     transition : false,
17461     bullets : 0,
17462     timer : 0,
17463     autoslide : false,
17464     slideFn : false,
17465     slideOnTouch : false,
17466     showarrow : true,
17467     
17468     getAutoCreate : function()
17469     {
17470         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17471         
17472         cfg.cls += ' tab-content';
17473         
17474         if (this.carousel) {
17475             cfg.cls += ' carousel slide';
17476             
17477             cfg.cn = [{
17478                cls : 'carousel-inner',
17479                cn : []
17480             }];
17481         
17482             if(this.bullets  && !Roo.isTouch){
17483                 
17484                 var bullets = {
17485                     cls : 'carousel-bullets',
17486                     cn : []
17487                 };
17488                
17489                 if(this.bullets_cls){
17490                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17491                 }
17492                 
17493                 bullets.cn.push({
17494                     cls : 'clear'
17495                 });
17496                 
17497                 cfg.cn[0].cn.push(bullets);
17498             }
17499             
17500             if(this.showarrow){
17501                 cfg.cn[0].cn.push({
17502                     tag : 'div',
17503                     class : 'carousel-arrow',
17504                     cn : [
17505                         {
17506                             tag : 'div',
17507                             class : 'carousel-prev',
17508                             cn : [
17509                                 {
17510                                     tag : 'i',
17511                                     class : 'fa fa-chevron-left'
17512                                 }
17513                             ]
17514                         },
17515                         {
17516                             tag : 'div',
17517                             class : 'carousel-next',
17518                             cn : [
17519                                 {
17520                                     tag : 'i',
17521                                     class : 'fa fa-chevron-right'
17522                                 }
17523                             ]
17524                         }
17525                     ]
17526                 });
17527             }
17528             
17529         }
17530         
17531         return cfg;
17532     },
17533     
17534     initEvents:  function()
17535     {
17536 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17537 //            this.el.on("touchstart", this.onTouchStart, this);
17538 //        }
17539         
17540         if(this.autoslide){
17541             var _this = this;
17542             
17543             this.slideFn = window.setInterval(function() {
17544                 _this.showPanelNext();
17545             }, this.timer);
17546         }
17547         
17548         if(this.showarrow){
17549             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17550             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17551         }
17552         
17553         
17554     },
17555     
17556 //    onTouchStart : function(e, el, o)
17557 //    {
17558 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17559 //            return;
17560 //        }
17561 //        
17562 //        this.showPanelNext();
17563 //    },
17564     
17565     
17566     getChildContainer : function()
17567     {
17568         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17569     },
17570     
17571     /**
17572     * register a Navigation item
17573     * @param {Roo.bootstrap.NavItem} the navitem to add
17574     */
17575     register : function(item)
17576     {
17577         this.tabs.push( item);
17578         item.navId = this.navId; // not really needed..
17579         this.addBullet();
17580     
17581     },
17582     
17583     getActivePanel : function()
17584     {
17585         var r = false;
17586         Roo.each(this.tabs, function(t) {
17587             if (t.active) {
17588                 r = t;
17589                 return false;
17590             }
17591             return null;
17592         });
17593         return r;
17594         
17595     },
17596     getPanelByName : function(n)
17597     {
17598         var r = false;
17599         Roo.each(this.tabs, function(t) {
17600             if (t.tabId == n) {
17601                 r = t;
17602                 return false;
17603             }
17604             return null;
17605         });
17606         return r;
17607     },
17608     indexOfPanel : function(p)
17609     {
17610         var r = false;
17611         Roo.each(this.tabs, function(t,i) {
17612             if (t.tabId == p.tabId) {
17613                 r = i;
17614                 return false;
17615             }
17616             return null;
17617         });
17618         return r;
17619     },
17620     /**
17621      * show a specific panel
17622      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17623      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17624      */
17625     showPanel : function (pan)
17626     {
17627         if(this.transition || typeof(pan) == 'undefined'){
17628             Roo.log("waiting for the transitionend");
17629             return;
17630         }
17631         
17632         if (typeof(pan) == 'number') {
17633             pan = this.tabs[pan];
17634         }
17635         
17636         if (typeof(pan) == 'string') {
17637             pan = this.getPanelByName(pan);
17638         }
17639         
17640         var cur = this.getActivePanel();
17641         
17642         if(!pan || !cur){
17643             Roo.log('pan or acitve pan is undefined');
17644             return false;
17645         }
17646         
17647         if (pan.tabId == this.getActivePanel().tabId) {
17648             return true;
17649         }
17650         
17651         if (false === cur.fireEvent('beforedeactivate')) {
17652             return false;
17653         }
17654         
17655         if(this.bullets > 0 && !Roo.isTouch){
17656             this.setActiveBullet(this.indexOfPanel(pan));
17657         }
17658         
17659         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17660             
17661             this.transition = true;
17662             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17663             var lr = dir == 'next' ? 'left' : 'right';
17664             pan.el.addClass(dir); // or prev
17665             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17666             cur.el.addClass(lr); // or right
17667             pan.el.addClass(lr);
17668             
17669             var _this = this;
17670             cur.el.on('transitionend', function() {
17671                 Roo.log("trans end?");
17672                 
17673                 pan.el.removeClass([lr,dir]);
17674                 pan.setActive(true);
17675                 
17676                 cur.el.removeClass([lr]);
17677                 cur.setActive(false);
17678                 
17679                 _this.transition = false;
17680                 
17681             }, this, { single:  true } );
17682             
17683             return true;
17684         }
17685         
17686         cur.setActive(false);
17687         pan.setActive(true);
17688         
17689         return true;
17690         
17691     },
17692     showPanelNext : function()
17693     {
17694         var i = this.indexOfPanel(this.getActivePanel());
17695         
17696         if (i >= this.tabs.length - 1 && !this.autoslide) {
17697             return;
17698         }
17699         
17700         if (i >= this.tabs.length - 1 && this.autoslide) {
17701             i = -1;
17702         }
17703         
17704         this.showPanel(this.tabs[i+1]);
17705     },
17706     
17707     showPanelPrev : function()
17708     {
17709         var i = this.indexOfPanel(this.getActivePanel());
17710         
17711         if (i  < 1 && !this.autoslide) {
17712             return;
17713         }
17714         
17715         if (i < 1 && this.autoslide) {
17716             i = this.tabs.length;
17717         }
17718         
17719         this.showPanel(this.tabs[i-1]);
17720     },
17721     
17722     
17723     addBullet: function()
17724     {
17725         if(!this.bullets || Roo.isTouch){
17726             return;
17727         }
17728         var ctr = this.el.select('.carousel-bullets',true).first();
17729         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17730         var bullet = ctr.createChild({
17731             cls : 'bullet bullet-' + i
17732         },ctr.dom.lastChild);
17733         
17734         
17735         var _this = this;
17736         
17737         bullet.on('click', (function(e, el, o, ii, t){
17738
17739             e.preventDefault();
17740
17741             this.showPanel(ii);
17742
17743             if(this.autoslide && this.slideFn){
17744                 clearInterval(this.slideFn);
17745                 this.slideFn = window.setInterval(function() {
17746                     _this.showPanelNext();
17747                 }, this.timer);
17748             }
17749
17750         }).createDelegate(this, [i, bullet], true));
17751                 
17752         
17753     },
17754      
17755     setActiveBullet : function(i)
17756     {
17757         if(Roo.isTouch){
17758             return;
17759         }
17760         
17761         Roo.each(this.el.select('.bullet', true).elements, function(el){
17762             el.removeClass('selected');
17763         });
17764
17765         var bullet = this.el.select('.bullet-' + i, true).first();
17766         
17767         if(!bullet){
17768             return;
17769         }
17770         
17771         bullet.addClass('selected');
17772     }
17773     
17774     
17775   
17776 });
17777
17778  
17779
17780  
17781  
17782 Roo.apply(Roo.bootstrap.TabGroup, {
17783     
17784     groups: {},
17785      /**
17786     * register a Navigation Group
17787     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17788     */
17789     register : function(navgrp)
17790     {
17791         this.groups[navgrp.navId] = navgrp;
17792         
17793     },
17794     /**
17795     * fetch a Navigation Group based on the navigation ID
17796     * if one does not exist , it will get created.
17797     * @param {string} the navgroup to add
17798     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17799     */
17800     get: function(navId) {
17801         if (typeof(this.groups[navId]) == 'undefined') {
17802             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17803         }
17804         return this.groups[navId] ;
17805     }
17806     
17807     
17808     
17809 });
17810
17811  /*
17812  * - LGPL
17813  *
17814  * TabPanel
17815  * 
17816  */
17817
17818 /**
17819  * @class Roo.bootstrap.TabPanel
17820  * @extends Roo.bootstrap.Component
17821  * Bootstrap TabPanel class
17822  * @cfg {Boolean} active panel active
17823  * @cfg {String} html panel content
17824  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17825  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17826  * @cfg {String} href click to link..
17827  * 
17828  * 
17829  * @constructor
17830  * Create a new TabPanel
17831  * @param {Object} config The config object
17832  */
17833
17834 Roo.bootstrap.TabPanel = function(config){
17835     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17836     this.addEvents({
17837         /**
17838              * @event changed
17839              * Fires when the active status changes
17840              * @param {Roo.bootstrap.TabPanel} this
17841              * @param {Boolean} state the new state
17842             
17843          */
17844         'changed': true,
17845         /**
17846              * @event beforedeactivate
17847              * Fires before a tab is de-activated - can be used to do validation on a form.
17848              * @param {Roo.bootstrap.TabPanel} this
17849              * @return {Boolean} false if there is an error
17850             
17851          */
17852         'beforedeactivate': true
17853      });
17854     
17855     this.tabId = this.tabId || Roo.id();
17856   
17857 };
17858
17859 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17860     
17861     active: false,
17862     html: false,
17863     tabId: false,
17864     navId : false,
17865     href : '',
17866     
17867     getAutoCreate : function(){
17868         var cfg = {
17869             tag: 'div',
17870             // item is needed for carousel - not sure if it has any effect otherwise
17871             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17872             html: this.html || ''
17873         };
17874         
17875         if(this.active){
17876             cfg.cls += ' active';
17877         }
17878         
17879         if(this.tabId){
17880             cfg.tabId = this.tabId;
17881         }
17882         
17883         
17884         return cfg;
17885     },
17886     
17887     initEvents:  function()
17888     {
17889         var p = this.parent();
17890         
17891         this.navId = this.navId || p.navId;
17892         
17893         if (typeof(this.navId) != 'undefined') {
17894             // not really needed.. but just in case.. parent should be a NavGroup.
17895             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17896             
17897             tg.register(this);
17898             
17899             var i = tg.tabs.length - 1;
17900             
17901             if(this.active && tg.bullets > 0 && i < tg.bullets){
17902                 tg.setActiveBullet(i);
17903             }
17904         }
17905         
17906         this.el.on('click', this.onClick, this);
17907         
17908         if(Roo.isTouch){
17909             this.el.on("touchstart", this.onTouchStart, this);
17910             this.el.on("touchmove", this.onTouchMove, this);
17911             this.el.on("touchend", this.onTouchEnd, this);
17912         }
17913         
17914     },
17915     
17916     onRender : function(ct, position)
17917     {
17918         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17919     },
17920     
17921     setActive : function(state)
17922     {
17923         Roo.log("panel - set active " + this.tabId + "=" + state);
17924         
17925         this.active = state;
17926         if (!state) {
17927             this.el.removeClass('active');
17928             
17929         } else  if (!this.el.hasClass('active')) {
17930             this.el.addClass('active');
17931         }
17932         
17933         this.fireEvent('changed', this, state);
17934     },
17935     
17936     onClick : function(e)
17937     {
17938         e.preventDefault();
17939         
17940         if(!this.href.length){
17941             return;
17942         }
17943         
17944         window.location.href = this.href;
17945     },
17946     
17947     startX : 0,
17948     startY : 0,
17949     endX : 0,
17950     endY : 0,
17951     swiping : false,
17952     
17953     onTouchStart : function(e)
17954     {
17955         this.swiping = false;
17956         
17957         this.startX = e.browserEvent.touches[0].clientX;
17958         this.startY = e.browserEvent.touches[0].clientY;
17959     },
17960     
17961     onTouchMove : function(e)
17962     {
17963         this.swiping = true;
17964         
17965         this.endX = e.browserEvent.touches[0].clientX;
17966         this.endY = e.browserEvent.touches[0].clientY;
17967     },
17968     
17969     onTouchEnd : function(e)
17970     {
17971         if(!this.swiping){
17972             this.onClick(e);
17973             return;
17974         }
17975         
17976         var tabGroup = this.parent();
17977         
17978         if(this.endX > this.startX){ // swiping right
17979             tabGroup.showPanelPrev();
17980             return;
17981         }
17982         
17983         if(this.startX > this.endX){ // swiping left
17984             tabGroup.showPanelNext();
17985             return;
17986         }
17987     }
17988     
17989     
17990 });
17991  
17992
17993  
17994
17995  /*
17996  * - LGPL
17997  *
17998  * DateField
17999  * 
18000  */
18001
18002 /**
18003  * @class Roo.bootstrap.DateField
18004  * @extends Roo.bootstrap.Input
18005  * Bootstrap DateField class
18006  * @cfg {Number} weekStart default 0
18007  * @cfg {String} viewMode default empty, (months|years)
18008  * @cfg {String} minViewMode default empty, (months|years)
18009  * @cfg {Number} startDate default -Infinity
18010  * @cfg {Number} endDate default Infinity
18011  * @cfg {Boolean} todayHighlight default false
18012  * @cfg {Boolean} todayBtn default false
18013  * @cfg {Boolean} calendarWeeks default false
18014  * @cfg {Object} daysOfWeekDisabled default empty
18015  * @cfg {Boolean} singleMode default false (true | false)
18016  * 
18017  * @cfg {Boolean} keyboardNavigation default true
18018  * @cfg {String} language default en
18019  * 
18020  * @constructor
18021  * Create a new DateField
18022  * @param {Object} config The config object
18023  */
18024
18025 Roo.bootstrap.DateField = function(config){
18026     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18027      this.addEvents({
18028             /**
18029              * @event show
18030              * Fires when this field show.
18031              * @param {Roo.bootstrap.DateField} this
18032              * @param {Mixed} date The date value
18033              */
18034             show : true,
18035             /**
18036              * @event show
18037              * Fires when this field hide.
18038              * @param {Roo.bootstrap.DateField} this
18039              * @param {Mixed} date The date value
18040              */
18041             hide : true,
18042             /**
18043              * @event select
18044              * Fires when select a date.
18045              * @param {Roo.bootstrap.DateField} this
18046              * @param {Mixed} date The date value
18047              */
18048             select : true,
18049             /**
18050              * @event beforeselect
18051              * Fires when before select a date.
18052              * @param {Roo.bootstrap.DateField} this
18053              * @param {Mixed} date The date value
18054              */
18055             beforeselect : true
18056         });
18057 };
18058
18059 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18060     
18061     /**
18062      * @cfg {String} format
18063      * The default date format string which can be overriden for localization support.  The format must be
18064      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18065      */
18066     format : "m/d/y",
18067     /**
18068      * @cfg {String} altFormats
18069      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18070      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18071      */
18072     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18073     
18074     weekStart : 0,
18075     
18076     viewMode : '',
18077     
18078     minViewMode : '',
18079     
18080     todayHighlight : false,
18081     
18082     todayBtn: false,
18083     
18084     language: 'en',
18085     
18086     keyboardNavigation: true,
18087     
18088     calendarWeeks: false,
18089     
18090     startDate: -Infinity,
18091     
18092     endDate: Infinity,
18093     
18094     daysOfWeekDisabled: [],
18095     
18096     _events: [],
18097     
18098     singleMode : false,
18099     
18100     UTCDate: function()
18101     {
18102         return new Date(Date.UTC.apply(Date, arguments));
18103     },
18104     
18105     UTCToday: function()
18106     {
18107         var today = new Date();
18108         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18109     },
18110     
18111     getDate: function() {
18112             var d = this.getUTCDate();
18113             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18114     },
18115     
18116     getUTCDate: function() {
18117             return this.date;
18118     },
18119     
18120     setDate: function(d) {
18121             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18122     },
18123     
18124     setUTCDate: function(d) {
18125             this.date = d;
18126             this.setValue(this.formatDate(this.date));
18127     },
18128         
18129     onRender: function(ct, position)
18130     {
18131         
18132         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18133         
18134         this.language = this.language || 'en';
18135         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18136         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18137         
18138         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18139         this.format = this.format || 'm/d/y';
18140         this.isInline = false;
18141         this.isInput = true;
18142         this.component = this.el.select('.add-on', true).first() || false;
18143         this.component = (this.component && this.component.length === 0) ? false : this.component;
18144         this.hasInput = this.component && this.inputEl().length;
18145         
18146         if (typeof(this.minViewMode === 'string')) {
18147             switch (this.minViewMode) {
18148                 case 'months':
18149                     this.minViewMode = 1;
18150                     break;
18151                 case 'years':
18152                     this.minViewMode = 2;
18153                     break;
18154                 default:
18155                     this.minViewMode = 0;
18156                     break;
18157             }
18158         }
18159         
18160         if (typeof(this.viewMode === 'string')) {
18161             switch (this.viewMode) {
18162                 case 'months':
18163                     this.viewMode = 1;
18164                     break;
18165                 case 'years':
18166                     this.viewMode = 2;
18167                     break;
18168                 default:
18169                     this.viewMode = 0;
18170                     break;
18171             }
18172         }
18173                 
18174         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18175         
18176 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18177         
18178         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18179         
18180         this.picker().on('mousedown', this.onMousedown, this);
18181         this.picker().on('click', this.onClick, this);
18182         
18183         this.picker().addClass('datepicker-dropdown');
18184         
18185         this.startViewMode = this.viewMode;
18186         
18187         if(this.singleMode){
18188             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18189                 v.setVisibilityMode(Roo.Element.DISPLAY);
18190                 v.hide();
18191             });
18192             
18193             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18194                 v.setStyle('width', '189px');
18195             });
18196         }
18197         
18198         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18199             if(!this.calendarWeeks){
18200                 v.remove();
18201                 return;
18202             }
18203             
18204             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18205             v.attr('colspan', function(i, val){
18206                 return parseInt(val) + 1;
18207             });
18208         });
18209                         
18210         
18211         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18212         
18213         this.setStartDate(this.startDate);
18214         this.setEndDate(this.endDate);
18215         
18216         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18217         
18218         this.fillDow();
18219         this.fillMonths();
18220         this.update();
18221         this.showMode();
18222         
18223         if(this.isInline) {
18224             this.show();
18225         }
18226     },
18227     
18228     picker : function()
18229     {
18230         return this.pickerEl;
18231 //        return this.el.select('.datepicker', true).first();
18232     },
18233     
18234     fillDow: function()
18235     {
18236         var dowCnt = this.weekStart;
18237         
18238         var dow = {
18239             tag: 'tr',
18240             cn: [
18241                 
18242             ]
18243         };
18244         
18245         if(this.calendarWeeks){
18246             dow.cn.push({
18247                 tag: 'th',
18248                 cls: 'cw',
18249                 html: '&nbsp;'
18250             })
18251         }
18252         
18253         while (dowCnt < this.weekStart + 7) {
18254             dow.cn.push({
18255                 tag: 'th',
18256                 cls: 'dow',
18257                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18258             });
18259         }
18260         
18261         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18262     },
18263     
18264     fillMonths: function()
18265     {    
18266         var i = 0;
18267         var months = this.picker().select('>.datepicker-months td', true).first();
18268         
18269         months.dom.innerHTML = '';
18270         
18271         while (i < 12) {
18272             var month = {
18273                 tag: 'span',
18274                 cls: 'month',
18275                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18276             };
18277             
18278             months.createChild(month);
18279         }
18280         
18281     },
18282     
18283     update: function()
18284     {
18285         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;
18286         
18287         if (this.date < this.startDate) {
18288             this.viewDate = new Date(this.startDate);
18289         } else if (this.date > this.endDate) {
18290             this.viewDate = new Date(this.endDate);
18291         } else {
18292             this.viewDate = new Date(this.date);
18293         }
18294         
18295         this.fill();
18296     },
18297     
18298     fill: function() 
18299     {
18300         var d = new Date(this.viewDate),
18301                 year = d.getUTCFullYear(),
18302                 month = d.getUTCMonth(),
18303                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18304                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18305                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18306                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18307                 currentDate = this.date && this.date.valueOf(),
18308                 today = this.UTCToday();
18309         
18310         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18311         
18312 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18313         
18314 //        this.picker.select('>tfoot th.today').
18315 //                                              .text(dates[this.language].today)
18316 //                                              .toggle(this.todayBtn !== false);
18317     
18318         this.updateNavArrows();
18319         this.fillMonths();
18320                                                 
18321         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18322         
18323         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18324          
18325         prevMonth.setUTCDate(day);
18326         
18327         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18328         
18329         var nextMonth = new Date(prevMonth);
18330         
18331         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18332         
18333         nextMonth = nextMonth.valueOf();
18334         
18335         var fillMonths = false;
18336         
18337         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18338         
18339         while(prevMonth.valueOf() < nextMonth) {
18340             var clsName = '';
18341             
18342             if (prevMonth.getUTCDay() === this.weekStart) {
18343                 if(fillMonths){
18344                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18345                 }
18346                     
18347                 fillMonths = {
18348                     tag: 'tr',
18349                     cn: []
18350                 };
18351                 
18352                 if(this.calendarWeeks){
18353                     // ISO 8601: First week contains first thursday.
18354                     // ISO also states week starts on Monday, but we can be more abstract here.
18355                     var
18356                     // Start of current week: based on weekstart/current date
18357                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18358                     // Thursday of this week
18359                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18360                     // First Thursday of year, year from thursday
18361                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18362                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18363                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18364                     
18365                     fillMonths.cn.push({
18366                         tag: 'td',
18367                         cls: 'cw',
18368                         html: calWeek
18369                     });
18370                 }
18371             }
18372             
18373             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18374                 clsName += ' old';
18375             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18376                 clsName += ' new';
18377             }
18378             if (this.todayHighlight &&
18379                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18380                 prevMonth.getUTCMonth() == today.getMonth() &&
18381                 prevMonth.getUTCDate() == today.getDate()) {
18382                 clsName += ' today';
18383             }
18384             
18385             if (currentDate && prevMonth.valueOf() === currentDate) {
18386                 clsName += ' active';
18387             }
18388             
18389             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18390                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18391                     clsName += ' disabled';
18392             }
18393             
18394             fillMonths.cn.push({
18395                 tag: 'td',
18396                 cls: 'day ' + clsName,
18397                 html: prevMonth.getDate()
18398             });
18399             
18400             prevMonth.setDate(prevMonth.getDate()+1);
18401         }
18402           
18403         var currentYear = this.date && this.date.getUTCFullYear();
18404         var currentMonth = this.date && this.date.getUTCMonth();
18405         
18406         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18407         
18408         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18409             v.removeClass('active');
18410             
18411             if(currentYear === year && k === currentMonth){
18412                 v.addClass('active');
18413             }
18414             
18415             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18416                 v.addClass('disabled');
18417             }
18418             
18419         });
18420         
18421         
18422         year = parseInt(year/10, 10) * 10;
18423         
18424         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18425         
18426         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18427         
18428         year -= 1;
18429         for (var i = -1; i < 11; i++) {
18430             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18431                 tag: 'span',
18432                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18433                 html: year
18434             });
18435             
18436             year += 1;
18437         }
18438     },
18439     
18440     showMode: function(dir) 
18441     {
18442         if (dir) {
18443             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18444         }
18445         
18446         Roo.each(this.picker().select('>div',true).elements, function(v){
18447             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18448             v.hide();
18449         });
18450         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18451     },
18452     
18453     place: function()
18454     {
18455         if(this.isInline) {
18456             return;
18457         }
18458         
18459         this.picker().removeClass(['bottom', 'top']);
18460         
18461         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18462             /*
18463              * place to the top of element!
18464              *
18465              */
18466             
18467             this.picker().addClass('top');
18468             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18469             
18470             return;
18471         }
18472         
18473         this.picker().addClass('bottom');
18474         
18475         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18476     },
18477     
18478     parseDate : function(value)
18479     {
18480         if(!value || value instanceof Date){
18481             return value;
18482         }
18483         var v = Date.parseDate(value, this.format);
18484         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18485             v = Date.parseDate(value, 'Y-m-d');
18486         }
18487         if(!v && this.altFormats){
18488             if(!this.altFormatsArray){
18489                 this.altFormatsArray = this.altFormats.split("|");
18490             }
18491             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18492                 v = Date.parseDate(value, this.altFormatsArray[i]);
18493             }
18494         }
18495         return v;
18496     },
18497     
18498     formatDate : function(date, fmt)
18499     {   
18500         return (!date || !(date instanceof Date)) ?
18501         date : date.dateFormat(fmt || this.format);
18502     },
18503     
18504     onFocus : function()
18505     {
18506         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18507         this.show();
18508     },
18509     
18510     onBlur : function()
18511     {
18512         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18513         
18514         var d = this.inputEl().getValue();
18515         
18516         this.setValue(d);
18517                 
18518         this.hide();
18519     },
18520     
18521     show : function()
18522     {
18523         this.picker().show();
18524         this.update();
18525         this.place();
18526         
18527         this.fireEvent('show', this, this.date);
18528     },
18529     
18530     hide : function()
18531     {
18532         if(this.isInline) {
18533             return;
18534         }
18535         this.picker().hide();
18536         this.viewMode = this.startViewMode;
18537         this.showMode();
18538         
18539         this.fireEvent('hide', this, this.date);
18540         
18541     },
18542     
18543     onMousedown: function(e)
18544     {
18545         e.stopPropagation();
18546         e.preventDefault();
18547     },
18548     
18549     keyup: function(e)
18550     {
18551         Roo.bootstrap.DateField.superclass.keyup.call(this);
18552         this.update();
18553     },
18554
18555     setValue: function(v)
18556     {
18557         if(this.fireEvent('beforeselect', this, v) !== false){
18558             var d = new Date(this.parseDate(v) ).clearTime();
18559         
18560             if(isNaN(d.getTime())){
18561                 this.date = this.viewDate = '';
18562                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18563                 return;
18564             }
18565
18566             v = this.formatDate(d);
18567
18568             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18569
18570             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18571
18572             this.update();
18573
18574             this.fireEvent('select', this, this.date);
18575         }
18576     },
18577     
18578     getValue: function()
18579     {
18580         return this.formatDate(this.date);
18581     },
18582     
18583     fireKey: function(e)
18584     {
18585         if (!this.picker().isVisible()){
18586             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18587                 this.show();
18588             }
18589             return;
18590         }
18591         
18592         var dateChanged = false,
18593         dir, day, month,
18594         newDate, newViewDate;
18595         
18596         switch(e.keyCode){
18597             case 27: // escape
18598                 this.hide();
18599                 e.preventDefault();
18600                 break;
18601             case 37: // left
18602             case 39: // right
18603                 if (!this.keyboardNavigation) {
18604                     break;
18605                 }
18606                 dir = e.keyCode == 37 ? -1 : 1;
18607                 
18608                 if (e.ctrlKey){
18609                     newDate = this.moveYear(this.date, dir);
18610                     newViewDate = this.moveYear(this.viewDate, dir);
18611                 } else if (e.shiftKey){
18612                     newDate = this.moveMonth(this.date, dir);
18613                     newViewDate = this.moveMonth(this.viewDate, dir);
18614                 } else {
18615                     newDate = new Date(this.date);
18616                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18617                     newViewDate = new Date(this.viewDate);
18618                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18619                 }
18620                 if (this.dateWithinRange(newDate)){
18621                     this.date = newDate;
18622                     this.viewDate = newViewDate;
18623                     this.setValue(this.formatDate(this.date));
18624 //                    this.update();
18625                     e.preventDefault();
18626                     dateChanged = true;
18627                 }
18628                 break;
18629             case 38: // up
18630             case 40: // down
18631                 if (!this.keyboardNavigation) {
18632                     break;
18633                 }
18634                 dir = e.keyCode == 38 ? -1 : 1;
18635                 if (e.ctrlKey){
18636                     newDate = this.moveYear(this.date, dir);
18637                     newViewDate = this.moveYear(this.viewDate, dir);
18638                 } else if (e.shiftKey){
18639                     newDate = this.moveMonth(this.date, dir);
18640                     newViewDate = this.moveMonth(this.viewDate, dir);
18641                 } else {
18642                     newDate = new Date(this.date);
18643                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18644                     newViewDate = new Date(this.viewDate);
18645                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18646                 }
18647                 if (this.dateWithinRange(newDate)){
18648                     this.date = newDate;
18649                     this.viewDate = newViewDate;
18650                     this.setValue(this.formatDate(this.date));
18651 //                    this.update();
18652                     e.preventDefault();
18653                     dateChanged = true;
18654                 }
18655                 break;
18656             case 13: // enter
18657                 this.setValue(this.formatDate(this.date));
18658                 this.hide();
18659                 e.preventDefault();
18660                 break;
18661             case 9: // tab
18662                 this.setValue(this.formatDate(this.date));
18663                 this.hide();
18664                 break;
18665             case 16: // shift
18666             case 17: // ctrl
18667             case 18: // alt
18668                 break;
18669             default :
18670                 this.hide();
18671                 
18672         }
18673     },
18674     
18675     
18676     onClick: function(e) 
18677     {
18678         e.stopPropagation();
18679         e.preventDefault();
18680         
18681         var target = e.getTarget();
18682         
18683         if(target.nodeName.toLowerCase() === 'i'){
18684             target = Roo.get(target).dom.parentNode;
18685         }
18686         
18687         var nodeName = target.nodeName;
18688         var className = target.className;
18689         var html = target.innerHTML;
18690         //Roo.log(nodeName);
18691         
18692         switch(nodeName.toLowerCase()) {
18693             case 'th':
18694                 switch(className) {
18695                     case 'switch':
18696                         this.showMode(1);
18697                         break;
18698                     case 'prev':
18699                     case 'next':
18700                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18701                         switch(this.viewMode){
18702                                 case 0:
18703                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18704                                         break;
18705                                 case 1:
18706                                 case 2:
18707                                         this.viewDate = this.moveYear(this.viewDate, dir);
18708                                         break;
18709                         }
18710                         this.fill();
18711                         break;
18712                     case 'today':
18713                         var date = new Date();
18714                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18715 //                        this.fill()
18716                         this.setValue(this.formatDate(this.date));
18717                         
18718                         this.hide();
18719                         break;
18720                 }
18721                 break;
18722             case 'span':
18723                 if (className.indexOf('disabled') < 0) {
18724                     this.viewDate.setUTCDate(1);
18725                     if (className.indexOf('month') > -1) {
18726                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18727                     } else {
18728                         var year = parseInt(html, 10) || 0;
18729                         this.viewDate.setUTCFullYear(year);
18730                         
18731                     }
18732                     
18733                     if(this.singleMode){
18734                         this.setValue(this.formatDate(this.viewDate));
18735                         this.hide();
18736                         return;
18737                     }
18738                     
18739                     this.showMode(-1);
18740                     this.fill();
18741                 }
18742                 break;
18743                 
18744             case 'td':
18745                 //Roo.log(className);
18746                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18747                     var day = parseInt(html, 10) || 1;
18748                     var year = this.viewDate.getUTCFullYear(),
18749                         month = this.viewDate.getUTCMonth();
18750
18751                     if (className.indexOf('old') > -1) {
18752                         if(month === 0 ){
18753                             month = 11;
18754                             year -= 1;
18755                         }else{
18756                             month -= 1;
18757                         }
18758                     } else if (className.indexOf('new') > -1) {
18759                         if (month == 11) {
18760                             month = 0;
18761                             year += 1;
18762                         } else {
18763                             month += 1;
18764                         }
18765                     }
18766                     //Roo.log([year,month,day]);
18767                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18768                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18769 //                    this.fill();
18770                     //Roo.log(this.formatDate(this.date));
18771                     this.setValue(this.formatDate(this.date));
18772                     this.hide();
18773                 }
18774                 break;
18775         }
18776     },
18777     
18778     setStartDate: function(startDate)
18779     {
18780         this.startDate = startDate || -Infinity;
18781         if (this.startDate !== -Infinity) {
18782             this.startDate = this.parseDate(this.startDate);
18783         }
18784         this.update();
18785         this.updateNavArrows();
18786     },
18787
18788     setEndDate: function(endDate)
18789     {
18790         this.endDate = endDate || Infinity;
18791         if (this.endDate !== Infinity) {
18792             this.endDate = this.parseDate(this.endDate);
18793         }
18794         this.update();
18795         this.updateNavArrows();
18796     },
18797     
18798     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18799     {
18800         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18801         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18802             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18803         }
18804         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18805             return parseInt(d, 10);
18806         });
18807         this.update();
18808         this.updateNavArrows();
18809     },
18810     
18811     updateNavArrows: function() 
18812     {
18813         if(this.singleMode){
18814             return;
18815         }
18816         
18817         var d = new Date(this.viewDate),
18818         year = d.getUTCFullYear(),
18819         month = d.getUTCMonth();
18820         
18821         Roo.each(this.picker().select('.prev', true).elements, function(v){
18822             v.show();
18823             switch (this.viewMode) {
18824                 case 0:
18825
18826                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18827                         v.hide();
18828                     }
18829                     break;
18830                 case 1:
18831                 case 2:
18832                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18833                         v.hide();
18834                     }
18835                     break;
18836             }
18837         });
18838         
18839         Roo.each(this.picker().select('.next', true).elements, function(v){
18840             v.show();
18841             switch (this.viewMode) {
18842                 case 0:
18843
18844                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18845                         v.hide();
18846                     }
18847                     break;
18848                 case 1:
18849                 case 2:
18850                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18851                         v.hide();
18852                     }
18853                     break;
18854             }
18855         })
18856     },
18857     
18858     moveMonth: function(date, dir)
18859     {
18860         if (!dir) {
18861             return date;
18862         }
18863         var new_date = new Date(date.valueOf()),
18864         day = new_date.getUTCDate(),
18865         month = new_date.getUTCMonth(),
18866         mag = Math.abs(dir),
18867         new_month, test;
18868         dir = dir > 0 ? 1 : -1;
18869         if (mag == 1){
18870             test = dir == -1
18871             // If going back one month, make sure month is not current month
18872             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18873             ? function(){
18874                 return new_date.getUTCMonth() == month;
18875             }
18876             // If going forward one month, make sure month is as expected
18877             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18878             : function(){
18879                 return new_date.getUTCMonth() != new_month;
18880             };
18881             new_month = month + dir;
18882             new_date.setUTCMonth(new_month);
18883             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18884             if (new_month < 0 || new_month > 11) {
18885                 new_month = (new_month + 12) % 12;
18886             }
18887         } else {
18888             // For magnitudes >1, move one month at a time...
18889             for (var i=0; i<mag; i++) {
18890                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18891                 new_date = this.moveMonth(new_date, dir);
18892             }
18893             // ...then reset the day, keeping it in the new month
18894             new_month = new_date.getUTCMonth();
18895             new_date.setUTCDate(day);
18896             test = function(){
18897                 return new_month != new_date.getUTCMonth();
18898             };
18899         }
18900         // Common date-resetting loop -- if date is beyond end of month, make it
18901         // end of month
18902         while (test()){
18903             new_date.setUTCDate(--day);
18904             new_date.setUTCMonth(new_month);
18905         }
18906         return new_date;
18907     },
18908
18909     moveYear: function(date, dir)
18910     {
18911         return this.moveMonth(date, dir*12);
18912     },
18913
18914     dateWithinRange: function(date)
18915     {
18916         return date >= this.startDate && date <= this.endDate;
18917     },
18918
18919     
18920     remove: function() 
18921     {
18922         this.picker().remove();
18923     },
18924     
18925     validateValue : function(value)
18926     {
18927         if(value.length < 1)  {
18928             if(this.allowBlank){
18929                 return true;
18930             }
18931             return false;
18932         }
18933         
18934         if(value.length < this.minLength){
18935             return false;
18936         }
18937         if(value.length > this.maxLength){
18938             return false;
18939         }
18940         if(this.vtype){
18941             var vt = Roo.form.VTypes;
18942             if(!vt[this.vtype](value, this)){
18943                 return false;
18944             }
18945         }
18946         if(typeof this.validator == "function"){
18947             var msg = this.validator(value);
18948             if(msg !== true){
18949                 return false;
18950             }
18951         }
18952         
18953         if(this.regex && !this.regex.test(value)){
18954             return false;
18955         }
18956         
18957         if(typeof(this.parseDate(value)) == 'undefined'){
18958             return false;
18959         }
18960         
18961         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18962             return false;
18963         }      
18964         
18965         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18966             return false;
18967         } 
18968         
18969         
18970         return true;
18971     }
18972    
18973 });
18974
18975 Roo.apply(Roo.bootstrap.DateField,  {
18976     
18977     head : {
18978         tag: 'thead',
18979         cn: [
18980         {
18981             tag: 'tr',
18982             cn: [
18983             {
18984                 tag: 'th',
18985                 cls: 'prev',
18986                 html: '<i class="fa fa-arrow-left"/>'
18987             },
18988             {
18989                 tag: 'th',
18990                 cls: 'switch',
18991                 colspan: '5'
18992             },
18993             {
18994                 tag: 'th',
18995                 cls: 'next',
18996                 html: '<i class="fa fa-arrow-right"/>'
18997             }
18998
18999             ]
19000         }
19001         ]
19002     },
19003     
19004     content : {
19005         tag: 'tbody',
19006         cn: [
19007         {
19008             tag: 'tr',
19009             cn: [
19010             {
19011                 tag: 'td',
19012                 colspan: '7'
19013             }
19014             ]
19015         }
19016         ]
19017     },
19018     
19019     footer : {
19020         tag: 'tfoot',
19021         cn: [
19022         {
19023             tag: 'tr',
19024             cn: [
19025             {
19026                 tag: 'th',
19027                 colspan: '7',
19028                 cls: 'today'
19029             }
19030                     
19031             ]
19032         }
19033         ]
19034     },
19035     
19036     dates:{
19037         en: {
19038             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19039             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19040             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19041             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19042             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19043             today: "Today"
19044         }
19045     },
19046     
19047     modes: [
19048     {
19049         clsName: 'days',
19050         navFnc: 'Month',
19051         navStep: 1
19052     },
19053     {
19054         clsName: 'months',
19055         navFnc: 'FullYear',
19056         navStep: 1
19057     },
19058     {
19059         clsName: 'years',
19060         navFnc: 'FullYear',
19061         navStep: 10
19062     }]
19063 });
19064
19065 Roo.apply(Roo.bootstrap.DateField,  {
19066   
19067     template : {
19068         tag: 'div',
19069         cls: 'datepicker dropdown-menu roo-dynamic',
19070         cn: [
19071         {
19072             tag: 'div',
19073             cls: 'datepicker-days',
19074             cn: [
19075             {
19076                 tag: 'table',
19077                 cls: 'table-condensed',
19078                 cn:[
19079                 Roo.bootstrap.DateField.head,
19080                 {
19081                     tag: 'tbody'
19082                 },
19083                 Roo.bootstrap.DateField.footer
19084                 ]
19085             }
19086             ]
19087         },
19088         {
19089             tag: 'div',
19090             cls: 'datepicker-months',
19091             cn: [
19092             {
19093                 tag: 'table',
19094                 cls: 'table-condensed',
19095                 cn:[
19096                 Roo.bootstrap.DateField.head,
19097                 Roo.bootstrap.DateField.content,
19098                 Roo.bootstrap.DateField.footer
19099                 ]
19100             }
19101             ]
19102         },
19103         {
19104             tag: 'div',
19105             cls: 'datepicker-years',
19106             cn: [
19107             {
19108                 tag: 'table',
19109                 cls: 'table-condensed',
19110                 cn:[
19111                 Roo.bootstrap.DateField.head,
19112                 Roo.bootstrap.DateField.content,
19113                 Roo.bootstrap.DateField.footer
19114                 ]
19115             }
19116             ]
19117         }
19118         ]
19119     }
19120 });
19121
19122  
19123
19124  /*
19125  * - LGPL
19126  *
19127  * TimeField
19128  * 
19129  */
19130
19131 /**
19132  * @class Roo.bootstrap.TimeField
19133  * @extends Roo.bootstrap.Input
19134  * Bootstrap DateField class
19135  * 
19136  * 
19137  * @constructor
19138  * Create a new TimeField
19139  * @param {Object} config The config object
19140  */
19141
19142 Roo.bootstrap.TimeField = function(config){
19143     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19144     this.addEvents({
19145             /**
19146              * @event show
19147              * Fires when this field show.
19148              * @param {Roo.bootstrap.DateField} thisthis
19149              * @param {Mixed} date The date value
19150              */
19151             show : true,
19152             /**
19153              * @event show
19154              * Fires when this field hide.
19155              * @param {Roo.bootstrap.DateField} this
19156              * @param {Mixed} date The date value
19157              */
19158             hide : true,
19159             /**
19160              * @event select
19161              * Fires when select a date.
19162              * @param {Roo.bootstrap.DateField} this
19163              * @param {Mixed} date The date value
19164              */
19165             select : true
19166         });
19167 };
19168
19169 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19170     
19171     /**
19172      * @cfg {String} format
19173      * The default time format string which can be overriden for localization support.  The format must be
19174      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19175      */
19176     format : "H:i",
19177        
19178     onRender: function(ct, position)
19179     {
19180         
19181         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19182                 
19183         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19184         
19185         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19186         
19187         this.pop = this.picker().select('>.datepicker-time',true).first();
19188         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19189         
19190         this.picker().on('mousedown', this.onMousedown, this);
19191         this.picker().on('click', this.onClick, this);
19192         
19193         this.picker().addClass('datepicker-dropdown');
19194     
19195         this.fillTime();
19196         this.update();
19197             
19198         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19199         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19200         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19201         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19202         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19203         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19204
19205     },
19206     
19207     fireKey: function(e){
19208         if (!this.picker().isVisible()){
19209             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19210                 this.show();
19211             }
19212             return;
19213         }
19214
19215         e.preventDefault();
19216         
19217         switch(e.keyCode){
19218             case 27: // escape
19219                 this.hide();
19220                 break;
19221             case 37: // left
19222             case 39: // right
19223                 this.onTogglePeriod();
19224                 break;
19225             case 38: // up
19226                 this.onIncrementMinutes();
19227                 break;
19228             case 40: // down
19229                 this.onDecrementMinutes();
19230                 break;
19231             case 13: // enter
19232             case 9: // tab
19233                 this.setTime();
19234                 break;
19235         }
19236     },
19237     
19238     onClick: function(e) {
19239         e.stopPropagation();
19240         e.preventDefault();
19241     },
19242     
19243     picker : function()
19244     {
19245         return this.el.select('.datepicker', true).first();
19246     },
19247     
19248     fillTime: function()
19249     {    
19250         var time = this.pop.select('tbody', true).first();
19251         
19252         time.dom.innerHTML = '';
19253         
19254         time.createChild({
19255             tag: 'tr',
19256             cn: [
19257                 {
19258                     tag: 'td',
19259                     cn: [
19260                         {
19261                             tag: 'a',
19262                             href: '#',
19263                             cls: 'btn',
19264                             cn: [
19265                                 {
19266                                     tag: 'span',
19267                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19268                                 }
19269                             ]
19270                         } 
19271                     ]
19272                 },
19273                 {
19274                     tag: 'td',
19275                     cls: 'separator'
19276                 },
19277                 {
19278                     tag: 'td',
19279                     cn: [
19280                         {
19281                             tag: 'a',
19282                             href: '#',
19283                             cls: 'btn',
19284                             cn: [
19285                                 {
19286                                     tag: 'span',
19287                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19288                                 }
19289                             ]
19290                         }
19291                     ]
19292                 },
19293                 {
19294                     tag: 'td',
19295                     cls: 'separator'
19296                 }
19297             ]
19298         });
19299         
19300         time.createChild({
19301             tag: 'tr',
19302             cn: [
19303                 {
19304                     tag: 'td',
19305                     cn: [
19306                         {
19307                             tag: 'span',
19308                             cls: 'timepicker-hour',
19309                             html: '00'
19310                         }  
19311                     ]
19312                 },
19313                 {
19314                     tag: 'td',
19315                     cls: 'separator',
19316                     html: ':'
19317                 },
19318                 {
19319                     tag: 'td',
19320                     cn: [
19321                         {
19322                             tag: 'span',
19323                             cls: 'timepicker-minute',
19324                             html: '00'
19325                         }  
19326                     ]
19327                 },
19328                 {
19329                     tag: 'td',
19330                     cls: 'separator'
19331                 },
19332                 {
19333                     tag: 'td',
19334                     cn: [
19335                         {
19336                             tag: 'button',
19337                             type: 'button',
19338                             cls: 'btn btn-primary period',
19339                             html: 'AM'
19340                             
19341                         }
19342                     ]
19343                 }
19344             ]
19345         });
19346         
19347         time.createChild({
19348             tag: 'tr',
19349             cn: [
19350                 {
19351                     tag: 'td',
19352                     cn: [
19353                         {
19354                             tag: 'a',
19355                             href: '#',
19356                             cls: 'btn',
19357                             cn: [
19358                                 {
19359                                     tag: 'span',
19360                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19361                                 }
19362                             ]
19363                         }
19364                     ]
19365                 },
19366                 {
19367                     tag: 'td',
19368                     cls: 'separator'
19369                 },
19370                 {
19371                     tag: 'td',
19372                     cn: [
19373                         {
19374                             tag: 'a',
19375                             href: '#',
19376                             cls: 'btn',
19377                             cn: [
19378                                 {
19379                                     tag: 'span',
19380                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19381                                 }
19382                             ]
19383                         }
19384                     ]
19385                 },
19386                 {
19387                     tag: 'td',
19388                     cls: 'separator'
19389                 }
19390             ]
19391         });
19392         
19393     },
19394     
19395     update: function()
19396     {
19397         
19398         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19399         
19400         this.fill();
19401     },
19402     
19403     fill: function() 
19404     {
19405         var hours = this.time.getHours();
19406         var minutes = this.time.getMinutes();
19407         var period = 'AM';
19408         
19409         if(hours > 11){
19410             period = 'PM';
19411         }
19412         
19413         if(hours == 0){
19414             hours = 12;
19415         }
19416         
19417         
19418         if(hours > 12){
19419             hours = hours - 12;
19420         }
19421         
19422         if(hours < 10){
19423             hours = '0' + hours;
19424         }
19425         
19426         if(minutes < 10){
19427             minutes = '0' + minutes;
19428         }
19429         
19430         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19431         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19432         this.pop.select('button', true).first().dom.innerHTML = period;
19433         
19434     },
19435     
19436     place: function()
19437     {   
19438         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19439         
19440         var cls = ['bottom'];
19441         
19442         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19443             cls.pop();
19444             cls.push('top');
19445         }
19446         
19447         cls.push('right');
19448         
19449         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19450             cls.pop();
19451             cls.push('left');
19452         }
19453         
19454         this.picker().addClass(cls.join('-'));
19455         
19456         var _this = this;
19457         
19458         Roo.each(cls, function(c){
19459             if(c == 'bottom'){
19460                 _this.picker().setTop(_this.inputEl().getHeight());
19461                 return;
19462             }
19463             if(c == 'top'){
19464                 _this.picker().setTop(0 - _this.picker().getHeight());
19465                 return;
19466             }
19467             
19468             if(c == 'left'){
19469                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19470                 return;
19471             }
19472             if(c == 'right'){
19473                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19474                 return;
19475             }
19476         });
19477         
19478     },
19479   
19480     onFocus : function()
19481     {
19482         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19483         this.show();
19484     },
19485     
19486     onBlur : function()
19487     {
19488         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19489         this.hide();
19490     },
19491     
19492     show : function()
19493     {
19494         this.picker().show();
19495         this.pop.show();
19496         this.update();
19497         this.place();
19498         
19499         this.fireEvent('show', this, this.date);
19500     },
19501     
19502     hide : function()
19503     {
19504         this.picker().hide();
19505         this.pop.hide();
19506         
19507         this.fireEvent('hide', this, this.date);
19508     },
19509     
19510     setTime : function()
19511     {
19512         this.hide();
19513         this.setValue(this.time.format(this.format));
19514         
19515         this.fireEvent('select', this, this.date);
19516         
19517         
19518     },
19519     
19520     onMousedown: function(e){
19521         e.stopPropagation();
19522         e.preventDefault();
19523     },
19524     
19525     onIncrementHours: function()
19526     {
19527         Roo.log('onIncrementHours');
19528         this.time = this.time.add(Date.HOUR, 1);
19529         this.update();
19530         
19531     },
19532     
19533     onDecrementHours: function()
19534     {
19535         Roo.log('onDecrementHours');
19536         this.time = this.time.add(Date.HOUR, -1);
19537         this.update();
19538     },
19539     
19540     onIncrementMinutes: function()
19541     {
19542         Roo.log('onIncrementMinutes');
19543         this.time = this.time.add(Date.MINUTE, 1);
19544         this.update();
19545     },
19546     
19547     onDecrementMinutes: function()
19548     {
19549         Roo.log('onDecrementMinutes');
19550         this.time = this.time.add(Date.MINUTE, -1);
19551         this.update();
19552     },
19553     
19554     onTogglePeriod: function()
19555     {
19556         Roo.log('onTogglePeriod');
19557         this.time = this.time.add(Date.HOUR, 12);
19558         this.update();
19559     }
19560     
19561    
19562 });
19563
19564 Roo.apply(Roo.bootstrap.TimeField,  {
19565     
19566     content : {
19567         tag: 'tbody',
19568         cn: [
19569             {
19570                 tag: 'tr',
19571                 cn: [
19572                 {
19573                     tag: 'td',
19574                     colspan: '7'
19575                 }
19576                 ]
19577             }
19578         ]
19579     },
19580     
19581     footer : {
19582         tag: 'tfoot',
19583         cn: [
19584             {
19585                 tag: 'tr',
19586                 cn: [
19587                 {
19588                     tag: 'th',
19589                     colspan: '7',
19590                     cls: '',
19591                     cn: [
19592                         {
19593                             tag: 'button',
19594                             cls: 'btn btn-info ok',
19595                             html: 'OK'
19596                         }
19597                     ]
19598                 }
19599
19600                 ]
19601             }
19602         ]
19603     }
19604 });
19605
19606 Roo.apply(Roo.bootstrap.TimeField,  {
19607   
19608     template : {
19609         tag: 'div',
19610         cls: 'datepicker dropdown-menu',
19611         cn: [
19612             {
19613                 tag: 'div',
19614                 cls: 'datepicker-time',
19615                 cn: [
19616                 {
19617                     tag: 'table',
19618                     cls: 'table-condensed',
19619                     cn:[
19620                     Roo.bootstrap.TimeField.content,
19621                     Roo.bootstrap.TimeField.footer
19622                     ]
19623                 }
19624                 ]
19625             }
19626         ]
19627     }
19628 });
19629
19630  
19631
19632  /*
19633  * - LGPL
19634  *
19635  * MonthField
19636  * 
19637  */
19638
19639 /**
19640  * @class Roo.bootstrap.MonthField
19641  * @extends Roo.bootstrap.Input
19642  * Bootstrap MonthField class
19643  * 
19644  * @cfg {String} language default en
19645  * 
19646  * @constructor
19647  * Create a new MonthField
19648  * @param {Object} config The config object
19649  */
19650
19651 Roo.bootstrap.MonthField = function(config){
19652     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19653     
19654     this.addEvents({
19655         /**
19656          * @event show
19657          * Fires when this field show.
19658          * @param {Roo.bootstrap.MonthField} this
19659          * @param {Mixed} date The date value
19660          */
19661         show : true,
19662         /**
19663          * @event show
19664          * Fires when this field hide.
19665          * @param {Roo.bootstrap.MonthField} this
19666          * @param {Mixed} date The date value
19667          */
19668         hide : true,
19669         /**
19670          * @event select
19671          * Fires when select a date.
19672          * @param {Roo.bootstrap.MonthField} this
19673          * @param {String} oldvalue The old value
19674          * @param {String} newvalue The new value
19675          */
19676         select : true
19677     });
19678 };
19679
19680 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19681     
19682     onRender: function(ct, position)
19683     {
19684         
19685         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19686         
19687         this.language = this.language || 'en';
19688         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19689         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19690         
19691         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19692         this.isInline = false;
19693         this.isInput = true;
19694         this.component = this.el.select('.add-on', true).first() || false;
19695         this.component = (this.component && this.component.length === 0) ? false : this.component;
19696         this.hasInput = this.component && this.inputEL().length;
19697         
19698         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19699         
19700         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19701         
19702         this.picker().on('mousedown', this.onMousedown, this);
19703         this.picker().on('click', this.onClick, this);
19704         
19705         this.picker().addClass('datepicker-dropdown');
19706         
19707         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19708             v.setStyle('width', '189px');
19709         });
19710         
19711         this.fillMonths();
19712         
19713         this.update();
19714         
19715         if(this.isInline) {
19716             this.show();
19717         }
19718         
19719     },
19720     
19721     setValue: function(v, suppressEvent)
19722     {   
19723         var o = this.getValue();
19724         
19725         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19726         
19727         this.update();
19728
19729         if(suppressEvent !== true){
19730             this.fireEvent('select', this, o, v);
19731         }
19732         
19733     },
19734     
19735     getValue: function()
19736     {
19737         return this.value;
19738     },
19739     
19740     onClick: function(e) 
19741     {
19742         e.stopPropagation();
19743         e.preventDefault();
19744         
19745         var target = e.getTarget();
19746         
19747         if(target.nodeName.toLowerCase() === 'i'){
19748             target = Roo.get(target).dom.parentNode;
19749         }
19750         
19751         var nodeName = target.nodeName;
19752         var className = target.className;
19753         var html = target.innerHTML;
19754         
19755         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19756             return;
19757         }
19758         
19759         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19760         
19761         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19762         
19763         this.hide();
19764                         
19765     },
19766     
19767     picker : function()
19768     {
19769         return this.pickerEl;
19770     },
19771     
19772     fillMonths: function()
19773     {    
19774         var i = 0;
19775         var months = this.picker().select('>.datepicker-months td', true).first();
19776         
19777         months.dom.innerHTML = '';
19778         
19779         while (i < 12) {
19780             var month = {
19781                 tag: 'span',
19782                 cls: 'month',
19783                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19784             };
19785             
19786             months.createChild(month);
19787         }
19788         
19789     },
19790     
19791     update: function()
19792     {
19793         var _this = this;
19794         
19795         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19796             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19797         }
19798         
19799         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19800             e.removeClass('active');
19801             
19802             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19803                 e.addClass('active');
19804             }
19805         })
19806     },
19807     
19808     place: function()
19809     {
19810         if(this.isInline) {
19811             return;
19812         }
19813         
19814         this.picker().removeClass(['bottom', 'top']);
19815         
19816         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19817             /*
19818              * place to the top of element!
19819              *
19820              */
19821             
19822             this.picker().addClass('top');
19823             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19824             
19825             return;
19826         }
19827         
19828         this.picker().addClass('bottom');
19829         
19830         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19831     },
19832     
19833     onFocus : function()
19834     {
19835         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19836         this.show();
19837     },
19838     
19839     onBlur : function()
19840     {
19841         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19842         
19843         var d = this.inputEl().getValue();
19844         
19845         this.setValue(d);
19846                 
19847         this.hide();
19848     },
19849     
19850     show : function()
19851     {
19852         this.picker().show();
19853         this.picker().select('>.datepicker-months', true).first().show();
19854         this.update();
19855         this.place();
19856         
19857         this.fireEvent('show', this, this.date);
19858     },
19859     
19860     hide : function()
19861     {
19862         if(this.isInline) {
19863             return;
19864         }
19865         this.picker().hide();
19866         this.fireEvent('hide', this, this.date);
19867         
19868     },
19869     
19870     onMousedown: function(e)
19871     {
19872         e.stopPropagation();
19873         e.preventDefault();
19874     },
19875     
19876     keyup: function(e)
19877     {
19878         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19879         this.update();
19880     },
19881
19882     fireKey: function(e)
19883     {
19884         if (!this.picker().isVisible()){
19885             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19886                 this.show();
19887             }
19888             return;
19889         }
19890         
19891         var dir;
19892         
19893         switch(e.keyCode){
19894             case 27: // escape
19895                 this.hide();
19896                 e.preventDefault();
19897                 break;
19898             case 37: // left
19899             case 39: // right
19900                 dir = e.keyCode == 37 ? -1 : 1;
19901                 
19902                 this.vIndex = this.vIndex + dir;
19903                 
19904                 if(this.vIndex < 0){
19905                     this.vIndex = 0;
19906                 }
19907                 
19908                 if(this.vIndex > 11){
19909                     this.vIndex = 11;
19910                 }
19911                 
19912                 if(isNaN(this.vIndex)){
19913                     this.vIndex = 0;
19914                 }
19915                 
19916                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19917                 
19918                 break;
19919             case 38: // up
19920             case 40: // down
19921                 
19922                 dir = e.keyCode == 38 ? -1 : 1;
19923                 
19924                 this.vIndex = this.vIndex + dir * 4;
19925                 
19926                 if(this.vIndex < 0){
19927                     this.vIndex = 0;
19928                 }
19929                 
19930                 if(this.vIndex > 11){
19931                     this.vIndex = 11;
19932                 }
19933                 
19934                 if(isNaN(this.vIndex)){
19935                     this.vIndex = 0;
19936                 }
19937                 
19938                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19939                 break;
19940                 
19941             case 13: // enter
19942                 
19943                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19944                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19945                 }
19946                 
19947                 this.hide();
19948                 e.preventDefault();
19949                 break;
19950             case 9: // tab
19951                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19952                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19953                 }
19954                 this.hide();
19955                 break;
19956             case 16: // shift
19957             case 17: // ctrl
19958             case 18: // alt
19959                 break;
19960             default :
19961                 this.hide();
19962                 
19963         }
19964     },
19965     
19966     remove: function() 
19967     {
19968         this.picker().remove();
19969     }
19970    
19971 });
19972
19973 Roo.apply(Roo.bootstrap.MonthField,  {
19974     
19975     content : {
19976         tag: 'tbody',
19977         cn: [
19978         {
19979             tag: 'tr',
19980             cn: [
19981             {
19982                 tag: 'td',
19983                 colspan: '7'
19984             }
19985             ]
19986         }
19987         ]
19988     },
19989     
19990     dates:{
19991         en: {
19992             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19993             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19994         }
19995     }
19996 });
19997
19998 Roo.apply(Roo.bootstrap.MonthField,  {
19999   
20000     template : {
20001         tag: 'div',
20002         cls: 'datepicker dropdown-menu roo-dynamic',
20003         cn: [
20004             {
20005                 tag: 'div',
20006                 cls: 'datepicker-months',
20007                 cn: [
20008                 {
20009                     tag: 'table',
20010                     cls: 'table-condensed',
20011                     cn:[
20012                         Roo.bootstrap.DateField.content
20013                     ]
20014                 }
20015                 ]
20016             }
20017         ]
20018     }
20019 });
20020
20021  
20022
20023  
20024  /*
20025  * - LGPL
20026  *
20027  * CheckBox
20028  * 
20029  */
20030
20031 /**
20032  * @class Roo.bootstrap.CheckBox
20033  * @extends Roo.bootstrap.Input
20034  * Bootstrap CheckBox class
20035  * 
20036  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20037  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20038  * @cfg {String} boxLabel The text that appears beside the checkbox
20039  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20040  * @cfg {Boolean} checked initnal the element
20041  * @cfg {Boolean} inline inline the element (default false)
20042  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20043  * 
20044  * @constructor
20045  * Create a new CheckBox
20046  * @param {Object} config The config object
20047  */
20048
20049 Roo.bootstrap.CheckBox = function(config){
20050     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20051    
20052     this.addEvents({
20053         /**
20054         * @event check
20055         * Fires when the element is checked or unchecked.
20056         * @param {Roo.bootstrap.CheckBox} this This input
20057         * @param {Boolean} checked The new checked value
20058         */
20059        check : true
20060     });
20061     
20062 };
20063
20064 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20065   
20066     inputType: 'checkbox',
20067     inputValue: 1,
20068     valueOff: 0,
20069     boxLabel: false,
20070     checked: false,
20071     weight : false,
20072     inline: false,
20073     
20074     getAutoCreate : function()
20075     {
20076         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20077         
20078         var id = Roo.id();
20079         
20080         var cfg = {};
20081         
20082         cfg.cls = 'form-group ' + this.inputType; //input-group
20083         
20084         if(this.inline){
20085             cfg.cls += ' ' + this.inputType + '-inline';
20086         }
20087         
20088         var input =  {
20089             tag: 'input',
20090             id : id,
20091             type : this.inputType,
20092             value : this.inputValue,
20093             cls : 'roo-' + this.inputType, //'form-box',
20094             placeholder : this.placeholder || ''
20095             
20096         };
20097         
20098         if(this.inputType != 'radio'){
20099             var hidden =  {
20100                 tag: 'input',
20101                 type : 'hidden',
20102                 cls : 'roo-hidden-value',
20103                 value : this.checked ? this.valueOff : this.inputValue
20104             };
20105         }
20106         
20107             
20108         if (this.weight) { // Validity check?
20109             cfg.cls += " " + this.inputType + "-" + this.weight;
20110         }
20111         
20112         if (this.disabled) {
20113             input.disabled=true;
20114         }
20115         
20116         if(this.checked){
20117             input.checked = this.checked;
20118             
20119         }
20120         
20121         
20122         if (this.name) {
20123             
20124             input.name = this.name;
20125             
20126             if(this.inputType != 'radio'){
20127                 hidden.name = this.name;
20128                 input.name = '_hidden_' + this.name;
20129             }
20130         }
20131         
20132         if (this.size) {
20133             input.cls += ' input-' + this.size;
20134         }
20135         
20136         var settings=this;
20137         
20138         ['xs','sm','md','lg'].map(function(size){
20139             if (settings[size]) {
20140                 cfg.cls += ' col-' + size + '-' + settings[size];
20141             }
20142         });
20143         
20144         var inputblock = input;
20145          
20146         if (this.before || this.after) {
20147             
20148             inputblock = {
20149                 cls : 'input-group',
20150                 cn :  [] 
20151             };
20152             
20153             if (this.before) {
20154                 inputblock.cn.push({
20155                     tag :'span',
20156                     cls : 'input-group-addon',
20157                     html : this.before
20158                 });
20159             }
20160             
20161             inputblock.cn.push(input);
20162             
20163             if(this.inputType != 'radio'){
20164                 inputblock.cn.push(hidden);
20165             }
20166             
20167             if (this.after) {
20168                 inputblock.cn.push({
20169                     tag :'span',
20170                     cls : 'input-group-addon',
20171                     html : this.after
20172                 });
20173             }
20174             
20175         }
20176         
20177         if (align ==='left' && this.fieldLabel.length) {
20178 //                Roo.log("left and has label");
20179             cfg.cn = [
20180                 {
20181                     tag: 'label',
20182                     'for' :  id,
20183                     cls : 'control-label',
20184                     html : this.fieldLabel
20185
20186                 },
20187                 {
20188                     cls : "", 
20189                     cn: [
20190                         inputblock
20191                     ]
20192                 }
20193             ];
20194             
20195             if(this.labelWidth > 12){
20196                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20197             }
20198             
20199             if(this.labelWidth < 13 && this.labelmd == 0){
20200                 this.labelmd = this.labelWidth;
20201             }
20202             
20203             if(this.labellg > 0){
20204                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20205                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20206             }
20207             
20208             if(this.labelmd > 0){
20209                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20210                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20211             }
20212             
20213             if(this.labelsm > 0){
20214                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20215                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20216             }
20217             
20218             if(this.labelxs > 0){
20219                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20220                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20221             }
20222             
20223         } else if ( this.fieldLabel.length) {
20224 //                Roo.log(" label");
20225                 cfg.cn = [
20226                    
20227                     {
20228                         tag: this.boxLabel ? 'span' : 'label',
20229                         'for': id,
20230                         cls: 'control-label box-input-label',
20231                         //cls : 'input-group-addon',
20232                         html : this.fieldLabel
20233                         
20234                     },
20235                     
20236                     inputblock
20237                     
20238                 ];
20239
20240         } else {
20241             
20242 //                Roo.log(" no label && no align");
20243                 cfg.cn = [  inputblock ] ;
20244                 
20245                 
20246         }
20247         
20248         if(this.boxLabel){
20249              var boxLabelCfg = {
20250                 tag: 'label',
20251                 //'for': id, // box label is handled by onclick - so no for...
20252                 cls: 'box-label',
20253                 html: this.boxLabel
20254             };
20255             
20256             if(this.tooltip){
20257                 boxLabelCfg.tooltip = this.tooltip;
20258             }
20259              
20260             cfg.cn.push(boxLabelCfg);
20261         }
20262         
20263         if(this.inputType != 'radio'){
20264             cfg.cn.push(hidden);
20265         }
20266         
20267         return cfg;
20268         
20269     },
20270     
20271     /**
20272      * return the real input element.
20273      */
20274     inputEl: function ()
20275     {
20276         return this.el.select('input.roo-' + this.inputType,true).first();
20277     },
20278     hiddenEl: function ()
20279     {
20280         return this.el.select('input.roo-hidden-value',true).first();
20281     },
20282     
20283     labelEl: function()
20284     {
20285         return this.el.select('label.control-label',true).first();
20286     },
20287     /* depricated... */
20288     
20289     label: function()
20290     {
20291         return this.labelEl();
20292     },
20293     
20294     boxLabelEl: function()
20295     {
20296         return this.el.select('label.box-label',true).first();
20297     },
20298     
20299     initEvents : function()
20300     {
20301 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20302         
20303         this.inputEl().on('click', this.onClick,  this);
20304         
20305         if (this.boxLabel) { 
20306             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20307         }
20308         
20309         this.startValue = this.getValue();
20310         
20311         if(this.groupId){
20312             Roo.bootstrap.CheckBox.register(this);
20313         }
20314     },
20315     
20316     onClick : function()
20317     {   
20318         this.setChecked(!this.checked);
20319     },
20320     
20321     setChecked : function(state,suppressEvent)
20322     {
20323         this.startValue = this.getValue();
20324
20325         if(this.inputType == 'radio'){
20326             
20327             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20328                 e.dom.checked = false;
20329             });
20330             
20331             this.inputEl().dom.checked = true;
20332             
20333             this.inputEl().dom.value = this.inputValue;
20334             
20335             if(suppressEvent !== true){
20336                 this.fireEvent('check', this, true);
20337             }
20338             
20339             this.validate();
20340             
20341             return;
20342         }
20343         
20344         this.checked = state;
20345         
20346         this.inputEl().dom.checked = state;
20347         
20348         
20349         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20350         
20351         if(suppressEvent !== true){
20352             this.fireEvent('check', this, state);
20353         }
20354         
20355         this.validate();
20356     },
20357     
20358     getValue : function()
20359     {
20360         if(this.inputType == 'radio'){
20361             return this.getGroupValue();
20362         }
20363         
20364         return this.hiddenEl().dom.value;
20365         
20366     },
20367     
20368     getGroupValue : function()
20369     {
20370         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20371             return '';
20372         }
20373         
20374         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20375     },
20376     
20377     setValue : function(v,suppressEvent)
20378     {
20379         if(this.inputType == 'radio'){
20380             this.setGroupValue(v, suppressEvent);
20381             return;
20382         }
20383         
20384         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20385         
20386         this.validate();
20387     },
20388     
20389     setGroupValue : function(v, suppressEvent)
20390     {
20391         this.startValue = this.getValue();
20392         
20393         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20394             e.dom.checked = false;
20395             
20396             if(e.dom.value == v){
20397                 e.dom.checked = true;
20398             }
20399         });
20400         
20401         if(suppressEvent !== true){
20402             this.fireEvent('check', this, true);
20403         }
20404
20405         this.validate();
20406         
20407         return;
20408     },
20409     
20410     validate : function()
20411     {
20412         if(
20413                 this.disabled || 
20414                 (this.inputType == 'radio' && this.validateRadio()) ||
20415                 (this.inputType == 'checkbox' && this.validateCheckbox())
20416         ){
20417             this.markValid();
20418             return true;
20419         }
20420         
20421         this.markInvalid();
20422         return false;
20423     },
20424     
20425     validateRadio : function()
20426     {
20427         if(this.allowBlank){
20428             return true;
20429         }
20430         
20431         var valid = false;
20432         
20433         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20434             if(!e.dom.checked){
20435                 return;
20436             }
20437             
20438             valid = true;
20439             
20440             return false;
20441         });
20442         
20443         return valid;
20444     },
20445     
20446     validateCheckbox : function()
20447     {
20448         if(!this.groupId){
20449             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20450             //return (this.getValue() == this.inputValue) ? true : false;
20451         }
20452         
20453         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20454         
20455         if(!group){
20456             return false;
20457         }
20458         
20459         var r = false;
20460         
20461         for(var i in group){
20462             if(r){
20463                 break;
20464             }
20465             
20466             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20467         }
20468         
20469         return r;
20470     },
20471     
20472     /**
20473      * Mark this field as valid
20474      */
20475     markValid : function()
20476     {
20477         var _this = this;
20478         
20479         this.fireEvent('valid', this);
20480         
20481         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20482         
20483         if(this.groupId){
20484             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20485         }
20486         
20487         if(label){
20488             label.markValid();
20489         }
20490
20491         if(this.inputType == 'radio'){
20492             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20493                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20494                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20495             });
20496             
20497             return;
20498         }
20499
20500         if(!this.groupId){
20501             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20502             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20503             return;
20504         }
20505         
20506         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20507         
20508         if(!group){
20509             return;
20510         }
20511         
20512         for(var i in group){
20513             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20514             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20515         }
20516     },
20517     
20518      /**
20519      * Mark this field as invalid
20520      * @param {String} msg The validation message
20521      */
20522     markInvalid : function(msg)
20523     {
20524         if(this.allowBlank){
20525             return;
20526         }
20527         
20528         var _this = this;
20529         
20530         this.fireEvent('invalid', this, msg);
20531         
20532         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20533         
20534         if(this.groupId){
20535             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20536         }
20537         
20538         if(label){
20539             label.markInvalid();
20540         }
20541             
20542         if(this.inputType == 'radio'){
20543             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20544                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20545                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20546             });
20547             
20548             return;
20549         }
20550         
20551         if(!this.groupId){
20552             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20553             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20554             return;
20555         }
20556         
20557         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20558         
20559         if(!group){
20560             return;
20561         }
20562         
20563         for(var i in group){
20564             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20565             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20566         }
20567         
20568     },
20569     
20570     clearInvalid : function()
20571     {
20572         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20573         
20574         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20575         
20576         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20577         
20578         if (label) {
20579             label.iconEl.removeClass(label.validClass);
20580             label.iconEl.removeClass(label.invalidClass);
20581         }
20582     },
20583     
20584     disable : function()
20585     {
20586         if(this.inputType != 'radio'){
20587             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20588             return;
20589         }
20590         
20591         var _this = this;
20592         
20593         if(this.rendered){
20594             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20595                 _this.getActionEl().addClass(this.disabledClass);
20596                 e.dom.disabled = true;
20597             });
20598         }
20599         
20600         this.disabled = true;
20601         this.fireEvent("disable", this);
20602         return this;
20603     },
20604
20605     enable : function()
20606     {
20607         if(this.inputType != 'radio'){
20608             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20609             return;
20610         }
20611         
20612         var _this = this;
20613         
20614         if(this.rendered){
20615             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20616                 _this.getActionEl().removeClass(this.disabledClass);
20617                 e.dom.disabled = false;
20618             });
20619         }
20620         
20621         this.disabled = false;
20622         this.fireEvent("enable", this);
20623         return this;
20624     }
20625
20626 });
20627
20628 Roo.apply(Roo.bootstrap.CheckBox, {
20629     
20630     groups: {},
20631     
20632      /**
20633     * register a CheckBox Group
20634     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20635     */
20636     register : function(checkbox)
20637     {
20638         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20639             this.groups[checkbox.groupId] = {};
20640         }
20641         
20642         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20643             return;
20644         }
20645         
20646         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20647         
20648     },
20649     /**
20650     * fetch a CheckBox Group based on the group ID
20651     * @param {string} the group ID
20652     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20653     */
20654     get: function(groupId) {
20655         if (typeof(this.groups[groupId]) == 'undefined') {
20656             return false;
20657         }
20658         
20659         return this.groups[groupId] ;
20660     }
20661     
20662     
20663 });
20664 /*
20665  * - LGPL
20666  *
20667  * RadioItem
20668  * 
20669  */
20670
20671 /**
20672  * @class Roo.bootstrap.Radio
20673  * @extends Roo.bootstrap.Component
20674  * Bootstrap Radio class
20675  * @cfg {String} boxLabel - the label associated
20676  * @cfg {String} value - the value of radio
20677  * 
20678  * @constructor
20679  * Create a new Radio
20680  * @param {Object} config The config object
20681  */
20682 Roo.bootstrap.Radio = function(config){
20683     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20684     
20685 };
20686
20687 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20688     
20689     boxLabel : '',
20690     
20691     value : '',
20692     
20693     getAutoCreate : function()
20694     {
20695         var cfg = {
20696             tag : 'div',
20697             cls : 'form-group radio',
20698             cn : [
20699                 {
20700                     tag : 'label',
20701                     cls : 'box-label',
20702                     html : this.boxLabel
20703                 }
20704             ]
20705         };
20706         
20707         return cfg;
20708     },
20709     
20710     initEvents : function() 
20711     {
20712         this.parent().register(this);
20713         
20714         this.el.on('click', this.onClick, this);
20715         
20716     },
20717     
20718     onClick : function()
20719     {
20720         this.setChecked(true);
20721     },
20722     
20723     setChecked : function(state, suppressEvent)
20724     {
20725         this.parent().setValue(this.value, suppressEvent);
20726         
20727     }
20728     
20729 });
20730  
20731
20732  /*
20733  * - LGPL
20734  *
20735  * Input
20736  * 
20737  */
20738
20739 /**
20740  * @class Roo.bootstrap.SecurePass
20741  * @extends Roo.bootstrap.Input
20742  * Bootstrap SecurePass class
20743  *
20744  * 
20745  * @constructor
20746  * Create a new SecurePass
20747  * @param {Object} config The config object
20748  */
20749  
20750 Roo.bootstrap.SecurePass = function (config) {
20751     // these go here, so the translation tool can replace them..
20752     this.errors = {
20753         PwdEmpty: "Please type a password, and then retype it to confirm.",
20754         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20755         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20756         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20757         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20758         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20759         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20760         TooWeak: "Your password is Too Weak."
20761     },
20762     this.meterLabel = "Password strength:";
20763     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20764     this.meterClass = [
20765         "roo-password-meter-tooweak", 
20766         "roo-password-meter-weak", 
20767         "roo-password-meter-medium", 
20768         "roo-password-meter-strong", 
20769         "roo-password-meter-grey"
20770     ];
20771     
20772     this.errors = {};
20773     
20774     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20775 }
20776
20777 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20778     /**
20779      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20780      * {
20781      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20782      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20783      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20784      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20785      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20786      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20787      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20788      * })
20789      */
20790     // private
20791     
20792     meterWidth: 300,
20793     errorMsg :'',    
20794     errors: false,
20795     imageRoot: '/',
20796     /**
20797      * @cfg {String/Object} Label for the strength meter (defaults to
20798      * 'Password strength:')
20799      */
20800     // private
20801     meterLabel: '',
20802     /**
20803      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20804      * ['Weak', 'Medium', 'Strong'])
20805      */
20806     // private    
20807     pwdStrengths: false,    
20808     // private
20809     strength: 0,
20810     // private
20811     _lastPwd: null,
20812     // private
20813     kCapitalLetter: 0,
20814     kSmallLetter: 1,
20815     kDigit: 2,
20816     kPunctuation: 3,
20817     
20818     insecure: false,
20819     // private
20820     initEvents: function ()
20821     {
20822         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20823
20824         if (this.el.is('input[type=password]') && Roo.isSafari) {
20825             this.el.on('keydown', this.SafariOnKeyDown, this);
20826         }
20827
20828         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20829     },
20830     // private
20831     onRender: function (ct, position)
20832     {
20833         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20834         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20835         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20836
20837         this.trigger.createChild({
20838                    cn: [
20839                     {
20840                     //id: 'PwdMeter',
20841                     tag: 'div',
20842                     cls: 'roo-password-meter-grey col-xs-12',
20843                     style: {
20844                         //width: 0,
20845                         //width: this.meterWidth + 'px'                                                
20846                         }
20847                     },
20848                     {                            
20849                          cls: 'roo-password-meter-text'                          
20850                     }
20851                 ]            
20852         });
20853
20854          
20855         if (this.hideTrigger) {
20856             this.trigger.setDisplayed(false);
20857         }
20858         this.setSize(this.width || '', this.height || '');
20859     },
20860     // private
20861     onDestroy: function ()
20862     {
20863         if (this.trigger) {
20864             this.trigger.removeAllListeners();
20865             this.trigger.remove();
20866         }
20867         if (this.wrap) {
20868             this.wrap.remove();
20869         }
20870         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20871     },
20872     // private
20873     checkStrength: function ()
20874     {
20875         var pwd = this.inputEl().getValue();
20876         if (pwd == this._lastPwd) {
20877             return;
20878         }
20879
20880         var strength;
20881         if (this.ClientSideStrongPassword(pwd)) {
20882             strength = 3;
20883         } else if (this.ClientSideMediumPassword(pwd)) {
20884             strength = 2;
20885         } else if (this.ClientSideWeakPassword(pwd)) {
20886             strength = 1;
20887         } else {
20888             strength = 0;
20889         }
20890         
20891         Roo.log('strength1: ' + strength);
20892         
20893         //var pm = this.trigger.child('div/div/div').dom;
20894         var pm = this.trigger.child('div/div');
20895         pm.removeClass(this.meterClass);
20896         pm.addClass(this.meterClass[strength]);
20897                 
20898         
20899         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20900                 
20901         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20902         
20903         this._lastPwd = pwd;
20904     },
20905     reset: function ()
20906     {
20907         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20908         
20909         this._lastPwd = '';
20910         
20911         var pm = this.trigger.child('div/div');
20912         pm.removeClass(this.meterClass);
20913         pm.addClass('roo-password-meter-grey');        
20914         
20915         
20916         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20917         
20918         pt.innerHTML = '';
20919         this.inputEl().dom.type='password';
20920     },
20921     // private
20922     validateValue: function (value)
20923     {
20924         
20925         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20926             return false;
20927         }
20928         if (value.length == 0) {
20929             if (this.allowBlank) {
20930                 this.clearInvalid();
20931                 return true;
20932             }
20933
20934             this.markInvalid(this.errors.PwdEmpty);
20935             this.errorMsg = this.errors.PwdEmpty;
20936             return false;
20937         }
20938         
20939         if(this.insecure){
20940             return true;
20941         }
20942         
20943         if ('[\x21-\x7e]*'.match(value)) {
20944             this.markInvalid(this.errors.PwdBadChar);
20945             this.errorMsg = this.errors.PwdBadChar;
20946             return false;
20947         }
20948         if (value.length < 6) {
20949             this.markInvalid(this.errors.PwdShort);
20950             this.errorMsg = this.errors.PwdShort;
20951             return false;
20952         }
20953         if (value.length > 16) {
20954             this.markInvalid(this.errors.PwdLong);
20955             this.errorMsg = this.errors.PwdLong;
20956             return false;
20957         }
20958         var strength;
20959         if (this.ClientSideStrongPassword(value)) {
20960             strength = 3;
20961         } else if (this.ClientSideMediumPassword(value)) {
20962             strength = 2;
20963         } else if (this.ClientSideWeakPassword(value)) {
20964             strength = 1;
20965         } else {
20966             strength = 0;
20967         }
20968
20969         
20970         if (strength < 2) {
20971             //this.markInvalid(this.errors.TooWeak);
20972             this.errorMsg = this.errors.TooWeak;
20973             //return false;
20974         }
20975         
20976         
20977         console.log('strength2: ' + strength);
20978         
20979         //var pm = this.trigger.child('div/div/div').dom;
20980         
20981         var pm = this.trigger.child('div/div');
20982         pm.removeClass(this.meterClass);
20983         pm.addClass(this.meterClass[strength]);
20984                 
20985         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20986                 
20987         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20988         
20989         this.errorMsg = ''; 
20990         return true;
20991     },
20992     // private
20993     CharacterSetChecks: function (type)
20994     {
20995         this.type = type;
20996         this.fResult = false;
20997     },
20998     // private
20999     isctype: function (character, type)
21000     {
21001         switch (type) {  
21002             case this.kCapitalLetter:
21003                 if (character >= 'A' && character <= 'Z') {
21004                     return true;
21005                 }
21006                 break;
21007             
21008             case this.kSmallLetter:
21009                 if (character >= 'a' && character <= 'z') {
21010                     return true;
21011                 }
21012                 break;
21013             
21014             case this.kDigit:
21015                 if (character >= '0' && character <= '9') {
21016                     return true;
21017                 }
21018                 break;
21019             
21020             case this.kPunctuation:
21021                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21022                     return true;
21023                 }
21024                 break;
21025             
21026             default:
21027                 return false;
21028         }
21029
21030     },
21031     // private
21032     IsLongEnough: function (pwd, size)
21033     {
21034         return !(pwd == null || isNaN(size) || pwd.length < size);
21035     },
21036     // private
21037     SpansEnoughCharacterSets: function (word, nb)
21038     {
21039         if (!this.IsLongEnough(word, nb))
21040         {
21041             return false;
21042         }
21043
21044         var characterSetChecks = new Array(
21045             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21046             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21047         );
21048         
21049         for (var index = 0; index < word.length; ++index) {
21050             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21051                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21052                     characterSetChecks[nCharSet].fResult = true;
21053                     break;
21054                 }
21055             }
21056         }
21057
21058         var nCharSets = 0;
21059         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21060             if (characterSetChecks[nCharSet].fResult) {
21061                 ++nCharSets;
21062             }
21063         }
21064
21065         if (nCharSets < nb) {
21066             return false;
21067         }
21068         return true;
21069     },
21070     // private
21071     ClientSideStrongPassword: function (pwd)
21072     {
21073         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21074     },
21075     // private
21076     ClientSideMediumPassword: function (pwd)
21077     {
21078         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21079     },
21080     // private
21081     ClientSideWeakPassword: function (pwd)
21082     {
21083         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21084     }
21085           
21086 })//<script type="text/javascript">
21087
21088 /*
21089  * Based  Ext JS Library 1.1.1
21090  * Copyright(c) 2006-2007, Ext JS, LLC.
21091  * LGPL
21092  *
21093  */
21094  
21095 /**
21096  * @class Roo.HtmlEditorCore
21097  * @extends Roo.Component
21098  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21099  *
21100  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21101  */
21102
21103 Roo.HtmlEditorCore = function(config){
21104     
21105     
21106     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21107     
21108     
21109     this.addEvents({
21110         /**
21111          * @event initialize
21112          * Fires when the editor is fully initialized (including the iframe)
21113          * @param {Roo.HtmlEditorCore} this
21114          */
21115         initialize: true,
21116         /**
21117          * @event activate
21118          * Fires when the editor is first receives the focus. Any insertion must wait
21119          * until after this event.
21120          * @param {Roo.HtmlEditorCore} this
21121          */
21122         activate: true,
21123          /**
21124          * @event beforesync
21125          * Fires before the textarea is updated with content from the editor iframe. Return false
21126          * to cancel the sync.
21127          * @param {Roo.HtmlEditorCore} this
21128          * @param {String} html
21129          */
21130         beforesync: true,
21131          /**
21132          * @event beforepush
21133          * Fires before the iframe editor is updated with content from the textarea. Return false
21134          * to cancel the push.
21135          * @param {Roo.HtmlEditorCore} this
21136          * @param {String} html
21137          */
21138         beforepush: true,
21139          /**
21140          * @event sync
21141          * Fires when the textarea is updated with content from the editor iframe.
21142          * @param {Roo.HtmlEditorCore} this
21143          * @param {String} html
21144          */
21145         sync: true,
21146          /**
21147          * @event push
21148          * Fires when the iframe editor is updated with content from the textarea.
21149          * @param {Roo.HtmlEditorCore} this
21150          * @param {String} html
21151          */
21152         push: true,
21153         
21154         /**
21155          * @event editorevent
21156          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21157          * @param {Roo.HtmlEditorCore} this
21158          */
21159         editorevent: true
21160         
21161     });
21162     
21163     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21164     
21165     // defaults : white / black...
21166     this.applyBlacklists();
21167     
21168     
21169     
21170 };
21171
21172
21173 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21174
21175
21176      /**
21177      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21178      */
21179     
21180     owner : false,
21181     
21182      /**
21183      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21184      *                        Roo.resizable.
21185      */
21186     resizable : false,
21187      /**
21188      * @cfg {Number} height (in pixels)
21189      */   
21190     height: 300,
21191    /**
21192      * @cfg {Number} width (in pixels)
21193      */   
21194     width: 500,
21195     
21196     /**
21197      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21198      * 
21199      */
21200     stylesheets: false,
21201     
21202     // id of frame..
21203     frameId: false,
21204     
21205     // private properties
21206     validationEvent : false,
21207     deferHeight: true,
21208     initialized : false,
21209     activated : false,
21210     sourceEditMode : false,
21211     onFocus : Roo.emptyFn,
21212     iframePad:3,
21213     hideMode:'offsets',
21214     
21215     clearUp: true,
21216     
21217     // blacklist + whitelisted elements..
21218     black: false,
21219     white: false,
21220      
21221     
21222
21223     /**
21224      * Protected method that will not generally be called directly. It
21225      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21226      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21227      */
21228     getDocMarkup : function(){
21229         // body styles..
21230         var st = '';
21231         
21232         // inherit styels from page...?? 
21233         if (this.stylesheets === false) {
21234             
21235             Roo.get(document.head).select('style').each(function(node) {
21236                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21237             });
21238             
21239             Roo.get(document.head).select('link').each(function(node) { 
21240                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21241             });
21242             
21243         } else if (!this.stylesheets.length) {
21244                 // simple..
21245                 st = '<style type="text/css">' +
21246                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21247                    '</style>';
21248         } else { 
21249             
21250         }
21251         
21252         st +=  '<style type="text/css">' +
21253             'IMG { cursor: pointer } ' +
21254         '</style>';
21255
21256         
21257         return '<html><head>' + st  +
21258             //<style type="text/css">' +
21259             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21260             //'</style>' +
21261             ' </head><body class="roo-htmleditor-body"></body></html>';
21262     },
21263
21264     // private
21265     onRender : function(ct, position)
21266     {
21267         var _t = this;
21268         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21269         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21270         
21271         
21272         this.el.dom.style.border = '0 none';
21273         this.el.dom.setAttribute('tabIndex', -1);
21274         this.el.addClass('x-hidden hide');
21275         
21276         
21277         
21278         if(Roo.isIE){ // fix IE 1px bogus margin
21279             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21280         }
21281        
21282         
21283         this.frameId = Roo.id();
21284         
21285          
21286         
21287         var iframe = this.owner.wrap.createChild({
21288             tag: 'iframe',
21289             cls: 'form-control', // bootstrap..
21290             id: this.frameId,
21291             name: this.frameId,
21292             frameBorder : 'no',
21293             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21294         }, this.el
21295         );
21296         
21297         
21298         this.iframe = iframe.dom;
21299
21300          this.assignDocWin();
21301         
21302         this.doc.designMode = 'on';
21303        
21304         this.doc.open();
21305         this.doc.write(this.getDocMarkup());
21306         this.doc.close();
21307
21308         
21309         var task = { // must defer to wait for browser to be ready
21310             run : function(){
21311                 //console.log("run task?" + this.doc.readyState);
21312                 this.assignDocWin();
21313                 if(this.doc.body || this.doc.readyState == 'complete'){
21314                     try {
21315                         this.doc.designMode="on";
21316                     } catch (e) {
21317                         return;
21318                     }
21319                     Roo.TaskMgr.stop(task);
21320                     this.initEditor.defer(10, this);
21321                 }
21322             },
21323             interval : 10,
21324             duration: 10000,
21325             scope: this
21326         };
21327         Roo.TaskMgr.start(task);
21328
21329     },
21330
21331     // private
21332     onResize : function(w, h)
21333     {
21334          Roo.log('resize: ' +w + ',' + h );
21335         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21336         if(!this.iframe){
21337             return;
21338         }
21339         if(typeof w == 'number'){
21340             
21341             this.iframe.style.width = w + 'px';
21342         }
21343         if(typeof h == 'number'){
21344             
21345             this.iframe.style.height = h + 'px';
21346             if(this.doc){
21347                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21348             }
21349         }
21350         
21351     },
21352
21353     /**
21354      * Toggles the editor between standard and source edit mode.
21355      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21356      */
21357     toggleSourceEdit : function(sourceEditMode){
21358         
21359         this.sourceEditMode = sourceEditMode === true;
21360         
21361         if(this.sourceEditMode){
21362  
21363             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21364             
21365         }else{
21366             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21367             //this.iframe.className = '';
21368             this.deferFocus();
21369         }
21370         //this.setSize(this.owner.wrap.getSize());
21371         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21372     },
21373
21374     
21375   
21376
21377     /**
21378      * Protected method that will not generally be called directly. If you need/want
21379      * custom HTML cleanup, this is the method you should override.
21380      * @param {String} html The HTML to be cleaned
21381      * return {String} The cleaned HTML
21382      */
21383     cleanHtml : function(html){
21384         html = String(html);
21385         if(html.length > 5){
21386             if(Roo.isSafari){ // strip safari nonsense
21387                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21388             }
21389         }
21390         if(html == '&nbsp;'){
21391             html = '';
21392         }
21393         return html;
21394     },
21395
21396     /**
21397      * HTML Editor -> Textarea
21398      * Protected method that will not generally be called directly. Syncs the contents
21399      * of the editor iframe with the textarea.
21400      */
21401     syncValue : function(){
21402         if(this.initialized){
21403             var bd = (this.doc.body || this.doc.documentElement);
21404             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21405             var html = bd.innerHTML;
21406             if(Roo.isSafari){
21407                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21408                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21409                 if(m && m[1]){
21410                     html = '<div style="'+m[0]+'">' + html + '</div>';
21411                 }
21412             }
21413             html = this.cleanHtml(html);
21414             // fix up the special chars.. normaly like back quotes in word...
21415             // however we do not want to do this with chinese..
21416             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21417                 var cc = b.charCodeAt();
21418                 if (
21419                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21420                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21421                     (cc >= 0xf900 && cc < 0xfb00 )
21422                 ) {
21423                         return b;
21424                 }
21425                 return "&#"+cc+";" 
21426             });
21427             if(this.owner.fireEvent('beforesync', this, html) !== false){
21428                 this.el.dom.value = html;
21429                 this.owner.fireEvent('sync', this, html);
21430             }
21431         }
21432     },
21433
21434     /**
21435      * Protected method that will not generally be called directly. Pushes the value of the textarea
21436      * into the iframe editor.
21437      */
21438     pushValue : function(){
21439         if(this.initialized){
21440             var v = this.el.dom.value.trim();
21441             
21442 //            if(v.length < 1){
21443 //                v = '&#160;';
21444 //            }
21445             
21446             if(this.owner.fireEvent('beforepush', this, v) !== false){
21447                 var d = (this.doc.body || this.doc.documentElement);
21448                 d.innerHTML = v;
21449                 this.cleanUpPaste();
21450                 this.el.dom.value = d.innerHTML;
21451                 this.owner.fireEvent('push', this, v);
21452             }
21453         }
21454     },
21455
21456     // private
21457     deferFocus : function(){
21458         this.focus.defer(10, this);
21459     },
21460
21461     // doc'ed in Field
21462     focus : function(){
21463         if(this.win && !this.sourceEditMode){
21464             this.win.focus();
21465         }else{
21466             this.el.focus();
21467         }
21468     },
21469     
21470     assignDocWin: function()
21471     {
21472         var iframe = this.iframe;
21473         
21474          if(Roo.isIE){
21475             this.doc = iframe.contentWindow.document;
21476             this.win = iframe.contentWindow;
21477         } else {
21478 //            if (!Roo.get(this.frameId)) {
21479 //                return;
21480 //            }
21481 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21482 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21483             
21484             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21485                 return;
21486             }
21487             
21488             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21489             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21490         }
21491     },
21492     
21493     // private
21494     initEditor : function(){
21495         //console.log("INIT EDITOR");
21496         this.assignDocWin();
21497         
21498         
21499         
21500         this.doc.designMode="on";
21501         this.doc.open();
21502         this.doc.write(this.getDocMarkup());
21503         this.doc.close();
21504         
21505         var dbody = (this.doc.body || this.doc.documentElement);
21506         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21507         // this copies styles from the containing element into thsi one..
21508         // not sure why we need all of this..
21509         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21510         
21511         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21512         //ss['background-attachment'] = 'fixed'; // w3c
21513         dbody.bgProperties = 'fixed'; // ie
21514         //Roo.DomHelper.applyStyles(dbody, ss);
21515         Roo.EventManager.on(this.doc, {
21516             //'mousedown': this.onEditorEvent,
21517             'mouseup': this.onEditorEvent,
21518             'dblclick': this.onEditorEvent,
21519             'click': this.onEditorEvent,
21520             'keyup': this.onEditorEvent,
21521             buffer:100,
21522             scope: this
21523         });
21524         if(Roo.isGecko){
21525             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21526         }
21527         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21528             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21529         }
21530         this.initialized = true;
21531
21532         this.owner.fireEvent('initialize', this);
21533         this.pushValue();
21534     },
21535
21536     // private
21537     onDestroy : function(){
21538         
21539         
21540         
21541         if(this.rendered){
21542             
21543             //for (var i =0; i < this.toolbars.length;i++) {
21544             //    // fixme - ask toolbars for heights?
21545             //    this.toolbars[i].onDestroy();
21546            // }
21547             
21548             //this.wrap.dom.innerHTML = '';
21549             //this.wrap.remove();
21550         }
21551     },
21552
21553     // private
21554     onFirstFocus : function(){
21555         
21556         this.assignDocWin();
21557         
21558         
21559         this.activated = true;
21560          
21561     
21562         if(Roo.isGecko){ // prevent silly gecko errors
21563             this.win.focus();
21564             var s = this.win.getSelection();
21565             if(!s.focusNode || s.focusNode.nodeType != 3){
21566                 var r = s.getRangeAt(0);
21567                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21568                 r.collapse(true);
21569                 this.deferFocus();
21570             }
21571             try{
21572                 this.execCmd('useCSS', true);
21573                 this.execCmd('styleWithCSS', false);
21574             }catch(e){}
21575         }
21576         this.owner.fireEvent('activate', this);
21577     },
21578
21579     // private
21580     adjustFont: function(btn){
21581         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21582         //if(Roo.isSafari){ // safari
21583         //    adjust *= 2;
21584        // }
21585         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21586         if(Roo.isSafari){ // safari
21587             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21588             v =  (v < 10) ? 10 : v;
21589             v =  (v > 48) ? 48 : v;
21590             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21591             
21592         }
21593         
21594         
21595         v = Math.max(1, v+adjust);
21596         
21597         this.execCmd('FontSize', v  );
21598     },
21599
21600     onEditorEvent : function(e)
21601     {
21602         this.owner.fireEvent('editorevent', this, e);
21603       //  this.updateToolbar();
21604         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21605     },
21606
21607     insertTag : function(tg)
21608     {
21609         // could be a bit smarter... -> wrap the current selected tRoo..
21610         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21611             
21612             range = this.createRange(this.getSelection());
21613             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21614             wrappingNode.appendChild(range.extractContents());
21615             range.insertNode(wrappingNode);
21616
21617             return;
21618             
21619             
21620             
21621         }
21622         this.execCmd("formatblock",   tg);
21623         
21624     },
21625     
21626     insertText : function(txt)
21627     {
21628         
21629         
21630         var range = this.createRange();
21631         range.deleteContents();
21632                //alert(Sender.getAttribute('label'));
21633                
21634         range.insertNode(this.doc.createTextNode(txt));
21635     } ,
21636     
21637      
21638
21639     /**
21640      * Executes a Midas editor command on the editor document and performs necessary focus and
21641      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21642      * @param {String} cmd The Midas command
21643      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21644      */
21645     relayCmd : function(cmd, value){
21646         this.win.focus();
21647         this.execCmd(cmd, value);
21648         this.owner.fireEvent('editorevent', this);
21649         //this.updateToolbar();
21650         this.owner.deferFocus();
21651     },
21652
21653     /**
21654      * Executes a Midas editor command directly on the editor document.
21655      * For visual commands, you should use {@link #relayCmd} instead.
21656      * <b>This should only be called after the editor is initialized.</b>
21657      * @param {String} cmd The Midas command
21658      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21659      */
21660     execCmd : function(cmd, value){
21661         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21662         this.syncValue();
21663     },
21664  
21665  
21666    
21667     /**
21668      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21669      * to insert tRoo.
21670      * @param {String} text | dom node.. 
21671      */
21672     insertAtCursor : function(text)
21673     {
21674         
21675         if(!this.activated){
21676             return;
21677         }
21678         /*
21679         if(Roo.isIE){
21680             this.win.focus();
21681             var r = this.doc.selection.createRange();
21682             if(r){
21683                 r.collapse(true);
21684                 r.pasteHTML(text);
21685                 this.syncValue();
21686                 this.deferFocus();
21687             
21688             }
21689             return;
21690         }
21691         */
21692         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21693             this.win.focus();
21694             
21695             
21696             // from jquery ui (MIT licenced)
21697             var range, node;
21698             var win = this.win;
21699             
21700             if (win.getSelection && win.getSelection().getRangeAt) {
21701                 range = win.getSelection().getRangeAt(0);
21702                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21703                 range.insertNode(node);
21704             } else if (win.document.selection && win.document.selection.createRange) {
21705                 // no firefox support
21706                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21707                 win.document.selection.createRange().pasteHTML(txt);
21708             } else {
21709                 // no firefox support
21710                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21711                 this.execCmd('InsertHTML', txt);
21712             } 
21713             
21714             this.syncValue();
21715             
21716             this.deferFocus();
21717         }
21718     },
21719  // private
21720     mozKeyPress : function(e){
21721         if(e.ctrlKey){
21722             var c = e.getCharCode(), cmd;
21723           
21724             if(c > 0){
21725                 c = String.fromCharCode(c).toLowerCase();
21726                 switch(c){
21727                     case 'b':
21728                         cmd = 'bold';
21729                         break;
21730                     case 'i':
21731                         cmd = 'italic';
21732                         break;
21733                     
21734                     case 'u':
21735                         cmd = 'underline';
21736                         break;
21737                     
21738                     case 'v':
21739                         this.cleanUpPaste.defer(100, this);
21740                         return;
21741                         
21742                 }
21743                 if(cmd){
21744                     this.win.focus();
21745                     this.execCmd(cmd);
21746                     this.deferFocus();
21747                     e.preventDefault();
21748                 }
21749                 
21750             }
21751         }
21752     },
21753
21754     // private
21755     fixKeys : function(){ // load time branching for fastest keydown performance
21756         if(Roo.isIE){
21757             return function(e){
21758                 var k = e.getKey(), r;
21759                 if(k == e.TAB){
21760                     e.stopEvent();
21761                     r = this.doc.selection.createRange();
21762                     if(r){
21763                         r.collapse(true);
21764                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21765                         this.deferFocus();
21766                     }
21767                     return;
21768                 }
21769                 
21770                 if(k == e.ENTER){
21771                     r = this.doc.selection.createRange();
21772                     if(r){
21773                         var target = r.parentElement();
21774                         if(!target || target.tagName.toLowerCase() != 'li'){
21775                             e.stopEvent();
21776                             r.pasteHTML('<br />');
21777                             r.collapse(false);
21778                             r.select();
21779                         }
21780                     }
21781                 }
21782                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21783                     this.cleanUpPaste.defer(100, this);
21784                     return;
21785                 }
21786                 
21787                 
21788             };
21789         }else if(Roo.isOpera){
21790             return function(e){
21791                 var k = e.getKey();
21792                 if(k == e.TAB){
21793                     e.stopEvent();
21794                     this.win.focus();
21795                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21796                     this.deferFocus();
21797                 }
21798                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21799                     this.cleanUpPaste.defer(100, this);
21800                     return;
21801                 }
21802                 
21803             };
21804         }else if(Roo.isSafari){
21805             return function(e){
21806                 var k = e.getKey();
21807                 
21808                 if(k == e.TAB){
21809                     e.stopEvent();
21810                     this.execCmd('InsertText','\t');
21811                     this.deferFocus();
21812                     return;
21813                 }
21814                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21815                     this.cleanUpPaste.defer(100, this);
21816                     return;
21817                 }
21818                 
21819              };
21820         }
21821     }(),
21822     
21823     getAllAncestors: function()
21824     {
21825         var p = this.getSelectedNode();
21826         var a = [];
21827         if (!p) {
21828             a.push(p); // push blank onto stack..
21829             p = this.getParentElement();
21830         }
21831         
21832         
21833         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21834             a.push(p);
21835             p = p.parentNode;
21836         }
21837         a.push(this.doc.body);
21838         return a;
21839     },
21840     lastSel : false,
21841     lastSelNode : false,
21842     
21843     
21844     getSelection : function() 
21845     {
21846         this.assignDocWin();
21847         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21848     },
21849     
21850     getSelectedNode: function() 
21851     {
21852         // this may only work on Gecko!!!
21853         
21854         // should we cache this!!!!
21855         
21856         
21857         
21858          
21859         var range = this.createRange(this.getSelection()).cloneRange();
21860         
21861         if (Roo.isIE) {
21862             var parent = range.parentElement();
21863             while (true) {
21864                 var testRange = range.duplicate();
21865                 testRange.moveToElementText(parent);
21866                 if (testRange.inRange(range)) {
21867                     break;
21868                 }
21869                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21870                     break;
21871                 }
21872                 parent = parent.parentElement;
21873             }
21874             return parent;
21875         }
21876         
21877         // is ancestor a text element.
21878         var ac =  range.commonAncestorContainer;
21879         if (ac.nodeType == 3) {
21880             ac = ac.parentNode;
21881         }
21882         
21883         var ar = ac.childNodes;
21884          
21885         var nodes = [];
21886         var other_nodes = [];
21887         var has_other_nodes = false;
21888         for (var i=0;i<ar.length;i++) {
21889             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21890                 continue;
21891             }
21892             // fullly contained node.
21893             
21894             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21895                 nodes.push(ar[i]);
21896                 continue;
21897             }
21898             
21899             // probably selected..
21900             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21901                 other_nodes.push(ar[i]);
21902                 continue;
21903             }
21904             // outer..
21905             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21906                 continue;
21907             }
21908             
21909             
21910             has_other_nodes = true;
21911         }
21912         if (!nodes.length && other_nodes.length) {
21913             nodes= other_nodes;
21914         }
21915         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21916             return false;
21917         }
21918         
21919         return nodes[0];
21920     },
21921     createRange: function(sel)
21922     {
21923         // this has strange effects when using with 
21924         // top toolbar - not sure if it's a great idea.
21925         //this.editor.contentWindow.focus();
21926         if (typeof sel != "undefined") {
21927             try {
21928                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21929             } catch(e) {
21930                 return this.doc.createRange();
21931             }
21932         } else {
21933             return this.doc.createRange();
21934         }
21935     },
21936     getParentElement: function()
21937     {
21938         
21939         this.assignDocWin();
21940         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21941         
21942         var range = this.createRange(sel);
21943          
21944         try {
21945             var p = range.commonAncestorContainer;
21946             while (p.nodeType == 3) { // text node
21947                 p = p.parentNode;
21948             }
21949             return p;
21950         } catch (e) {
21951             return null;
21952         }
21953     
21954     },
21955     /***
21956      *
21957      * Range intersection.. the hard stuff...
21958      *  '-1' = before
21959      *  '0' = hits..
21960      *  '1' = after.
21961      *         [ -- selected range --- ]
21962      *   [fail]                        [fail]
21963      *
21964      *    basically..
21965      *      if end is before start or  hits it. fail.
21966      *      if start is after end or hits it fail.
21967      *
21968      *   if either hits (but other is outside. - then it's not 
21969      *   
21970      *    
21971      **/
21972     
21973     
21974     // @see http://www.thismuchiknow.co.uk/?p=64.
21975     rangeIntersectsNode : function(range, node)
21976     {
21977         var nodeRange = node.ownerDocument.createRange();
21978         try {
21979             nodeRange.selectNode(node);
21980         } catch (e) {
21981             nodeRange.selectNodeContents(node);
21982         }
21983     
21984         var rangeStartRange = range.cloneRange();
21985         rangeStartRange.collapse(true);
21986     
21987         var rangeEndRange = range.cloneRange();
21988         rangeEndRange.collapse(false);
21989     
21990         var nodeStartRange = nodeRange.cloneRange();
21991         nodeStartRange.collapse(true);
21992     
21993         var nodeEndRange = nodeRange.cloneRange();
21994         nodeEndRange.collapse(false);
21995     
21996         return rangeStartRange.compareBoundaryPoints(
21997                  Range.START_TO_START, nodeEndRange) == -1 &&
21998                rangeEndRange.compareBoundaryPoints(
21999                  Range.START_TO_START, nodeStartRange) == 1;
22000         
22001          
22002     },
22003     rangeCompareNode : function(range, node)
22004     {
22005         var nodeRange = node.ownerDocument.createRange();
22006         try {
22007             nodeRange.selectNode(node);
22008         } catch (e) {
22009             nodeRange.selectNodeContents(node);
22010         }
22011         
22012         
22013         range.collapse(true);
22014     
22015         nodeRange.collapse(true);
22016      
22017         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22018         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22019          
22020         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22021         
22022         var nodeIsBefore   =  ss == 1;
22023         var nodeIsAfter    = ee == -1;
22024         
22025         if (nodeIsBefore && nodeIsAfter) {
22026             return 0; // outer
22027         }
22028         if (!nodeIsBefore && nodeIsAfter) {
22029             return 1; //right trailed.
22030         }
22031         
22032         if (nodeIsBefore && !nodeIsAfter) {
22033             return 2;  // left trailed.
22034         }
22035         // fully contined.
22036         return 3;
22037     },
22038
22039     // private? - in a new class?
22040     cleanUpPaste :  function()
22041     {
22042         // cleans up the whole document..
22043         Roo.log('cleanuppaste');
22044         
22045         this.cleanUpChildren(this.doc.body);
22046         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22047         if (clean != this.doc.body.innerHTML) {
22048             this.doc.body.innerHTML = clean;
22049         }
22050         
22051     },
22052     
22053     cleanWordChars : function(input) {// change the chars to hex code
22054         var he = Roo.HtmlEditorCore;
22055         
22056         var output = input;
22057         Roo.each(he.swapCodes, function(sw) { 
22058             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22059             
22060             output = output.replace(swapper, sw[1]);
22061         });
22062         
22063         return output;
22064     },
22065     
22066     
22067     cleanUpChildren : function (n)
22068     {
22069         if (!n.childNodes.length) {
22070             return;
22071         }
22072         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22073            this.cleanUpChild(n.childNodes[i]);
22074         }
22075     },
22076     
22077     
22078         
22079     
22080     cleanUpChild : function (node)
22081     {
22082         var ed = this;
22083         //console.log(node);
22084         if (node.nodeName == "#text") {
22085             // clean up silly Windows -- stuff?
22086             return; 
22087         }
22088         if (node.nodeName == "#comment") {
22089             node.parentNode.removeChild(node);
22090             // clean up silly Windows -- stuff?
22091             return; 
22092         }
22093         var lcname = node.tagName.toLowerCase();
22094         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22095         // whitelist of tags..
22096         
22097         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22098             // remove node.
22099             node.parentNode.removeChild(node);
22100             return;
22101             
22102         }
22103         
22104         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22105         
22106         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22107         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22108         
22109         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22110         //    remove_keep_children = true;
22111         //}
22112         
22113         if (remove_keep_children) {
22114             this.cleanUpChildren(node);
22115             // inserts everything just before this node...
22116             while (node.childNodes.length) {
22117                 var cn = node.childNodes[0];
22118                 node.removeChild(cn);
22119                 node.parentNode.insertBefore(cn, node);
22120             }
22121             node.parentNode.removeChild(node);
22122             return;
22123         }
22124         
22125         if (!node.attributes || !node.attributes.length) {
22126             this.cleanUpChildren(node);
22127             return;
22128         }
22129         
22130         function cleanAttr(n,v)
22131         {
22132             
22133             if (v.match(/^\./) || v.match(/^\//)) {
22134                 return;
22135             }
22136             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22137                 return;
22138             }
22139             if (v.match(/^#/)) {
22140                 return;
22141             }
22142 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22143             node.removeAttribute(n);
22144             
22145         }
22146         
22147         var cwhite = this.cwhite;
22148         var cblack = this.cblack;
22149             
22150         function cleanStyle(n,v)
22151         {
22152             if (v.match(/expression/)) { //XSS?? should we even bother..
22153                 node.removeAttribute(n);
22154                 return;
22155             }
22156             
22157             var parts = v.split(/;/);
22158             var clean = [];
22159             
22160             Roo.each(parts, function(p) {
22161                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22162                 if (!p.length) {
22163                     return true;
22164                 }
22165                 var l = p.split(':').shift().replace(/\s+/g,'');
22166                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22167                 
22168                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22169 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22170                     //node.removeAttribute(n);
22171                     return true;
22172                 }
22173                 //Roo.log()
22174                 // only allow 'c whitelisted system attributes'
22175                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22176 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22177                     //node.removeAttribute(n);
22178                     return true;
22179                 }
22180                 
22181                 
22182                  
22183                 
22184                 clean.push(p);
22185                 return true;
22186             });
22187             if (clean.length) { 
22188                 node.setAttribute(n, clean.join(';'));
22189             } else {
22190                 node.removeAttribute(n);
22191             }
22192             
22193         }
22194         
22195         
22196         for (var i = node.attributes.length-1; i > -1 ; i--) {
22197             var a = node.attributes[i];
22198             //console.log(a);
22199             
22200             if (a.name.toLowerCase().substr(0,2)=='on')  {
22201                 node.removeAttribute(a.name);
22202                 continue;
22203             }
22204             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22205                 node.removeAttribute(a.name);
22206                 continue;
22207             }
22208             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22209                 cleanAttr(a.name,a.value); // fixme..
22210                 continue;
22211             }
22212             if (a.name == 'style') {
22213                 cleanStyle(a.name,a.value);
22214                 continue;
22215             }
22216             /// clean up MS crap..
22217             // tecnically this should be a list of valid class'es..
22218             
22219             
22220             if (a.name == 'class') {
22221                 if (a.value.match(/^Mso/)) {
22222                     node.className = '';
22223                 }
22224                 
22225                 if (a.value.match(/^body$/)) {
22226                     node.className = '';
22227                 }
22228                 continue;
22229             }
22230             
22231             // style cleanup!?
22232             // class cleanup?
22233             
22234         }
22235         
22236         
22237         this.cleanUpChildren(node);
22238         
22239         
22240     },
22241     
22242     /**
22243      * Clean up MS wordisms...
22244      */
22245     cleanWord : function(node)
22246     {
22247         
22248         
22249         if (!node) {
22250             this.cleanWord(this.doc.body);
22251             return;
22252         }
22253         if (node.nodeName == "#text") {
22254             // clean up silly Windows -- stuff?
22255             return; 
22256         }
22257         if (node.nodeName == "#comment") {
22258             node.parentNode.removeChild(node);
22259             // clean up silly Windows -- stuff?
22260             return; 
22261         }
22262         
22263         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22264             node.parentNode.removeChild(node);
22265             return;
22266         }
22267         
22268         // remove - but keep children..
22269         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22270             while (node.childNodes.length) {
22271                 var cn = node.childNodes[0];
22272                 node.removeChild(cn);
22273                 node.parentNode.insertBefore(cn, node);
22274             }
22275             node.parentNode.removeChild(node);
22276             this.iterateChildren(node, this.cleanWord);
22277             return;
22278         }
22279         // clean styles
22280         if (node.className.length) {
22281             
22282             var cn = node.className.split(/\W+/);
22283             var cna = [];
22284             Roo.each(cn, function(cls) {
22285                 if (cls.match(/Mso[a-zA-Z]+/)) {
22286                     return;
22287                 }
22288                 cna.push(cls);
22289             });
22290             node.className = cna.length ? cna.join(' ') : '';
22291             if (!cna.length) {
22292                 node.removeAttribute("class");
22293             }
22294         }
22295         
22296         if (node.hasAttribute("lang")) {
22297             node.removeAttribute("lang");
22298         }
22299         
22300         if (node.hasAttribute("style")) {
22301             
22302             var styles = node.getAttribute("style").split(";");
22303             var nstyle = [];
22304             Roo.each(styles, function(s) {
22305                 if (!s.match(/:/)) {
22306                     return;
22307                 }
22308                 var kv = s.split(":");
22309                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22310                     return;
22311                 }
22312                 // what ever is left... we allow.
22313                 nstyle.push(s);
22314             });
22315             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22316             if (!nstyle.length) {
22317                 node.removeAttribute('style');
22318             }
22319         }
22320         this.iterateChildren(node, this.cleanWord);
22321         
22322         
22323         
22324     },
22325     /**
22326      * iterateChildren of a Node, calling fn each time, using this as the scole..
22327      * @param {DomNode} node node to iterate children of.
22328      * @param {Function} fn method of this class to call on each item.
22329      */
22330     iterateChildren : function(node, fn)
22331     {
22332         if (!node.childNodes.length) {
22333                 return;
22334         }
22335         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22336            fn.call(this, node.childNodes[i])
22337         }
22338     },
22339     
22340     
22341     /**
22342      * cleanTableWidths.
22343      *
22344      * Quite often pasting from word etc.. results in tables with column and widths.
22345      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22346      *
22347      */
22348     cleanTableWidths : function(node)
22349     {
22350          
22351          
22352         if (!node) {
22353             this.cleanTableWidths(this.doc.body);
22354             return;
22355         }
22356         
22357         // ignore list...
22358         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22359             return; 
22360         }
22361         Roo.log(node.tagName);
22362         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22363             this.iterateChildren(node, this.cleanTableWidths);
22364             return;
22365         }
22366         if (node.hasAttribute('width')) {
22367             node.removeAttribute('width');
22368         }
22369         
22370          
22371         if (node.hasAttribute("style")) {
22372             // pretty basic...
22373             
22374             var styles = node.getAttribute("style").split(";");
22375             var nstyle = [];
22376             Roo.each(styles, function(s) {
22377                 if (!s.match(/:/)) {
22378                     return;
22379                 }
22380                 var kv = s.split(":");
22381                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22382                     return;
22383                 }
22384                 // what ever is left... we allow.
22385                 nstyle.push(s);
22386             });
22387             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22388             if (!nstyle.length) {
22389                 node.removeAttribute('style');
22390             }
22391         }
22392         
22393         this.iterateChildren(node, this.cleanTableWidths);
22394         
22395         
22396     },
22397     
22398     
22399     
22400     
22401     domToHTML : function(currentElement, depth, nopadtext) {
22402         
22403         depth = depth || 0;
22404         nopadtext = nopadtext || false;
22405     
22406         if (!currentElement) {
22407             return this.domToHTML(this.doc.body);
22408         }
22409         
22410         //Roo.log(currentElement);
22411         var j;
22412         var allText = false;
22413         var nodeName = currentElement.nodeName;
22414         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22415         
22416         if  (nodeName == '#text') {
22417             
22418             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22419         }
22420         
22421         
22422         var ret = '';
22423         if (nodeName != 'BODY') {
22424              
22425             var i = 0;
22426             // Prints the node tagName, such as <A>, <IMG>, etc
22427             if (tagName) {
22428                 var attr = [];
22429                 for(i = 0; i < currentElement.attributes.length;i++) {
22430                     // quoting?
22431                     var aname = currentElement.attributes.item(i).name;
22432                     if (!currentElement.attributes.item(i).value.length) {
22433                         continue;
22434                     }
22435                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22436                 }
22437                 
22438                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22439             } 
22440             else {
22441                 
22442                 // eack
22443             }
22444         } else {
22445             tagName = false;
22446         }
22447         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22448             return ret;
22449         }
22450         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22451             nopadtext = true;
22452         }
22453         
22454         
22455         // Traverse the tree
22456         i = 0;
22457         var currentElementChild = currentElement.childNodes.item(i);
22458         var allText = true;
22459         var innerHTML  = '';
22460         lastnode = '';
22461         while (currentElementChild) {
22462             // Formatting code (indent the tree so it looks nice on the screen)
22463             var nopad = nopadtext;
22464             if (lastnode == 'SPAN') {
22465                 nopad  = true;
22466             }
22467             // text
22468             if  (currentElementChild.nodeName == '#text') {
22469                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22470                 toadd = nopadtext ? toadd : toadd.trim();
22471                 if (!nopad && toadd.length > 80) {
22472                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22473                 }
22474                 innerHTML  += toadd;
22475                 
22476                 i++;
22477                 currentElementChild = currentElement.childNodes.item(i);
22478                 lastNode = '';
22479                 continue;
22480             }
22481             allText = false;
22482             
22483             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22484                 
22485             // Recursively traverse the tree structure of the child node
22486             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22487             lastnode = currentElementChild.nodeName;
22488             i++;
22489             currentElementChild=currentElement.childNodes.item(i);
22490         }
22491         
22492         ret += innerHTML;
22493         
22494         if (!allText) {
22495                 // The remaining code is mostly for formatting the tree
22496             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22497         }
22498         
22499         
22500         if (tagName) {
22501             ret+= "</"+tagName+">";
22502         }
22503         return ret;
22504         
22505     },
22506         
22507     applyBlacklists : function()
22508     {
22509         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22510         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22511         
22512         this.white = [];
22513         this.black = [];
22514         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22515             if (b.indexOf(tag) > -1) {
22516                 return;
22517             }
22518             this.white.push(tag);
22519             
22520         }, this);
22521         
22522         Roo.each(w, function(tag) {
22523             if (b.indexOf(tag) > -1) {
22524                 return;
22525             }
22526             if (this.white.indexOf(tag) > -1) {
22527                 return;
22528             }
22529             this.white.push(tag);
22530             
22531         }, this);
22532         
22533         
22534         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22535             if (w.indexOf(tag) > -1) {
22536                 return;
22537             }
22538             this.black.push(tag);
22539             
22540         }, this);
22541         
22542         Roo.each(b, function(tag) {
22543             if (w.indexOf(tag) > -1) {
22544                 return;
22545             }
22546             if (this.black.indexOf(tag) > -1) {
22547                 return;
22548             }
22549             this.black.push(tag);
22550             
22551         }, this);
22552         
22553         
22554         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22555         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22556         
22557         this.cwhite = [];
22558         this.cblack = [];
22559         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22560             if (b.indexOf(tag) > -1) {
22561                 return;
22562             }
22563             this.cwhite.push(tag);
22564             
22565         }, this);
22566         
22567         Roo.each(w, function(tag) {
22568             if (b.indexOf(tag) > -1) {
22569                 return;
22570             }
22571             if (this.cwhite.indexOf(tag) > -1) {
22572                 return;
22573             }
22574             this.cwhite.push(tag);
22575             
22576         }, this);
22577         
22578         
22579         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22580             if (w.indexOf(tag) > -1) {
22581                 return;
22582             }
22583             this.cblack.push(tag);
22584             
22585         }, this);
22586         
22587         Roo.each(b, function(tag) {
22588             if (w.indexOf(tag) > -1) {
22589                 return;
22590             }
22591             if (this.cblack.indexOf(tag) > -1) {
22592                 return;
22593             }
22594             this.cblack.push(tag);
22595             
22596         }, this);
22597     },
22598     
22599     setStylesheets : function(stylesheets)
22600     {
22601         if(typeof(stylesheets) == 'string'){
22602             Roo.get(this.iframe.contentDocument.head).createChild({
22603                 tag : 'link',
22604                 rel : 'stylesheet',
22605                 type : 'text/css',
22606                 href : stylesheets
22607             });
22608             
22609             return;
22610         }
22611         var _this = this;
22612      
22613         Roo.each(stylesheets, function(s) {
22614             if(!s.length){
22615                 return;
22616             }
22617             
22618             Roo.get(_this.iframe.contentDocument.head).createChild({
22619                 tag : 'link',
22620                 rel : 'stylesheet',
22621                 type : 'text/css',
22622                 href : s
22623             });
22624         });
22625
22626         
22627     },
22628     
22629     removeStylesheets : function()
22630     {
22631         var _this = this;
22632         
22633         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22634             s.remove();
22635         });
22636     }
22637     
22638     // hide stuff that is not compatible
22639     /**
22640      * @event blur
22641      * @hide
22642      */
22643     /**
22644      * @event change
22645      * @hide
22646      */
22647     /**
22648      * @event focus
22649      * @hide
22650      */
22651     /**
22652      * @event specialkey
22653      * @hide
22654      */
22655     /**
22656      * @cfg {String} fieldClass @hide
22657      */
22658     /**
22659      * @cfg {String} focusClass @hide
22660      */
22661     /**
22662      * @cfg {String} autoCreate @hide
22663      */
22664     /**
22665      * @cfg {String} inputType @hide
22666      */
22667     /**
22668      * @cfg {String} invalidClass @hide
22669      */
22670     /**
22671      * @cfg {String} invalidText @hide
22672      */
22673     /**
22674      * @cfg {String} msgFx @hide
22675      */
22676     /**
22677      * @cfg {String} validateOnBlur @hide
22678      */
22679 });
22680
22681 Roo.HtmlEditorCore.white = [
22682         'area', 'br', 'img', 'input', 'hr', 'wbr',
22683         
22684        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22685        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22686        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22687        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22688        'table',   'ul',         'xmp', 
22689        
22690        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22691       'thead',   'tr', 
22692      
22693       'dir', 'menu', 'ol', 'ul', 'dl',
22694        
22695       'embed',  'object'
22696 ];
22697
22698
22699 Roo.HtmlEditorCore.black = [
22700     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22701         'applet', // 
22702         'base',   'basefont', 'bgsound', 'blink',  'body', 
22703         'frame',  'frameset', 'head',    'html',   'ilayer', 
22704         'iframe', 'layer',  'link',     'meta',    'object',   
22705         'script', 'style' ,'title',  'xml' // clean later..
22706 ];
22707 Roo.HtmlEditorCore.clean = [
22708     'script', 'style', 'title', 'xml'
22709 ];
22710 Roo.HtmlEditorCore.remove = [
22711     'font'
22712 ];
22713 // attributes..
22714
22715 Roo.HtmlEditorCore.ablack = [
22716     'on'
22717 ];
22718     
22719 Roo.HtmlEditorCore.aclean = [ 
22720     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22721 ];
22722
22723 // protocols..
22724 Roo.HtmlEditorCore.pwhite= [
22725         'http',  'https',  'mailto'
22726 ];
22727
22728 // white listed style attributes.
22729 Roo.HtmlEditorCore.cwhite= [
22730       //  'text-align', /// default is to allow most things..
22731       
22732          
22733 //        'font-size'//??
22734 ];
22735
22736 // black listed style attributes.
22737 Roo.HtmlEditorCore.cblack= [
22738       //  'font-size' -- this can be set by the project 
22739 ];
22740
22741
22742 Roo.HtmlEditorCore.swapCodes   =[ 
22743     [    8211, "--" ], 
22744     [    8212, "--" ], 
22745     [    8216,  "'" ],  
22746     [    8217, "'" ],  
22747     [    8220, '"' ],  
22748     [    8221, '"' ],  
22749     [    8226, "*" ],  
22750     [    8230, "..." ]
22751 ]; 
22752
22753     /*
22754  * - LGPL
22755  *
22756  * HtmlEditor
22757  * 
22758  */
22759
22760 /**
22761  * @class Roo.bootstrap.HtmlEditor
22762  * @extends Roo.bootstrap.TextArea
22763  * Bootstrap HtmlEditor class
22764
22765  * @constructor
22766  * Create a new HtmlEditor
22767  * @param {Object} config The config object
22768  */
22769
22770 Roo.bootstrap.HtmlEditor = function(config){
22771     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22772     if (!this.toolbars) {
22773         this.toolbars = [];
22774     }
22775     
22776     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22777     this.addEvents({
22778             /**
22779              * @event initialize
22780              * Fires when the editor is fully initialized (including the iframe)
22781              * @param {HtmlEditor} this
22782              */
22783             initialize: true,
22784             /**
22785              * @event activate
22786              * Fires when the editor is first receives the focus. Any insertion must wait
22787              * until after this event.
22788              * @param {HtmlEditor} this
22789              */
22790             activate: true,
22791              /**
22792              * @event beforesync
22793              * Fires before the textarea is updated with content from the editor iframe. Return false
22794              * to cancel the sync.
22795              * @param {HtmlEditor} this
22796              * @param {String} html
22797              */
22798             beforesync: true,
22799              /**
22800              * @event beforepush
22801              * Fires before the iframe editor is updated with content from the textarea. Return false
22802              * to cancel the push.
22803              * @param {HtmlEditor} this
22804              * @param {String} html
22805              */
22806             beforepush: true,
22807              /**
22808              * @event sync
22809              * Fires when the textarea is updated with content from the editor iframe.
22810              * @param {HtmlEditor} this
22811              * @param {String} html
22812              */
22813             sync: true,
22814              /**
22815              * @event push
22816              * Fires when the iframe editor is updated with content from the textarea.
22817              * @param {HtmlEditor} this
22818              * @param {String} html
22819              */
22820             push: true,
22821              /**
22822              * @event editmodechange
22823              * Fires when the editor switches edit modes
22824              * @param {HtmlEditor} this
22825              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22826              */
22827             editmodechange: true,
22828             /**
22829              * @event editorevent
22830              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22831              * @param {HtmlEditor} this
22832              */
22833             editorevent: true,
22834             /**
22835              * @event firstfocus
22836              * Fires when on first focus - needed by toolbars..
22837              * @param {HtmlEditor} this
22838              */
22839             firstfocus: true,
22840             /**
22841              * @event autosave
22842              * Auto save the htmlEditor value as a file into Events
22843              * @param {HtmlEditor} this
22844              */
22845             autosave: true,
22846             /**
22847              * @event savedpreview
22848              * preview the saved version of htmlEditor
22849              * @param {HtmlEditor} this
22850              */
22851             savedpreview: true
22852         });
22853 };
22854
22855
22856 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22857     
22858     
22859       /**
22860      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22861      */
22862     toolbars : false,
22863     
22864      /**
22865     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22866     */
22867     btns : [],
22868    
22869      /**
22870      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22871      *                        Roo.resizable.
22872      */
22873     resizable : false,
22874      /**
22875      * @cfg {Number} height (in pixels)
22876      */   
22877     height: 300,
22878    /**
22879      * @cfg {Number} width (in pixels)
22880      */   
22881     width: false,
22882     
22883     /**
22884      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22885      * 
22886      */
22887     stylesheets: false,
22888     
22889     // id of frame..
22890     frameId: false,
22891     
22892     // private properties
22893     validationEvent : false,
22894     deferHeight: true,
22895     initialized : false,
22896     activated : false,
22897     
22898     onFocus : Roo.emptyFn,
22899     iframePad:3,
22900     hideMode:'offsets',
22901     
22902     tbContainer : false,
22903     
22904     toolbarContainer :function() {
22905         return this.wrap.select('.x-html-editor-tb',true).first();
22906     },
22907
22908     /**
22909      * Protected method that will not generally be called directly. It
22910      * is called when the editor creates its toolbar. Override this method if you need to
22911      * add custom toolbar buttons.
22912      * @param {HtmlEditor} editor
22913      */
22914     createToolbar : function(){
22915         Roo.log('renewing');
22916         Roo.log("create toolbars");
22917         
22918         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22919         this.toolbars[0].render(this.toolbarContainer());
22920         
22921         return;
22922         
22923 //        if (!editor.toolbars || !editor.toolbars.length) {
22924 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22925 //        }
22926 //        
22927 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22928 //            editor.toolbars[i] = Roo.factory(
22929 //                    typeof(editor.toolbars[i]) == 'string' ?
22930 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22931 //                Roo.bootstrap.HtmlEditor);
22932 //            editor.toolbars[i].init(editor);
22933 //        }
22934     },
22935
22936      
22937     // private
22938     onRender : function(ct, position)
22939     {
22940        // Roo.log("Call onRender: " + this.xtype);
22941         var _t = this;
22942         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22943       
22944         this.wrap = this.inputEl().wrap({
22945             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22946         });
22947         
22948         this.editorcore.onRender(ct, position);
22949          
22950         if (this.resizable) {
22951             this.resizeEl = new Roo.Resizable(this.wrap, {
22952                 pinned : true,
22953                 wrap: true,
22954                 dynamic : true,
22955                 minHeight : this.height,
22956                 height: this.height,
22957                 handles : this.resizable,
22958                 width: this.width,
22959                 listeners : {
22960                     resize : function(r, w, h) {
22961                         _t.onResize(w,h); // -something
22962                     }
22963                 }
22964             });
22965             
22966         }
22967         this.createToolbar(this);
22968        
22969         
22970         if(!this.width && this.resizable){
22971             this.setSize(this.wrap.getSize());
22972         }
22973         if (this.resizeEl) {
22974             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22975             // should trigger onReize..
22976         }
22977         
22978     },
22979
22980     // private
22981     onResize : function(w, h)
22982     {
22983         Roo.log('resize: ' +w + ',' + h );
22984         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22985         var ew = false;
22986         var eh = false;
22987         
22988         if(this.inputEl() ){
22989             if(typeof w == 'number'){
22990                 var aw = w - this.wrap.getFrameWidth('lr');
22991                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22992                 ew = aw;
22993             }
22994             if(typeof h == 'number'){
22995                  var tbh = -11;  // fixme it needs to tool bar size!
22996                 for (var i =0; i < this.toolbars.length;i++) {
22997                     // fixme - ask toolbars for heights?
22998                     tbh += this.toolbars[i].el.getHeight();
22999                     //if (this.toolbars[i].footer) {
23000                     //    tbh += this.toolbars[i].footer.el.getHeight();
23001                     //}
23002                 }
23003               
23004                 
23005                 
23006                 
23007                 
23008                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23009                 ah -= 5; // knock a few pixes off for look..
23010                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23011                 var eh = ah;
23012             }
23013         }
23014         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23015         this.editorcore.onResize(ew,eh);
23016         
23017     },
23018
23019     /**
23020      * Toggles the editor between standard and source edit mode.
23021      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23022      */
23023     toggleSourceEdit : function(sourceEditMode)
23024     {
23025         this.editorcore.toggleSourceEdit(sourceEditMode);
23026         
23027         if(this.editorcore.sourceEditMode){
23028             Roo.log('editor - showing textarea');
23029             
23030 //            Roo.log('in');
23031 //            Roo.log(this.syncValue());
23032             this.syncValue();
23033             this.inputEl().removeClass(['hide', 'x-hidden']);
23034             this.inputEl().dom.removeAttribute('tabIndex');
23035             this.inputEl().focus();
23036         }else{
23037             Roo.log('editor - hiding textarea');
23038 //            Roo.log('out')
23039 //            Roo.log(this.pushValue()); 
23040             this.pushValue();
23041             
23042             this.inputEl().addClass(['hide', 'x-hidden']);
23043             this.inputEl().dom.setAttribute('tabIndex', -1);
23044             //this.deferFocus();
23045         }
23046          
23047         if(this.resizable){
23048             this.setSize(this.wrap.getSize());
23049         }
23050         
23051         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23052     },
23053  
23054     // private (for BoxComponent)
23055     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23056
23057     // private (for BoxComponent)
23058     getResizeEl : function(){
23059         return this.wrap;
23060     },
23061
23062     // private (for BoxComponent)
23063     getPositionEl : function(){
23064         return this.wrap;
23065     },
23066
23067     // private
23068     initEvents : function(){
23069         this.originalValue = this.getValue();
23070     },
23071
23072 //    /**
23073 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23074 //     * @method
23075 //     */
23076 //    markInvalid : Roo.emptyFn,
23077 //    /**
23078 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23079 //     * @method
23080 //     */
23081 //    clearInvalid : Roo.emptyFn,
23082
23083     setValue : function(v){
23084         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23085         this.editorcore.pushValue();
23086     },
23087
23088      
23089     // private
23090     deferFocus : function(){
23091         this.focus.defer(10, this);
23092     },
23093
23094     // doc'ed in Field
23095     focus : function(){
23096         this.editorcore.focus();
23097         
23098     },
23099       
23100
23101     // private
23102     onDestroy : function(){
23103         
23104         
23105         
23106         if(this.rendered){
23107             
23108             for (var i =0; i < this.toolbars.length;i++) {
23109                 // fixme - ask toolbars for heights?
23110                 this.toolbars[i].onDestroy();
23111             }
23112             
23113             this.wrap.dom.innerHTML = '';
23114             this.wrap.remove();
23115         }
23116     },
23117
23118     // private
23119     onFirstFocus : function(){
23120         //Roo.log("onFirstFocus");
23121         this.editorcore.onFirstFocus();
23122          for (var i =0; i < this.toolbars.length;i++) {
23123             this.toolbars[i].onFirstFocus();
23124         }
23125         
23126     },
23127     
23128     // private
23129     syncValue : function()
23130     {   
23131         this.editorcore.syncValue();
23132     },
23133     
23134     pushValue : function()
23135     {   
23136         this.editorcore.pushValue();
23137     }
23138      
23139     
23140     // hide stuff that is not compatible
23141     /**
23142      * @event blur
23143      * @hide
23144      */
23145     /**
23146      * @event change
23147      * @hide
23148      */
23149     /**
23150      * @event focus
23151      * @hide
23152      */
23153     /**
23154      * @event specialkey
23155      * @hide
23156      */
23157     /**
23158      * @cfg {String} fieldClass @hide
23159      */
23160     /**
23161      * @cfg {String} focusClass @hide
23162      */
23163     /**
23164      * @cfg {String} autoCreate @hide
23165      */
23166     /**
23167      * @cfg {String} inputType @hide
23168      */
23169     /**
23170      * @cfg {String} invalidClass @hide
23171      */
23172     /**
23173      * @cfg {String} invalidText @hide
23174      */
23175     /**
23176      * @cfg {String} msgFx @hide
23177      */
23178     /**
23179      * @cfg {String} validateOnBlur @hide
23180      */
23181 });
23182  
23183     
23184    
23185    
23186    
23187       
23188 Roo.namespace('Roo.bootstrap.htmleditor');
23189 /**
23190  * @class Roo.bootstrap.HtmlEditorToolbar1
23191  * Basic Toolbar
23192  * 
23193  * Usage:
23194  *
23195  new Roo.bootstrap.HtmlEditor({
23196     ....
23197     toolbars : [
23198         new Roo.bootstrap.HtmlEditorToolbar1({
23199             disable : { fonts: 1 , format: 1, ..., ... , ...],
23200             btns : [ .... ]
23201         })
23202     }
23203      
23204  * 
23205  * @cfg {Object} disable List of elements to disable..
23206  * @cfg {Array} btns List of additional buttons.
23207  * 
23208  * 
23209  * NEEDS Extra CSS? 
23210  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23211  */
23212  
23213 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23214 {
23215     
23216     Roo.apply(this, config);
23217     
23218     // default disabled, based on 'good practice'..
23219     this.disable = this.disable || {};
23220     Roo.applyIf(this.disable, {
23221         fontSize : true,
23222         colors : true,
23223         specialElements : true
23224     });
23225     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23226     
23227     this.editor = config.editor;
23228     this.editorcore = config.editor.editorcore;
23229     
23230     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23231     
23232     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23233     // dont call parent... till later.
23234 }
23235 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23236      
23237     bar : true,
23238     
23239     editor : false,
23240     editorcore : false,
23241     
23242     
23243     formats : [
23244         "p" ,  
23245         "h1","h2","h3","h4","h5","h6", 
23246         "pre", "code", 
23247         "abbr", "acronym", "address", "cite", "samp", "var",
23248         'div','span'
23249     ],
23250     
23251     onRender : function(ct, position)
23252     {
23253        // Roo.log("Call onRender: " + this.xtype);
23254         
23255        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23256        Roo.log(this.el);
23257        this.el.dom.style.marginBottom = '0';
23258        var _this = this;
23259        var editorcore = this.editorcore;
23260        var editor= this.editor;
23261        
23262        var children = [];
23263        var btn = function(id,cmd , toggle, handler, html){
23264        
23265             var  event = toggle ? 'toggle' : 'click';
23266        
23267             var a = {
23268                 size : 'sm',
23269                 xtype: 'Button',
23270                 xns: Roo.bootstrap,
23271                 glyphicon : id,
23272                 cmd : id || cmd,
23273                 enableToggle:toggle !== false,
23274                 html : html || '',
23275                 pressed : toggle ? false : null,
23276                 listeners : {}
23277             };
23278             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23279                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23280             };
23281             children.push(a);
23282             return a;
23283        }
23284        
23285     //    var cb_box = function...
23286         
23287         var style = {
23288                 xtype: 'Button',
23289                 size : 'sm',
23290                 xns: Roo.bootstrap,
23291                 glyphicon : 'font',
23292                 //html : 'submit'
23293                 menu : {
23294                     xtype: 'Menu',
23295                     xns: Roo.bootstrap,
23296                     items:  []
23297                 }
23298         };
23299         Roo.each(this.formats, function(f) {
23300             style.menu.items.push({
23301                 xtype :'MenuItem',
23302                 xns: Roo.bootstrap,
23303                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23304                 tagname : f,
23305                 listeners : {
23306                     click : function()
23307                     {
23308                         editorcore.insertTag(this.tagname);
23309                         editor.focus();
23310                     }
23311                 }
23312                 
23313             });
23314         });
23315         children.push(style);   
23316         
23317         btn('bold',false,true);
23318         btn('italic',false,true);
23319         btn('align-left', 'justifyleft',true);
23320         btn('align-center', 'justifycenter',true);
23321         btn('align-right' , 'justifyright',true);
23322         btn('link', false, false, function(btn) {
23323             //Roo.log("create link?");
23324             var url = prompt(this.createLinkText, this.defaultLinkValue);
23325             if(url && url != 'http:/'+'/'){
23326                 this.editorcore.relayCmd('createlink', url);
23327             }
23328         }),
23329         btn('list','insertunorderedlist',true);
23330         btn('pencil', false,true, function(btn){
23331                 Roo.log(this);
23332                 this.toggleSourceEdit(btn.pressed);
23333         });
23334         
23335         if (this.editor.btns.length > 0) {
23336             for (var i = 0; i<this.editor.btns.length; i++) {
23337                 children.push(this.editor.btns[i]);
23338             }
23339         }
23340         
23341         /*
23342         var cog = {
23343                 xtype: 'Button',
23344                 size : 'sm',
23345                 xns: Roo.bootstrap,
23346                 glyphicon : 'cog',
23347                 //html : 'submit'
23348                 menu : {
23349                     xtype: 'Menu',
23350                     xns: Roo.bootstrap,
23351                     items:  []
23352                 }
23353         };
23354         
23355         cog.menu.items.push({
23356             xtype :'MenuItem',
23357             xns: Roo.bootstrap,
23358             html : Clean styles,
23359             tagname : f,
23360             listeners : {
23361                 click : function()
23362                 {
23363                     editorcore.insertTag(this.tagname);
23364                     editor.focus();
23365                 }
23366             }
23367             
23368         });
23369        */
23370         
23371          
23372        this.xtype = 'NavSimplebar';
23373         
23374         for(var i=0;i< children.length;i++) {
23375             
23376             this.buttons.add(this.addxtypeChild(children[i]));
23377             
23378         }
23379         
23380         editor.on('editorevent', this.updateToolbar, this);
23381     },
23382     onBtnClick : function(id)
23383     {
23384        this.editorcore.relayCmd(id);
23385        this.editorcore.focus();
23386     },
23387     
23388     /**
23389      * Protected method that will not generally be called directly. It triggers
23390      * a toolbar update by reading the markup state of the current selection in the editor.
23391      */
23392     updateToolbar: function(){
23393
23394         if(!this.editorcore.activated){
23395             this.editor.onFirstFocus(); // is this neeed?
23396             return;
23397         }
23398
23399         var btns = this.buttons; 
23400         var doc = this.editorcore.doc;
23401         btns.get('bold').setActive(doc.queryCommandState('bold'));
23402         btns.get('italic').setActive(doc.queryCommandState('italic'));
23403         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23404         
23405         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23406         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23407         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23408         
23409         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23410         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23411          /*
23412         
23413         var ans = this.editorcore.getAllAncestors();
23414         if (this.formatCombo) {
23415             
23416             
23417             var store = this.formatCombo.store;
23418             this.formatCombo.setValue("");
23419             for (var i =0; i < ans.length;i++) {
23420                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23421                     // select it..
23422                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23423                     break;
23424                 }
23425             }
23426         }
23427         
23428         
23429         
23430         // hides menus... - so this cant be on a menu...
23431         Roo.bootstrap.MenuMgr.hideAll();
23432         */
23433         Roo.bootstrap.MenuMgr.hideAll();
23434         //this.editorsyncValue();
23435     },
23436     onFirstFocus: function() {
23437         this.buttons.each(function(item){
23438            item.enable();
23439         });
23440     },
23441     toggleSourceEdit : function(sourceEditMode){
23442         
23443           
23444         if(sourceEditMode){
23445             Roo.log("disabling buttons");
23446            this.buttons.each( function(item){
23447                 if(item.cmd != 'pencil'){
23448                     item.disable();
23449                 }
23450             });
23451           
23452         }else{
23453             Roo.log("enabling buttons");
23454             if(this.editorcore.initialized){
23455                 this.buttons.each( function(item){
23456                     item.enable();
23457                 });
23458             }
23459             
23460         }
23461         Roo.log("calling toggole on editor");
23462         // tell the editor that it's been pressed..
23463         this.editor.toggleSourceEdit(sourceEditMode);
23464        
23465     }
23466 });
23467
23468
23469
23470
23471
23472 /**
23473  * @class Roo.bootstrap.Table.AbstractSelectionModel
23474  * @extends Roo.util.Observable
23475  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23476  * implemented by descendant classes.  This class should not be directly instantiated.
23477  * @constructor
23478  */
23479 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23480     this.locked = false;
23481     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23482 };
23483
23484
23485 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23486     /** @ignore Called by the grid automatically. Do not call directly. */
23487     init : function(grid){
23488         this.grid = grid;
23489         this.initEvents();
23490     },
23491
23492     /**
23493      * Locks the selections.
23494      */
23495     lock : function(){
23496         this.locked = true;
23497     },
23498
23499     /**
23500      * Unlocks the selections.
23501      */
23502     unlock : function(){
23503         this.locked = false;
23504     },
23505
23506     /**
23507      * Returns true if the selections are locked.
23508      * @return {Boolean}
23509      */
23510     isLocked : function(){
23511         return this.locked;
23512     }
23513 });
23514 /**
23515  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23516  * @class Roo.bootstrap.Table.RowSelectionModel
23517  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23518  * It supports multiple selections and keyboard selection/navigation. 
23519  * @constructor
23520  * @param {Object} config
23521  */
23522
23523 Roo.bootstrap.Table.RowSelectionModel = function(config){
23524     Roo.apply(this, config);
23525     this.selections = new Roo.util.MixedCollection(false, function(o){
23526         return o.id;
23527     });
23528
23529     this.last = false;
23530     this.lastActive = false;
23531
23532     this.addEvents({
23533         /**
23534              * @event selectionchange
23535              * Fires when the selection changes
23536              * @param {SelectionModel} this
23537              */
23538             "selectionchange" : true,
23539         /**
23540              * @event afterselectionchange
23541              * Fires after the selection changes (eg. by key press or clicking)
23542              * @param {SelectionModel} this
23543              */
23544             "afterselectionchange" : true,
23545         /**
23546              * @event beforerowselect
23547              * Fires when a row is selected being selected, return false to cancel.
23548              * @param {SelectionModel} this
23549              * @param {Number} rowIndex The selected index
23550              * @param {Boolean} keepExisting False if other selections will be cleared
23551              */
23552             "beforerowselect" : true,
23553         /**
23554              * @event rowselect
23555              * Fires when a row is selected.
23556              * @param {SelectionModel} this
23557              * @param {Number} rowIndex The selected index
23558              * @param {Roo.data.Record} r The record
23559              */
23560             "rowselect" : true,
23561         /**
23562              * @event rowdeselect
23563              * Fires when a row is deselected.
23564              * @param {SelectionModel} this
23565              * @param {Number} rowIndex The selected index
23566              */
23567         "rowdeselect" : true
23568     });
23569     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23570     this.locked = false;
23571  };
23572
23573 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23574     /**
23575      * @cfg {Boolean} singleSelect
23576      * True to allow selection of only one row at a time (defaults to false)
23577      */
23578     singleSelect : false,
23579
23580     // private
23581     initEvents : function()
23582     {
23583
23584         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23585         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23586         //}else{ // allow click to work like normal
23587          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23588         //}
23589         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23590         this.grid.on("rowclick", this.handleMouseDown, this);
23591         
23592         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23593             "up" : function(e){
23594                 if(!e.shiftKey){
23595                     this.selectPrevious(e.shiftKey);
23596                 }else if(this.last !== false && this.lastActive !== false){
23597                     var last = this.last;
23598                     this.selectRange(this.last,  this.lastActive-1);
23599                     this.grid.getView().focusRow(this.lastActive);
23600                     if(last !== false){
23601                         this.last = last;
23602                     }
23603                 }else{
23604                     this.selectFirstRow();
23605                 }
23606                 this.fireEvent("afterselectionchange", this);
23607             },
23608             "down" : function(e){
23609                 if(!e.shiftKey){
23610                     this.selectNext(e.shiftKey);
23611                 }else if(this.last !== false && this.lastActive !== false){
23612                     var last = this.last;
23613                     this.selectRange(this.last,  this.lastActive+1);
23614                     this.grid.getView().focusRow(this.lastActive);
23615                     if(last !== false){
23616                         this.last = last;
23617                     }
23618                 }else{
23619                     this.selectFirstRow();
23620                 }
23621                 this.fireEvent("afterselectionchange", this);
23622             },
23623             scope: this
23624         });
23625         this.grid.store.on('load', function(){
23626             this.selections.clear();
23627         },this);
23628         /*
23629         var view = this.grid.view;
23630         view.on("refresh", this.onRefresh, this);
23631         view.on("rowupdated", this.onRowUpdated, this);
23632         view.on("rowremoved", this.onRemove, this);
23633         */
23634     },
23635
23636     // private
23637     onRefresh : function()
23638     {
23639         var ds = this.grid.store, i, v = this.grid.view;
23640         var s = this.selections;
23641         s.each(function(r){
23642             if((i = ds.indexOfId(r.id)) != -1){
23643                 v.onRowSelect(i);
23644             }else{
23645                 s.remove(r);
23646             }
23647         });
23648     },
23649
23650     // private
23651     onRemove : function(v, index, r){
23652         this.selections.remove(r);
23653     },
23654
23655     // private
23656     onRowUpdated : function(v, index, r){
23657         if(this.isSelected(r)){
23658             v.onRowSelect(index);
23659         }
23660     },
23661
23662     /**
23663      * Select records.
23664      * @param {Array} records The records to select
23665      * @param {Boolean} keepExisting (optional) True to keep existing selections
23666      */
23667     selectRecords : function(records, keepExisting)
23668     {
23669         if(!keepExisting){
23670             this.clearSelections();
23671         }
23672             var ds = this.grid.store;
23673         for(var i = 0, len = records.length; i < len; i++){
23674             this.selectRow(ds.indexOf(records[i]), true);
23675         }
23676     },
23677
23678     /**
23679      * Gets the number of selected rows.
23680      * @return {Number}
23681      */
23682     getCount : function(){
23683         return this.selections.length;
23684     },
23685
23686     /**
23687      * Selects the first row in the grid.
23688      */
23689     selectFirstRow : function(){
23690         this.selectRow(0);
23691     },
23692
23693     /**
23694      * Select the last row.
23695      * @param {Boolean} keepExisting (optional) True to keep existing selections
23696      */
23697     selectLastRow : function(keepExisting){
23698         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23699         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23700     },
23701
23702     /**
23703      * Selects the row immediately following the last selected row.
23704      * @param {Boolean} keepExisting (optional) True to keep existing selections
23705      */
23706     selectNext : function(keepExisting)
23707     {
23708             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23709             this.selectRow(this.last+1, keepExisting);
23710             this.grid.getView().focusRow(this.last);
23711         }
23712     },
23713
23714     /**
23715      * Selects the row that precedes the last selected row.
23716      * @param {Boolean} keepExisting (optional) True to keep existing selections
23717      */
23718     selectPrevious : function(keepExisting){
23719         if(this.last){
23720             this.selectRow(this.last-1, keepExisting);
23721             this.grid.getView().focusRow(this.last);
23722         }
23723     },
23724
23725     /**
23726      * Returns the selected records
23727      * @return {Array} Array of selected records
23728      */
23729     getSelections : function(){
23730         return [].concat(this.selections.items);
23731     },
23732
23733     /**
23734      * Returns the first selected record.
23735      * @return {Record}
23736      */
23737     getSelected : function(){
23738         return this.selections.itemAt(0);
23739     },
23740
23741
23742     /**
23743      * Clears all selections.
23744      */
23745     clearSelections : function(fast)
23746     {
23747         if(this.locked) {
23748             return;
23749         }
23750         if(fast !== true){
23751                 var ds = this.grid.store;
23752             var s = this.selections;
23753             s.each(function(r){
23754                 this.deselectRow(ds.indexOfId(r.id));
23755             }, this);
23756             s.clear();
23757         }else{
23758             this.selections.clear();
23759         }
23760         this.last = false;
23761     },
23762
23763
23764     /**
23765      * Selects all rows.
23766      */
23767     selectAll : function(){
23768         if(this.locked) {
23769             return;
23770         }
23771         this.selections.clear();
23772         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23773             this.selectRow(i, true);
23774         }
23775     },
23776
23777     /**
23778      * Returns True if there is a selection.
23779      * @return {Boolean}
23780      */
23781     hasSelection : function(){
23782         return this.selections.length > 0;
23783     },
23784
23785     /**
23786      * Returns True if the specified row is selected.
23787      * @param {Number/Record} record The record or index of the record to check
23788      * @return {Boolean}
23789      */
23790     isSelected : function(index){
23791             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23792         return (r && this.selections.key(r.id) ? true : false);
23793     },
23794
23795     /**
23796      * Returns True if the specified record id is selected.
23797      * @param {String} id The id of record to check
23798      * @return {Boolean}
23799      */
23800     isIdSelected : function(id){
23801         return (this.selections.key(id) ? true : false);
23802     },
23803
23804
23805     // private
23806     handleMouseDBClick : function(e, t){
23807         
23808     },
23809     // private
23810     handleMouseDown : function(e, t)
23811     {
23812             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23813         if(this.isLocked() || rowIndex < 0 ){
23814             return;
23815         };
23816         if(e.shiftKey && this.last !== false){
23817             var last = this.last;
23818             this.selectRange(last, rowIndex, e.ctrlKey);
23819             this.last = last; // reset the last
23820             t.focus();
23821     
23822         }else{
23823             var isSelected = this.isSelected(rowIndex);
23824             //Roo.log("select row:" + rowIndex);
23825             if(isSelected){
23826                 this.deselectRow(rowIndex);
23827             } else {
23828                         this.selectRow(rowIndex, true);
23829             }
23830     
23831             /*
23832                 if(e.button !== 0 && isSelected){
23833                 alert('rowIndex 2: ' + rowIndex);
23834                     view.focusRow(rowIndex);
23835                 }else if(e.ctrlKey && isSelected){
23836                     this.deselectRow(rowIndex);
23837                 }else if(!isSelected){
23838                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23839                     view.focusRow(rowIndex);
23840                 }
23841             */
23842         }
23843         this.fireEvent("afterselectionchange", this);
23844     },
23845     // private
23846     handleDragableRowClick :  function(grid, rowIndex, e) 
23847     {
23848         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23849             this.selectRow(rowIndex, false);
23850             grid.view.focusRow(rowIndex);
23851              this.fireEvent("afterselectionchange", this);
23852         }
23853     },
23854     
23855     /**
23856      * Selects multiple rows.
23857      * @param {Array} rows Array of the indexes of the row to select
23858      * @param {Boolean} keepExisting (optional) True to keep existing selections
23859      */
23860     selectRows : function(rows, keepExisting){
23861         if(!keepExisting){
23862             this.clearSelections();
23863         }
23864         for(var i = 0, len = rows.length; i < len; i++){
23865             this.selectRow(rows[i], true);
23866         }
23867     },
23868
23869     /**
23870      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23871      * @param {Number} startRow The index of the first row in the range
23872      * @param {Number} endRow The index of the last row in the range
23873      * @param {Boolean} keepExisting (optional) True to retain existing selections
23874      */
23875     selectRange : function(startRow, endRow, keepExisting){
23876         if(this.locked) {
23877             return;
23878         }
23879         if(!keepExisting){
23880             this.clearSelections();
23881         }
23882         if(startRow <= endRow){
23883             for(var i = startRow; i <= endRow; i++){
23884                 this.selectRow(i, true);
23885             }
23886         }else{
23887             for(var i = startRow; i >= endRow; i--){
23888                 this.selectRow(i, true);
23889             }
23890         }
23891     },
23892
23893     /**
23894      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23895      * @param {Number} startRow The index of the first row in the range
23896      * @param {Number} endRow The index of the last row in the range
23897      */
23898     deselectRange : function(startRow, endRow, preventViewNotify){
23899         if(this.locked) {
23900             return;
23901         }
23902         for(var i = startRow; i <= endRow; i++){
23903             this.deselectRow(i, preventViewNotify);
23904         }
23905     },
23906
23907     /**
23908      * Selects a row.
23909      * @param {Number} row The index of the row to select
23910      * @param {Boolean} keepExisting (optional) True to keep existing selections
23911      */
23912     selectRow : function(index, keepExisting, preventViewNotify)
23913     {
23914             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23915             return;
23916         }
23917         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23918             if(!keepExisting || this.singleSelect){
23919                 this.clearSelections();
23920             }
23921             
23922             var r = this.grid.store.getAt(index);
23923             //console.log('selectRow - record id :' + r.id);
23924             
23925             this.selections.add(r);
23926             this.last = this.lastActive = index;
23927             if(!preventViewNotify){
23928                 var proxy = new Roo.Element(
23929                                 this.grid.getRowDom(index)
23930                 );
23931                 proxy.addClass('bg-info info');
23932             }
23933             this.fireEvent("rowselect", this, index, r);
23934             this.fireEvent("selectionchange", this);
23935         }
23936     },
23937
23938     /**
23939      * Deselects a row.
23940      * @param {Number} row The index of the row to deselect
23941      */
23942     deselectRow : function(index, preventViewNotify)
23943     {
23944         if(this.locked) {
23945             return;
23946         }
23947         if(this.last == index){
23948             this.last = false;
23949         }
23950         if(this.lastActive == index){
23951             this.lastActive = false;
23952         }
23953         
23954         var r = this.grid.store.getAt(index);
23955         if (!r) {
23956             return;
23957         }
23958         
23959         this.selections.remove(r);
23960         //.console.log('deselectRow - record id :' + r.id);
23961         if(!preventViewNotify){
23962         
23963             var proxy = new Roo.Element(
23964                 this.grid.getRowDom(index)
23965             );
23966             proxy.removeClass('bg-info info');
23967         }
23968         this.fireEvent("rowdeselect", this, index);
23969         this.fireEvent("selectionchange", this);
23970     },
23971
23972     // private
23973     restoreLast : function(){
23974         if(this._last){
23975             this.last = this._last;
23976         }
23977     },
23978
23979     // private
23980     acceptsNav : function(row, col, cm){
23981         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23982     },
23983
23984     // private
23985     onEditorKey : function(field, e){
23986         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23987         if(k == e.TAB){
23988             e.stopEvent();
23989             ed.completeEdit();
23990             if(e.shiftKey){
23991                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23992             }else{
23993                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23994             }
23995         }else if(k == e.ENTER && !e.ctrlKey){
23996             e.stopEvent();
23997             ed.completeEdit();
23998             if(e.shiftKey){
23999                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24000             }else{
24001                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24002             }
24003         }else if(k == e.ESC){
24004             ed.cancelEdit();
24005         }
24006         if(newCell){
24007             g.startEditing(newCell[0], newCell[1]);
24008         }
24009     }
24010 });
24011 /*
24012  * Based on:
24013  * Ext JS Library 1.1.1
24014  * Copyright(c) 2006-2007, Ext JS, LLC.
24015  *
24016  * Originally Released Under LGPL - original licence link has changed is not relivant.
24017  *
24018  * Fork - LGPL
24019  * <script type="text/javascript">
24020  */
24021  
24022 /**
24023  * @class Roo.bootstrap.PagingToolbar
24024  * @extends Roo.bootstrap.NavSimplebar
24025  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24026  * @constructor
24027  * Create a new PagingToolbar
24028  * @param {Object} config The config object
24029  * @param {Roo.data.Store} store
24030  */
24031 Roo.bootstrap.PagingToolbar = function(config)
24032 {
24033     // old args format still supported... - xtype is prefered..
24034         // created from xtype...
24035     
24036     this.ds = config.dataSource;
24037     
24038     if (config.store && !this.ds) {
24039         this.store= Roo.factory(config.store, Roo.data);
24040         this.ds = this.store;
24041         this.ds.xmodule = this.xmodule || false;
24042     }
24043     
24044     this.toolbarItems = [];
24045     if (config.items) {
24046         this.toolbarItems = config.items;
24047     }
24048     
24049     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24050     
24051     this.cursor = 0;
24052     
24053     if (this.ds) { 
24054         this.bind(this.ds);
24055     }
24056     
24057     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24058     
24059 };
24060
24061 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24062     /**
24063      * @cfg {Roo.data.Store} dataSource
24064      * The underlying data store providing the paged data
24065      */
24066     /**
24067      * @cfg {String/HTMLElement/Element} container
24068      * container The id or element that will contain the toolbar
24069      */
24070     /**
24071      * @cfg {Boolean} displayInfo
24072      * True to display the displayMsg (defaults to false)
24073      */
24074     /**
24075      * @cfg {Number} pageSize
24076      * The number of records to display per page (defaults to 20)
24077      */
24078     pageSize: 20,
24079     /**
24080      * @cfg {String} displayMsg
24081      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24082      */
24083     displayMsg : 'Displaying {0} - {1} of {2}',
24084     /**
24085      * @cfg {String} emptyMsg
24086      * The message to display when no records are found (defaults to "No data to display")
24087      */
24088     emptyMsg : 'No data to display',
24089     /**
24090      * Customizable piece of the default paging text (defaults to "Page")
24091      * @type String
24092      */
24093     beforePageText : "Page",
24094     /**
24095      * Customizable piece of the default paging text (defaults to "of %0")
24096      * @type String
24097      */
24098     afterPageText : "of {0}",
24099     /**
24100      * Customizable piece of the default paging text (defaults to "First Page")
24101      * @type String
24102      */
24103     firstText : "First Page",
24104     /**
24105      * Customizable piece of the default paging text (defaults to "Previous Page")
24106      * @type String
24107      */
24108     prevText : "Previous Page",
24109     /**
24110      * Customizable piece of the default paging text (defaults to "Next Page")
24111      * @type String
24112      */
24113     nextText : "Next Page",
24114     /**
24115      * Customizable piece of the default paging text (defaults to "Last Page")
24116      * @type String
24117      */
24118     lastText : "Last Page",
24119     /**
24120      * Customizable piece of the default paging text (defaults to "Refresh")
24121      * @type String
24122      */
24123     refreshText : "Refresh",
24124
24125     buttons : false,
24126     // private
24127     onRender : function(ct, position) 
24128     {
24129         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24130         this.navgroup.parentId = this.id;
24131         this.navgroup.onRender(this.el, null);
24132         // add the buttons to the navgroup
24133         
24134         if(this.displayInfo){
24135             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24136             this.displayEl = this.el.select('.x-paging-info', true).first();
24137 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24138 //            this.displayEl = navel.el.select('span',true).first();
24139         }
24140         
24141         var _this = this;
24142         
24143         if(this.buttons){
24144             Roo.each(_this.buttons, function(e){ // this might need to use render????
24145                Roo.factory(e).onRender(_this.el, null);
24146             });
24147         }
24148             
24149         Roo.each(_this.toolbarItems, function(e) {
24150             _this.navgroup.addItem(e);
24151         });
24152         
24153         
24154         this.first = this.navgroup.addItem({
24155             tooltip: this.firstText,
24156             cls: "prev",
24157             icon : 'fa fa-backward',
24158             disabled: true,
24159             preventDefault: true,
24160             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24161         });
24162         
24163         this.prev =  this.navgroup.addItem({
24164             tooltip: this.prevText,
24165             cls: "prev",
24166             icon : 'fa fa-step-backward',
24167             disabled: true,
24168             preventDefault: true,
24169             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24170         });
24171     //this.addSeparator();
24172         
24173         
24174         var field = this.navgroup.addItem( {
24175             tagtype : 'span',
24176             cls : 'x-paging-position',
24177             
24178             html : this.beforePageText  +
24179                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24180                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24181          } ); //?? escaped?
24182         
24183         this.field = field.el.select('input', true).first();
24184         this.field.on("keydown", this.onPagingKeydown, this);
24185         this.field.on("focus", function(){this.dom.select();});
24186     
24187     
24188         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24189         //this.field.setHeight(18);
24190         //this.addSeparator();
24191         this.next = this.navgroup.addItem({
24192             tooltip: this.nextText,
24193             cls: "next",
24194             html : ' <i class="fa fa-step-forward">',
24195             disabled: true,
24196             preventDefault: true,
24197             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24198         });
24199         this.last = this.navgroup.addItem({
24200             tooltip: this.lastText,
24201             icon : 'fa fa-forward',
24202             cls: "next",
24203             disabled: true,
24204             preventDefault: true,
24205             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24206         });
24207     //this.addSeparator();
24208         this.loading = this.navgroup.addItem({
24209             tooltip: this.refreshText,
24210             icon: 'fa fa-refresh',
24211             preventDefault: true,
24212             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24213         });
24214         
24215     },
24216
24217     // private
24218     updateInfo : function(){
24219         if(this.displayEl){
24220             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24221             var msg = count == 0 ?
24222                 this.emptyMsg :
24223                 String.format(
24224                     this.displayMsg,
24225                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24226                 );
24227             this.displayEl.update(msg);
24228         }
24229     },
24230
24231     // private
24232     onLoad : function(ds, r, o)
24233     {
24234         this.cursor = o.params ? o.params.start : 0;
24235         var d = this.getPageData(),
24236             ap = d.activePage,
24237             ps = d.pages;
24238         
24239         
24240         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24241         this.field.dom.value = ap;
24242         this.first.setDisabled(ap == 1);
24243         this.prev.setDisabled(ap == 1);
24244         this.next.setDisabled(ap == ps);
24245         this.last.setDisabled(ap == ps);
24246         this.loading.enable();
24247         this.updateInfo();
24248     },
24249
24250     // private
24251     getPageData : function(){
24252         var total = this.ds.getTotalCount();
24253         return {
24254             total : total,
24255             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24256             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24257         };
24258     },
24259
24260     // private
24261     onLoadError : function(){
24262         this.loading.enable();
24263     },
24264
24265     // private
24266     onPagingKeydown : function(e){
24267         var k = e.getKey();
24268         var d = this.getPageData();
24269         if(k == e.RETURN){
24270             var v = this.field.dom.value, pageNum;
24271             if(!v || isNaN(pageNum = parseInt(v, 10))){
24272                 this.field.dom.value = d.activePage;
24273                 return;
24274             }
24275             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24276             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24277             e.stopEvent();
24278         }
24279         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))
24280         {
24281           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24282           this.field.dom.value = pageNum;
24283           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24284           e.stopEvent();
24285         }
24286         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24287         {
24288           var v = this.field.dom.value, pageNum; 
24289           var increment = (e.shiftKey) ? 10 : 1;
24290           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24291                 increment *= -1;
24292           }
24293           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24294             this.field.dom.value = d.activePage;
24295             return;
24296           }
24297           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24298           {
24299             this.field.dom.value = parseInt(v, 10) + increment;
24300             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24301             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24302           }
24303           e.stopEvent();
24304         }
24305     },
24306
24307     // private
24308     beforeLoad : function(){
24309         if(this.loading){
24310             this.loading.disable();
24311         }
24312     },
24313
24314     // private
24315     onClick : function(which){
24316         
24317         var ds = this.ds;
24318         if (!ds) {
24319             return;
24320         }
24321         
24322         switch(which){
24323             case "first":
24324                 ds.load({params:{start: 0, limit: this.pageSize}});
24325             break;
24326             case "prev":
24327                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24328             break;
24329             case "next":
24330                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24331             break;
24332             case "last":
24333                 var total = ds.getTotalCount();
24334                 var extra = total % this.pageSize;
24335                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24336                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24337             break;
24338             case "refresh":
24339                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24340             break;
24341         }
24342     },
24343
24344     /**
24345      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24346      * @param {Roo.data.Store} store The data store to unbind
24347      */
24348     unbind : function(ds){
24349         ds.un("beforeload", this.beforeLoad, this);
24350         ds.un("load", this.onLoad, this);
24351         ds.un("loadexception", this.onLoadError, this);
24352         ds.un("remove", this.updateInfo, this);
24353         ds.un("add", this.updateInfo, this);
24354         this.ds = undefined;
24355     },
24356
24357     /**
24358      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24359      * @param {Roo.data.Store} store The data store to bind
24360      */
24361     bind : function(ds){
24362         ds.on("beforeload", this.beforeLoad, this);
24363         ds.on("load", this.onLoad, this);
24364         ds.on("loadexception", this.onLoadError, this);
24365         ds.on("remove", this.updateInfo, this);
24366         ds.on("add", this.updateInfo, this);
24367         this.ds = ds;
24368     }
24369 });/*
24370  * - LGPL
24371  *
24372  * element
24373  * 
24374  */
24375
24376 /**
24377  * @class Roo.bootstrap.MessageBar
24378  * @extends Roo.bootstrap.Component
24379  * Bootstrap MessageBar class
24380  * @cfg {String} html contents of the MessageBar
24381  * @cfg {String} weight (info | success | warning | danger) default info
24382  * @cfg {String} beforeClass insert the bar before the given class
24383  * @cfg {Boolean} closable (true | false) default false
24384  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24385  * 
24386  * @constructor
24387  * Create a new Element
24388  * @param {Object} config The config object
24389  */
24390
24391 Roo.bootstrap.MessageBar = function(config){
24392     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24393 };
24394
24395 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24396     
24397     html: '',
24398     weight: 'info',
24399     closable: false,
24400     fixed: false,
24401     beforeClass: 'bootstrap-sticky-wrap',
24402     
24403     getAutoCreate : function(){
24404         
24405         var cfg = {
24406             tag: 'div',
24407             cls: 'alert alert-dismissable alert-' + this.weight,
24408             cn: [
24409                 {
24410                     tag: 'span',
24411                     cls: 'message',
24412                     html: this.html || ''
24413                 }
24414             ]
24415         };
24416         
24417         if(this.fixed){
24418             cfg.cls += ' alert-messages-fixed';
24419         }
24420         
24421         if(this.closable){
24422             cfg.cn.push({
24423                 tag: 'button',
24424                 cls: 'close',
24425                 html: 'x'
24426             });
24427         }
24428         
24429         return cfg;
24430     },
24431     
24432     onRender : function(ct, position)
24433     {
24434         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24435         
24436         if(!this.el){
24437             var cfg = Roo.apply({},  this.getAutoCreate());
24438             cfg.id = Roo.id();
24439             
24440             if (this.cls) {
24441                 cfg.cls += ' ' + this.cls;
24442             }
24443             if (this.style) {
24444                 cfg.style = this.style;
24445             }
24446             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24447             
24448             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24449         }
24450         
24451         this.el.select('>button.close').on('click', this.hide, this);
24452         
24453     },
24454     
24455     show : function()
24456     {
24457         if (!this.rendered) {
24458             this.render();
24459         }
24460         
24461         this.el.show();
24462         
24463         this.fireEvent('show', this);
24464         
24465     },
24466     
24467     hide : function()
24468     {
24469         if (!this.rendered) {
24470             this.render();
24471         }
24472         
24473         this.el.hide();
24474         
24475         this.fireEvent('hide', this);
24476     },
24477     
24478     update : function()
24479     {
24480 //        var e = this.el.dom.firstChild;
24481 //        
24482 //        if(this.closable){
24483 //            e = e.nextSibling;
24484 //        }
24485 //        
24486 //        e.data = this.html || '';
24487
24488         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24489     }
24490    
24491 });
24492
24493  
24494
24495      /*
24496  * - LGPL
24497  *
24498  * Graph
24499  * 
24500  */
24501
24502
24503 /**
24504  * @class Roo.bootstrap.Graph
24505  * @extends Roo.bootstrap.Component
24506  * Bootstrap Graph class
24507 > Prameters
24508  -sm {number} sm 4
24509  -md {number} md 5
24510  @cfg {String} graphtype  bar | vbar | pie
24511  @cfg {number} g_x coodinator | centre x (pie)
24512  @cfg {number} g_y coodinator | centre y (pie)
24513  @cfg {number} g_r radius (pie)
24514  @cfg {number} g_height height of the chart (respected by all elements in the set)
24515  @cfg {number} g_width width of the chart (respected by all elements in the set)
24516  @cfg {Object} title The title of the chart
24517     
24518  -{Array}  values
24519  -opts (object) options for the chart 
24520      o {
24521      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24522      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24523      o vgutter (number)
24524      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.
24525      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24526      o to
24527      o stretch (boolean)
24528      o }
24529  -opts (object) options for the pie
24530      o{
24531      o cut
24532      o startAngle (number)
24533      o endAngle (number)
24534      } 
24535  *
24536  * @constructor
24537  * Create a new Input
24538  * @param {Object} config The config object
24539  */
24540
24541 Roo.bootstrap.Graph = function(config){
24542     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24543     
24544     this.addEvents({
24545         // img events
24546         /**
24547          * @event click
24548          * The img click event for the img.
24549          * @param {Roo.EventObject} e
24550          */
24551         "click" : true
24552     });
24553 };
24554
24555 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24556     
24557     sm: 4,
24558     md: 5,
24559     graphtype: 'bar',
24560     g_height: 250,
24561     g_width: 400,
24562     g_x: 50,
24563     g_y: 50,
24564     g_r: 30,
24565     opts:{
24566         //g_colors: this.colors,
24567         g_type: 'soft',
24568         g_gutter: '20%'
24569
24570     },
24571     title : false,
24572
24573     getAutoCreate : function(){
24574         
24575         var cfg = {
24576             tag: 'div',
24577             html : null
24578         };
24579         
24580         
24581         return  cfg;
24582     },
24583
24584     onRender : function(ct,position){
24585         
24586         
24587         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24588         
24589         if (typeof(Raphael) == 'undefined') {
24590             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24591             return;
24592         }
24593         
24594         this.raphael = Raphael(this.el.dom);
24595         
24596                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24597                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24598                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24599                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24600                 /*
24601                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24602                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24603                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24604                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24605                 
24606                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24607                 r.barchart(330, 10, 300, 220, data1);
24608                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24609                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24610                 */
24611                 
24612                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24613                 // r.barchart(30, 30, 560, 250,  xdata, {
24614                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24615                 //     axis : "0 0 1 1",
24616                 //     axisxlabels :  xdata
24617                 //     //yvalues : cols,
24618                    
24619                 // });
24620 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24621 //        
24622 //        this.load(null,xdata,{
24623 //                axis : "0 0 1 1",
24624 //                axisxlabels :  xdata
24625 //                });
24626
24627     },
24628
24629     load : function(graphtype,xdata,opts)
24630     {
24631         this.raphael.clear();
24632         if(!graphtype) {
24633             graphtype = this.graphtype;
24634         }
24635         if(!opts){
24636             opts = this.opts;
24637         }
24638         var r = this.raphael,
24639             fin = function () {
24640                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24641             },
24642             fout = function () {
24643                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24644             },
24645             pfin = function() {
24646                 this.sector.stop();
24647                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24648
24649                 if (this.label) {
24650                     this.label[0].stop();
24651                     this.label[0].attr({ r: 7.5 });
24652                     this.label[1].attr({ "font-weight": 800 });
24653                 }
24654             },
24655             pfout = function() {
24656                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24657
24658                 if (this.label) {
24659                     this.label[0].animate({ r: 5 }, 500, "bounce");
24660                     this.label[1].attr({ "font-weight": 400 });
24661                 }
24662             };
24663
24664         switch(graphtype){
24665             case 'bar':
24666                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24667                 break;
24668             case 'hbar':
24669                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24670                 break;
24671             case 'pie':
24672 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24673 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24674 //            
24675                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24676                 
24677                 break;
24678
24679         }
24680         
24681         if(this.title){
24682             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24683         }
24684         
24685     },
24686     
24687     setTitle: function(o)
24688     {
24689         this.title = o;
24690     },
24691     
24692     initEvents: function() {
24693         
24694         if(!this.href){
24695             this.el.on('click', this.onClick, this);
24696         }
24697     },
24698     
24699     onClick : function(e)
24700     {
24701         Roo.log('img onclick');
24702         this.fireEvent('click', this, e);
24703     }
24704    
24705 });
24706
24707  
24708 /*
24709  * - LGPL
24710  *
24711  * numberBox
24712  * 
24713  */
24714 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24715
24716 /**
24717  * @class Roo.bootstrap.dash.NumberBox
24718  * @extends Roo.bootstrap.Component
24719  * Bootstrap NumberBox class
24720  * @cfg {String} headline Box headline
24721  * @cfg {String} content Box content
24722  * @cfg {String} icon Box icon
24723  * @cfg {String} footer Footer text
24724  * @cfg {String} fhref Footer href
24725  * 
24726  * @constructor
24727  * Create a new NumberBox
24728  * @param {Object} config The config object
24729  */
24730
24731
24732 Roo.bootstrap.dash.NumberBox = function(config){
24733     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24734     
24735 };
24736
24737 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24738     
24739     headline : '',
24740     content : '',
24741     icon : '',
24742     footer : '',
24743     fhref : '',
24744     ficon : '',
24745     
24746     getAutoCreate : function(){
24747         
24748         var cfg = {
24749             tag : 'div',
24750             cls : 'small-box ',
24751             cn : [
24752                 {
24753                     tag : 'div',
24754                     cls : 'inner',
24755                     cn :[
24756                         {
24757                             tag : 'h3',
24758                             cls : 'roo-headline',
24759                             html : this.headline
24760                         },
24761                         {
24762                             tag : 'p',
24763                             cls : 'roo-content',
24764                             html : this.content
24765                         }
24766                     ]
24767                 }
24768             ]
24769         };
24770         
24771         if(this.icon){
24772             cfg.cn.push({
24773                 tag : 'div',
24774                 cls : 'icon',
24775                 cn :[
24776                     {
24777                         tag : 'i',
24778                         cls : 'ion ' + this.icon
24779                     }
24780                 ]
24781             });
24782         }
24783         
24784         if(this.footer){
24785             var footer = {
24786                 tag : 'a',
24787                 cls : 'small-box-footer',
24788                 href : this.fhref || '#',
24789                 html : this.footer
24790             };
24791             
24792             cfg.cn.push(footer);
24793             
24794         }
24795         
24796         return  cfg;
24797     },
24798
24799     onRender : function(ct,position){
24800         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24801
24802
24803        
24804                 
24805     },
24806
24807     setHeadline: function (value)
24808     {
24809         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24810     },
24811     
24812     setFooter: function (value, href)
24813     {
24814         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24815         
24816         if(href){
24817             this.el.select('a.small-box-footer',true).first().attr('href', href);
24818         }
24819         
24820     },
24821
24822     setContent: function (value)
24823     {
24824         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24825     },
24826
24827     initEvents: function() 
24828     {   
24829         
24830     }
24831     
24832 });
24833
24834  
24835 /*
24836  * - LGPL
24837  *
24838  * TabBox
24839  * 
24840  */
24841 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24842
24843 /**
24844  * @class Roo.bootstrap.dash.TabBox
24845  * @extends Roo.bootstrap.Component
24846  * Bootstrap TabBox class
24847  * @cfg {String} title Title of the TabBox
24848  * @cfg {String} icon Icon of the TabBox
24849  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24850  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24851  * 
24852  * @constructor
24853  * Create a new TabBox
24854  * @param {Object} config The config object
24855  */
24856
24857
24858 Roo.bootstrap.dash.TabBox = function(config){
24859     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24860     this.addEvents({
24861         // raw events
24862         /**
24863          * @event addpane
24864          * When a pane is added
24865          * @param {Roo.bootstrap.dash.TabPane} pane
24866          */
24867         "addpane" : true,
24868         /**
24869          * @event activatepane
24870          * When a pane is activated
24871          * @param {Roo.bootstrap.dash.TabPane} pane
24872          */
24873         "activatepane" : true
24874         
24875          
24876     });
24877     
24878     this.panes = [];
24879 };
24880
24881 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24882
24883     title : '',
24884     icon : false,
24885     showtabs : true,
24886     tabScrollable : false,
24887     
24888     getChildContainer : function()
24889     {
24890         return this.el.select('.tab-content', true).first();
24891     },
24892     
24893     getAutoCreate : function(){
24894         
24895         var header = {
24896             tag: 'li',
24897             cls: 'pull-left header',
24898             html: this.title,
24899             cn : []
24900         };
24901         
24902         if(this.icon){
24903             header.cn.push({
24904                 tag: 'i',
24905                 cls: 'fa ' + this.icon
24906             });
24907         }
24908         
24909         var h = {
24910             tag: 'ul',
24911             cls: 'nav nav-tabs pull-right',
24912             cn: [
24913                 header
24914             ]
24915         };
24916         
24917         if(this.tabScrollable){
24918             h = {
24919                 tag: 'div',
24920                 cls: 'tab-header',
24921                 cn: [
24922                     {
24923                         tag: 'ul',
24924                         cls: 'nav nav-tabs pull-right',
24925                         cn: [
24926                             header
24927                         ]
24928                     }
24929                 ]
24930             };
24931         }
24932         
24933         var cfg = {
24934             tag: 'div',
24935             cls: 'nav-tabs-custom',
24936             cn: [
24937                 h,
24938                 {
24939                     tag: 'div',
24940                     cls: 'tab-content no-padding',
24941                     cn: []
24942                 }
24943             ]
24944         };
24945
24946         return  cfg;
24947     },
24948     initEvents : function()
24949     {
24950         //Roo.log('add add pane handler');
24951         this.on('addpane', this.onAddPane, this);
24952     },
24953      /**
24954      * Updates the box title
24955      * @param {String} html to set the title to.
24956      */
24957     setTitle : function(value)
24958     {
24959         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24960     },
24961     onAddPane : function(pane)
24962     {
24963         this.panes.push(pane);
24964         //Roo.log('addpane');
24965         //Roo.log(pane);
24966         // tabs are rendere left to right..
24967         if(!this.showtabs){
24968             return;
24969         }
24970         
24971         var ctr = this.el.select('.nav-tabs', true).first();
24972          
24973          
24974         var existing = ctr.select('.nav-tab',true);
24975         var qty = existing.getCount();;
24976         
24977         
24978         var tab = ctr.createChild({
24979             tag : 'li',
24980             cls : 'nav-tab' + (qty ? '' : ' active'),
24981             cn : [
24982                 {
24983                     tag : 'a',
24984                     href:'#',
24985                     html : pane.title
24986                 }
24987             ]
24988         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24989         pane.tab = tab;
24990         
24991         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24992         if (!qty) {
24993             pane.el.addClass('active');
24994         }
24995         
24996                 
24997     },
24998     onTabClick : function(ev,un,ob,pane)
24999     {
25000         //Roo.log('tab - prev default');
25001         ev.preventDefault();
25002         
25003         
25004         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25005         pane.tab.addClass('active');
25006         //Roo.log(pane.title);
25007         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25008         // technically we should have a deactivate event.. but maybe add later.
25009         // and it should not de-activate the selected tab...
25010         this.fireEvent('activatepane', pane);
25011         pane.el.addClass('active');
25012         pane.fireEvent('activate');
25013         
25014         
25015     },
25016     
25017     getActivePane : function()
25018     {
25019         var r = false;
25020         Roo.each(this.panes, function(p) {
25021             if(p.el.hasClass('active')){
25022                 r = p;
25023                 return false;
25024             }
25025             
25026             return;
25027         });
25028         
25029         return r;
25030     }
25031     
25032     
25033 });
25034
25035  
25036 /*
25037  * - LGPL
25038  *
25039  * Tab pane
25040  * 
25041  */
25042 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25043 /**
25044  * @class Roo.bootstrap.TabPane
25045  * @extends Roo.bootstrap.Component
25046  * Bootstrap TabPane class
25047  * @cfg {Boolean} active (false | true) Default false
25048  * @cfg {String} title title of panel
25049
25050  * 
25051  * @constructor
25052  * Create a new TabPane
25053  * @param {Object} config The config object
25054  */
25055
25056 Roo.bootstrap.dash.TabPane = function(config){
25057     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25058     
25059     this.addEvents({
25060         // raw events
25061         /**
25062          * @event activate
25063          * When a pane is activated
25064          * @param {Roo.bootstrap.dash.TabPane} pane
25065          */
25066         "activate" : true
25067          
25068     });
25069 };
25070
25071 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25072     
25073     active : false,
25074     title : '',
25075     
25076     // the tabBox that this is attached to.
25077     tab : false,
25078      
25079     getAutoCreate : function() 
25080     {
25081         var cfg = {
25082             tag: 'div',
25083             cls: 'tab-pane'
25084         };
25085         
25086         if(this.active){
25087             cfg.cls += ' active';
25088         }
25089         
25090         return cfg;
25091     },
25092     initEvents  : function()
25093     {
25094         //Roo.log('trigger add pane handler');
25095         this.parent().fireEvent('addpane', this)
25096     },
25097     
25098      /**
25099      * Updates the tab title 
25100      * @param {String} html to set the title to.
25101      */
25102     setTitle: function(str)
25103     {
25104         if (!this.tab) {
25105             return;
25106         }
25107         this.title = str;
25108         this.tab.select('a', true).first().dom.innerHTML = str;
25109         
25110     }
25111     
25112     
25113     
25114 });
25115
25116  
25117
25118
25119  /*
25120  * - LGPL
25121  *
25122  * menu
25123  * 
25124  */
25125 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25126
25127 /**
25128  * @class Roo.bootstrap.menu.Menu
25129  * @extends Roo.bootstrap.Component
25130  * Bootstrap Menu class - container for Menu
25131  * @cfg {String} html Text of the menu
25132  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25133  * @cfg {String} icon Font awesome icon
25134  * @cfg {String} pos Menu align to (top | bottom) default bottom
25135  * 
25136  * 
25137  * @constructor
25138  * Create a new Menu
25139  * @param {Object} config The config object
25140  */
25141
25142
25143 Roo.bootstrap.menu.Menu = function(config){
25144     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25145     
25146     this.addEvents({
25147         /**
25148          * @event beforeshow
25149          * Fires before this menu is displayed
25150          * @param {Roo.bootstrap.menu.Menu} this
25151          */
25152         beforeshow : true,
25153         /**
25154          * @event beforehide
25155          * Fires before this menu is hidden
25156          * @param {Roo.bootstrap.menu.Menu} this
25157          */
25158         beforehide : true,
25159         /**
25160          * @event show
25161          * Fires after this menu is displayed
25162          * @param {Roo.bootstrap.menu.Menu} this
25163          */
25164         show : true,
25165         /**
25166          * @event hide
25167          * Fires after this menu is hidden
25168          * @param {Roo.bootstrap.menu.Menu} this
25169          */
25170         hide : true,
25171         /**
25172          * @event click
25173          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25174          * @param {Roo.bootstrap.menu.Menu} this
25175          * @param {Roo.EventObject} e
25176          */
25177         click : true
25178     });
25179     
25180 };
25181
25182 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25183     
25184     submenu : false,
25185     html : '',
25186     weight : 'default',
25187     icon : false,
25188     pos : 'bottom',
25189     
25190     
25191     getChildContainer : function() {
25192         if(this.isSubMenu){
25193             return this.el;
25194         }
25195         
25196         return this.el.select('ul.dropdown-menu', true).first();  
25197     },
25198     
25199     getAutoCreate : function()
25200     {
25201         var text = [
25202             {
25203                 tag : 'span',
25204                 cls : 'roo-menu-text',
25205                 html : this.html
25206             }
25207         ];
25208         
25209         if(this.icon){
25210             text.unshift({
25211                 tag : 'i',
25212                 cls : 'fa ' + this.icon
25213             })
25214         }
25215         
25216         
25217         var cfg = {
25218             tag : 'div',
25219             cls : 'btn-group',
25220             cn : [
25221                 {
25222                     tag : 'button',
25223                     cls : 'dropdown-button btn btn-' + this.weight,
25224                     cn : text
25225                 },
25226                 {
25227                     tag : 'button',
25228                     cls : 'dropdown-toggle btn btn-' + this.weight,
25229                     cn : [
25230                         {
25231                             tag : 'span',
25232                             cls : 'caret'
25233                         }
25234                     ]
25235                 },
25236                 {
25237                     tag : 'ul',
25238                     cls : 'dropdown-menu'
25239                 }
25240             ]
25241             
25242         };
25243         
25244         if(this.pos == 'top'){
25245             cfg.cls += ' dropup';
25246         }
25247         
25248         if(this.isSubMenu){
25249             cfg = {
25250                 tag : 'ul',
25251                 cls : 'dropdown-menu'
25252             }
25253         }
25254         
25255         return cfg;
25256     },
25257     
25258     onRender : function(ct, position)
25259     {
25260         this.isSubMenu = ct.hasClass('dropdown-submenu');
25261         
25262         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25263     },
25264     
25265     initEvents : function() 
25266     {
25267         if(this.isSubMenu){
25268             return;
25269         }
25270         
25271         this.hidden = true;
25272         
25273         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25274         this.triggerEl.on('click', this.onTriggerPress, this);
25275         
25276         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25277         this.buttonEl.on('click', this.onClick, this);
25278         
25279     },
25280     
25281     list : function()
25282     {
25283         if(this.isSubMenu){
25284             return this.el;
25285         }
25286         
25287         return this.el.select('ul.dropdown-menu', true).first();
25288     },
25289     
25290     onClick : function(e)
25291     {
25292         this.fireEvent("click", this, e);
25293     },
25294     
25295     onTriggerPress  : function(e)
25296     {   
25297         if (this.isVisible()) {
25298             this.hide();
25299         } else {
25300             this.show();
25301         }
25302     },
25303     
25304     isVisible : function(){
25305         return !this.hidden;
25306     },
25307     
25308     show : function()
25309     {
25310         this.fireEvent("beforeshow", this);
25311         
25312         this.hidden = false;
25313         this.el.addClass('open');
25314         
25315         Roo.get(document).on("mouseup", this.onMouseUp, this);
25316         
25317         this.fireEvent("show", this);
25318         
25319         
25320     },
25321     
25322     hide : function()
25323     {
25324         this.fireEvent("beforehide", this);
25325         
25326         this.hidden = true;
25327         this.el.removeClass('open');
25328         
25329         Roo.get(document).un("mouseup", this.onMouseUp);
25330         
25331         this.fireEvent("hide", this);
25332     },
25333     
25334     onMouseUp : function()
25335     {
25336         this.hide();
25337     }
25338     
25339 });
25340
25341  
25342  /*
25343  * - LGPL
25344  *
25345  * menu item
25346  * 
25347  */
25348 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25349
25350 /**
25351  * @class Roo.bootstrap.menu.Item
25352  * @extends Roo.bootstrap.Component
25353  * Bootstrap MenuItem class
25354  * @cfg {Boolean} submenu (true | false) default false
25355  * @cfg {String} html text of the item
25356  * @cfg {String} href the link
25357  * @cfg {Boolean} disable (true | false) default false
25358  * @cfg {Boolean} preventDefault (true | false) default true
25359  * @cfg {String} icon Font awesome icon
25360  * @cfg {String} pos Submenu align to (left | right) default right 
25361  * 
25362  * 
25363  * @constructor
25364  * Create a new Item
25365  * @param {Object} config The config object
25366  */
25367
25368
25369 Roo.bootstrap.menu.Item = function(config){
25370     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25371     this.addEvents({
25372         /**
25373          * @event mouseover
25374          * Fires when the mouse is hovering over this menu
25375          * @param {Roo.bootstrap.menu.Item} this
25376          * @param {Roo.EventObject} e
25377          */
25378         mouseover : true,
25379         /**
25380          * @event mouseout
25381          * Fires when the mouse exits this menu
25382          * @param {Roo.bootstrap.menu.Item} this
25383          * @param {Roo.EventObject} e
25384          */
25385         mouseout : true,
25386         // raw events
25387         /**
25388          * @event click
25389          * The raw click event for the entire grid.
25390          * @param {Roo.EventObject} e
25391          */
25392         click : true
25393     });
25394 };
25395
25396 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25397     
25398     submenu : false,
25399     href : '',
25400     html : '',
25401     preventDefault: true,
25402     disable : false,
25403     icon : false,
25404     pos : 'right',
25405     
25406     getAutoCreate : function()
25407     {
25408         var text = [
25409             {
25410                 tag : 'span',
25411                 cls : 'roo-menu-item-text',
25412                 html : this.html
25413             }
25414         ];
25415         
25416         if(this.icon){
25417             text.unshift({
25418                 tag : 'i',
25419                 cls : 'fa ' + this.icon
25420             })
25421         }
25422         
25423         var cfg = {
25424             tag : 'li',
25425             cn : [
25426                 {
25427                     tag : 'a',
25428                     href : this.href || '#',
25429                     cn : text
25430                 }
25431             ]
25432         };
25433         
25434         if(this.disable){
25435             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25436         }
25437         
25438         if(this.submenu){
25439             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25440             
25441             if(this.pos == 'left'){
25442                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25443             }
25444         }
25445         
25446         return cfg;
25447     },
25448     
25449     initEvents : function() 
25450     {
25451         this.el.on('mouseover', this.onMouseOver, this);
25452         this.el.on('mouseout', this.onMouseOut, this);
25453         
25454         this.el.select('a', true).first().on('click', this.onClick, this);
25455         
25456     },
25457     
25458     onClick : function(e)
25459     {
25460         if(this.preventDefault){
25461             e.preventDefault();
25462         }
25463         
25464         this.fireEvent("click", this, e);
25465     },
25466     
25467     onMouseOver : function(e)
25468     {
25469         if(this.submenu && this.pos == 'left'){
25470             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25471         }
25472         
25473         this.fireEvent("mouseover", this, e);
25474     },
25475     
25476     onMouseOut : function(e)
25477     {
25478         this.fireEvent("mouseout", this, e);
25479     }
25480 });
25481
25482  
25483
25484  /*
25485  * - LGPL
25486  *
25487  * menu separator
25488  * 
25489  */
25490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25491
25492 /**
25493  * @class Roo.bootstrap.menu.Separator
25494  * @extends Roo.bootstrap.Component
25495  * Bootstrap Separator class
25496  * 
25497  * @constructor
25498  * Create a new Separator
25499  * @param {Object} config The config object
25500  */
25501
25502
25503 Roo.bootstrap.menu.Separator = function(config){
25504     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25505 };
25506
25507 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25508     
25509     getAutoCreate : function(){
25510         var cfg = {
25511             tag : 'li',
25512             cls: 'divider'
25513         };
25514         
25515         return cfg;
25516     }
25517    
25518 });
25519
25520  
25521
25522  /*
25523  * - LGPL
25524  *
25525  * Tooltip
25526  * 
25527  */
25528
25529 /**
25530  * @class Roo.bootstrap.Tooltip
25531  * Bootstrap Tooltip class
25532  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25533  * to determine which dom element triggers the tooltip.
25534  * 
25535  * It needs to add support for additional attributes like tooltip-position
25536  * 
25537  * @constructor
25538  * Create a new Toolti
25539  * @param {Object} config The config object
25540  */
25541
25542 Roo.bootstrap.Tooltip = function(config){
25543     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25544     
25545     this.alignment = Roo.bootstrap.Tooltip.alignment;
25546     
25547     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25548         this.alignment = config.alignment;
25549     }
25550     
25551 };
25552
25553 Roo.apply(Roo.bootstrap.Tooltip, {
25554     /**
25555      * @function init initialize tooltip monitoring.
25556      * @static
25557      */
25558     currentEl : false,
25559     currentTip : false,
25560     currentRegion : false,
25561     
25562     //  init : delay?
25563     
25564     init : function()
25565     {
25566         Roo.get(document).on('mouseover', this.enter ,this);
25567         Roo.get(document).on('mouseout', this.leave, this);
25568          
25569         
25570         this.currentTip = new Roo.bootstrap.Tooltip();
25571     },
25572     
25573     enter : function(ev)
25574     {
25575         var dom = ev.getTarget();
25576         
25577         //Roo.log(['enter',dom]);
25578         var el = Roo.fly(dom);
25579         if (this.currentEl) {
25580             //Roo.log(dom);
25581             //Roo.log(this.currentEl);
25582             //Roo.log(this.currentEl.contains(dom));
25583             if (this.currentEl == el) {
25584                 return;
25585             }
25586             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25587                 return;
25588             }
25589
25590         }
25591         
25592         if (this.currentTip.el) {
25593             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25594         }    
25595         //Roo.log(ev);
25596         
25597         if(!el || el.dom == document){
25598             return;
25599         }
25600         
25601         var bindEl = el;
25602         
25603         // you can not look for children, as if el is the body.. then everythign is the child..
25604         if (!el.attr('tooltip')) { //
25605             if (!el.select("[tooltip]").elements.length) {
25606                 return;
25607             }
25608             // is the mouse over this child...?
25609             bindEl = el.select("[tooltip]").first();
25610             var xy = ev.getXY();
25611             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25612                 //Roo.log("not in region.");
25613                 return;
25614             }
25615             //Roo.log("child element over..");
25616             
25617         }
25618         this.currentEl = bindEl;
25619         this.currentTip.bind(bindEl);
25620         this.currentRegion = Roo.lib.Region.getRegion(dom);
25621         this.currentTip.enter();
25622         
25623     },
25624     leave : function(ev)
25625     {
25626         var dom = ev.getTarget();
25627         //Roo.log(['leave',dom]);
25628         if (!this.currentEl) {
25629             return;
25630         }
25631         
25632         
25633         if (dom != this.currentEl.dom) {
25634             return;
25635         }
25636         var xy = ev.getXY();
25637         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25638             return;
25639         }
25640         // only activate leave if mouse cursor is outside... bounding box..
25641         
25642         
25643         
25644         
25645         if (this.currentTip) {
25646             this.currentTip.leave();
25647         }
25648         //Roo.log('clear currentEl');
25649         this.currentEl = false;
25650         
25651         
25652     },
25653     alignment : {
25654         'left' : ['r-l', [-2,0], 'right'],
25655         'right' : ['l-r', [2,0], 'left'],
25656         'bottom' : ['t-b', [0,2], 'top'],
25657         'top' : [ 'b-t', [0,-2], 'bottom']
25658     }
25659     
25660 });
25661
25662
25663 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25664     
25665     
25666     bindEl : false,
25667     
25668     delay : null, // can be { show : 300 , hide: 500}
25669     
25670     timeout : null,
25671     
25672     hoverState : null, //???
25673     
25674     placement : 'bottom', 
25675     
25676     alignment : false,
25677     
25678     getAutoCreate : function(){
25679     
25680         var cfg = {
25681            cls : 'tooltip',
25682            role : 'tooltip',
25683            cn : [
25684                 {
25685                     cls : 'tooltip-arrow'
25686                 },
25687                 {
25688                     cls : 'tooltip-inner'
25689                 }
25690            ]
25691         };
25692         
25693         return cfg;
25694     },
25695     bind : function(el)
25696     {
25697         this.bindEl = el;
25698     },
25699       
25700     
25701     enter : function () {
25702        
25703         if (this.timeout != null) {
25704             clearTimeout(this.timeout);
25705         }
25706         
25707         this.hoverState = 'in';
25708          //Roo.log("enter - show");
25709         if (!this.delay || !this.delay.show) {
25710             this.show();
25711             return;
25712         }
25713         var _t = this;
25714         this.timeout = setTimeout(function () {
25715             if (_t.hoverState == 'in') {
25716                 _t.show();
25717             }
25718         }, this.delay.show);
25719     },
25720     leave : function()
25721     {
25722         clearTimeout(this.timeout);
25723     
25724         this.hoverState = 'out';
25725          if (!this.delay || !this.delay.hide) {
25726             this.hide();
25727             return;
25728         }
25729        
25730         var _t = this;
25731         this.timeout = setTimeout(function () {
25732             //Roo.log("leave - timeout");
25733             
25734             if (_t.hoverState == 'out') {
25735                 _t.hide();
25736                 Roo.bootstrap.Tooltip.currentEl = false;
25737             }
25738         }, delay);
25739     },
25740     
25741     show : function (msg)
25742     {
25743         if (!this.el) {
25744             this.render(document.body);
25745         }
25746         // set content.
25747         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25748         
25749         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25750         
25751         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25752         
25753         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25754         
25755         var placement = typeof this.placement == 'function' ?
25756             this.placement.call(this, this.el, on_el) :
25757             this.placement;
25758             
25759         var autoToken = /\s?auto?\s?/i;
25760         var autoPlace = autoToken.test(placement);
25761         if (autoPlace) {
25762             placement = placement.replace(autoToken, '') || 'top';
25763         }
25764         
25765         //this.el.detach()
25766         //this.el.setXY([0,0]);
25767         this.el.show();
25768         //this.el.dom.style.display='block';
25769         
25770         //this.el.appendTo(on_el);
25771         
25772         var p = this.getPosition();
25773         var box = this.el.getBox();
25774         
25775         if (autoPlace) {
25776             // fixme..
25777         }
25778         
25779         var align = this.alignment[placement];
25780         
25781         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25782         
25783         if(placement == 'top' || placement == 'bottom'){
25784             if(xy[0] < 0){
25785                 placement = 'right';
25786             }
25787             
25788             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25789                 placement = 'left';
25790             }
25791             
25792             var scroll = Roo.select('body', true).first().getScroll();
25793             
25794             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25795                 placement = 'top';
25796             }
25797             
25798         }
25799         
25800         this.el.alignTo(this.bindEl, align[0],align[1]);
25801         //var arrow = this.el.select('.arrow',true).first();
25802         //arrow.set(align[2], 
25803         
25804         this.el.addClass(placement);
25805         
25806         this.el.addClass('in fade');
25807         
25808         this.hoverState = null;
25809         
25810         if (this.el.hasClass('fade')) {
25811             // fade it?
25812         }
25813         
25814     },
25815     hide : function()
25816     {
25817          
25818         if (!this.el) {
25819             return;
25820         }
25821         //this.el.setXY([0,0]);
25822         this.el.removeClass('in');
25823         //this.el.hide();
25824         
25825     }
25826     
25827 });
25828  
25829
25830  /*
25831  * - LGPL
25832  *
25833  * Location Picker
25834  * 
25835  */
25836
25837 /**
25838  * @class Roo.bootstrap.LocationPicker
25839  * @extends Roo.bootstrap.Component
25840  * Bootstrap LocationPicker class
25841  * @cfg {Number} latitude Position when init default 0
25842  * @cfg {Number} longitude Position when init default 0
25843  * @cfg {Number} zoom default 15
25844  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25845  * @cfg {Boolean} mapTypeControl default false
25846  * @cfg {Boolean} disableDoubleClickZoom default false
25847  * @cfg {Boolean} scrollwheel default true
25848  * @cfg {Boolean} streetViewControl default false
25849  * @cfg {Number} radius default 0
25850  * @cfg {String} locationName
25851  * @cfg {Boolean} draggable default true
25852  * @cfg {Boolean} enableAutocomplete default false
25853  * @cfg {Boolean} enableReverseGeocode default true
25854  * @cfg {String} markerTitle
25855  * 
25856  * @constructor
25857  * Create a new LocationPicker
25858  * @param {Object} config The config object
25859  */
25860
25861
25862 Roo.bootstrap.LocationPicker = function(config){
25863     
25864     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25865     
25866     this.addEvents({
25867         /**
25868          * @event initial
25869          * Fires when the picker initialized.
25870          * @param {Roo.bootstrap.LocationPicker} this
25871          * @param {Google Location} location
25872          */
25873         initial : true,
25874         /**
25875          * @event positionchanged
25876          * Fires when the picker position changed.
25877          * @param {Roo.bootstrap.LocationPicker} this
25878          * @param {Google Location} location
25879          */
25880         positionchanged : true,
25881         /**
25882          * @event resize
25883          * Fires when the map resize.
25884          * @param {Roo.bootstrap.LocationPicker} this
25885          */
25886         resize : true,
25887         /**
25888          * @event show
25889          * Fires when the map show.
25890          * @param {Roo.bootstrap.LocationPicker} this
25891          */
25892         show : true,
25893         /**
25894          * @event hide
25895          * Fires when the map hide.
25896          * @param {Roo.bootstrap.LocationPicker} this
25897          */
25898         hide : true,
25899         /**
25900          * @event mapClick
25901          * Fires when click the map.
25902          * @param {Roo.bootstrap.LocationPicker} this
25903          * @param {Map event} e
25904          */
25905         mapClick : true,
25906         /**
25907          * @event mapRightClick
25908          * Fires when right click the map.
25909          * @param {Roo.bootstrap.LocationPicker} this
25910          * @param {Map event} e
25911          */
25912         mapRightClick : true,
25913         /**
25914          * @event markerClick
25915          * Fires when click the marker.
25916          * @param {Roo.bootstrap.LocationPicker} this
25917          * @param {Map event} e
25918          */
25919         markerClick : true,
25920         /**
25921          * @event markerRightClick
25922          * Fires when right click the marker.
25923          * @param {Roo.bootstrap.LocationPicker} this
25924          * @param {Map event} e
25925          */
25926         markerRightClick : true,
25927         /**
25928          * @event OverlayViewDraw
25929          * Fires when OverlayView Draw
25930          * @param {Roo.bootstrap.LocationPicker} this
25931          */
25932         OverlayViewDraw : true,
25933         /**
25934          * @event OverlayViewOnAdd
25935          * Fires when OverlayView Draw
25936          * @param {Roo.bootstrap.LocationPicker} this
25937          */
25938         OverlayViewOnAdd : true,
25939         /**
25940          * @event OverlayViewOnRemove
25941          * Fires when OverlayView Draw
25942          * @param {Roo.bootstrap.LocationPicker} this
25943          */
25944         OverlayViewOnRemove : true,
25945         /**
25946          * @event OverlayViewShow
25947          * Fires when OverlayView Draw
25948          * @param {Roo.bootstrap.LocationPicker} this
25949          * @param {Pixel} cpx
25950          */
25951         OverlayViewShow : true,
25952         /**
25953          * @event OverlayViewHide
25954          * Fires when OverlayView Draw
25955          * @param {Roo.bootstrap.LocationPicker} this
25956          */
25957         OverlayViewHide : true,
25958         /**
25959          * @event loadexception
25960          * Fires when load google lib failed.
25961          * @param {Roo.bootstrap.LocationPicker} this
25962          */
25963         loadexception : true
25964     });
25965         
25966 };
25967
25968 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25969     
25970     gMapContext: false,
25971     
25972     latitude: 0,
25973     longitude: 0,
25974     zoom: 15,
25975     mapTypeId: false,
25976     mapTypeControl: false,
25977     disableDoubleClickZoom: false,
25978     scrollwheel: true,
25979     streetViewControl: false,
25980     radius: 0,
25981     locationName: '',
25982     draggable: true,
25983     enableAutocomplete: false,
25984     enableReverseGeocode: true,
25985     markerTitle: '',
25986     
25987     getAutoCreate: function()
25988     {
25989
25990         var cfg = {
25991             tag: 'div',
25992             cls: 'roo-location-picker'
25993         };
25994         
25995         return cfg
25996     },
25997     
25998     initEvents: function(ct, position)
25999     {       
26000         if(!this.el.getWidth() || this.isApplied()){
26001             return;
26002         }
26003         
26004         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26005         
26006         this.initial();
26007     },
26008     
26009     initial: function()
26010     {
26011         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26012             this.fireEvent('loadexception', this);
26013             return;
26014         }
26015         
26016         if(!this.mapTypeId){
26017             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26018         }
26019         
26020         this.gMapContext = this.GMapContext();
26021         
26022         this.initOverlayView();
26023         
26024         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26025         
26026         var _this = this;
26027                 
26028         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26029             _this.setPosition(_this.gMapContext.marker.position);
26030         });
26031         
26032         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26033             _this.fireEvent('mapClick', this, event);
26034             
26035         });
26036
26037         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26038             _this.fireEvent('mapRightClick', this, event);
26039             
26040         });
26041         
26042         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26043             _this.fireEvent('markerClick', this, event);
26044             
26045         });
26046
26047         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26048             _this.fireEvent('markerRightClick', this, event);
26049             
26050         });
26051         
26052         this.setPosition(this.gMapContext.location);
26053         
26054         this.fireEvent('initial', this, this.gMapContext.location);
26055     },
26056     
26057     initOverlayView: function()
26058     {
26059         var _this = this;
26060         
26061         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26062             
26063             draw: function()
26064             {
26065                 _this.fireEvent('OverlayViewDraw', _this);
26066             },
26067             
26068             onAdd: function()
26069             {
26070                 _this.fireEvent('OverlayViewOnAdd', _this);
26071             },
26072             
26073             onRemove: function()
26074             {
26075                 _this.fireEvent('OverlayViewOnRemove', _this);
26076             },
26077             
26078             show: function(cpx)
26079             {
26080                 _this.fireEvent('OverlayViewShow', _this, cpx);
26081             },
26082             
26083             hide: function()
26084             {
26085                 _this.fireEvent('OverlayViewHide', _this);
26086             }
26087             
26088         });
26089     },
26090     
26091     fromLatLngToContainerPixel: function(event)
26092     {
26093         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26094     },
26095     
26096     isApplied: function() 
26097     {
26098         return this.getGmapContext() == false ? false : true;
26099     },
26100     
26101     getGmapContext: function() 
26102     {
26103         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26104     },
26105     
26106     GMapContext: function() 
26107     {
26108         var position = new google.maps.LatLng(this.latitude, this.longitude);
26109         
26110         var _map = new google.maps.Map(this.el.dom, {
26111             center: position,
26112             zoom: this.zoom,
26113             mapTypeId: this.mapTypeId,
26114             mapTypeControl: this.mapTypeControl,
26115             disableDoubleClickZoom: this.disableDoubleClickZoom,
26116             scrollwheel: this.scrollwheel,
26117             streetViewControl: this.streetViewControl,
26118             locationName: this.locationName,
26119             draggable: this.draggable,
26120             enableAutocomplete: this.enableAutocomplete,
26121             enableReverseGeocode: this.enableReverseGeocode
26122         });
26123         
26124         var _marker = new google.maps.Marker({
26125             position: position,
26126             map: _map,
26127             title: this.markerTitle,
26128             draggable: this.draggable
26129         });
26130         
26131         return {
26132             map: _map,
26133             marker: _marker,
26134             circle: null,
26135             location: position,
26136             radius: this.radius,
26137             locationName: this.locationName,
26138             addressComponents: {
26139                 formatted_address: null,
26140                 addressLine1: null,
26141                 addressLine2: null,
26142                 streetName: null,
26143                 streetNumber: null,
26144                 city: null,
26145                 district: null,
26146                 state: null,
26147                 stateOrProvince: null
26148             },
26149             settings: this,
26150             domContainer: this.el.dom,
26151             geodecoder: new google.maps.Geocoder()
26152         };
26153     },
26154     
26155     drawCircle: function(center, radius, options) 
26156     {
26157         if (this.gMapContext.circle != null) {
26158             this.gMapContext.circle.setMap(null);
26159         }
26160         if (radius > 0) {
26161             radius *= 1;
26162             options = Roo.apply({}, options, {
26163                 strokeColor: "#0000FF",
26164                 strokeOpacity: .35,
26165                 strokeWeight: 2,
26166                 fillColor: "#0000FF",
26167                 fillOpacity: .2
26168             });
26169             
26170             options.map = this.gMapContext.map;
26171             options.radius = radius;
26172             options.center = center;
26173             this.gMapContext.circle = new google.maps.Circle(options);
26174             return this.gMapContext.circle;
26175         }
26176         
26177         return null;
26178     },
26179     
26180     setPosition: function(location) 
26181     {
26182         this.gMapContext.location = location;
26183         this.gMapContext.marker.setPosition(location);
26184         this.gMapContext.map.panTo(location);
26185         this.drawCircle(location, this.gMapContext.radius, {});
26186         
26187         var _this = this;
26188         
26189         if (this.gMapContext.settings.enableReverseGeocode) {
26190             this.gMapContext.geodecoder.geocode({
26191                 latLng: this.gMapContext.location
26192             }, function(results, status) {
26193                 
26194                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26195                     _this.gMapContext.locationName = results[0].formatted_address;
26196                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26197                     
26198                     _this.fireEvent('positionchanged', this, location);
26199                 }
26200             });
26201             
26202             return;
26203         }
26204         
26205         this.fireEvent('positionchanged', this, location);
26206     },
26207     
26208     resize: function()
26209     {
26210         google.maps.event.trigger(this.gMapContext.map, "resize");
26211         
26212         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26213         
26214         this.fireEvent('resize', this);
26215     },
26216     
26217     setPositionByLatLng: function(latitude, longitude)
26218     {
26219         this.setPosition(new google.maps.LatLng(latitude, longitude));
26220     },
26221     
26222     getCurrentPosition: function() 
26223     {
26224         return {
26225             latitude: this.gMapContext.location.lat(),
26226             longitude: this.gMapContext.location.lng()
26227         };
26228     },
26229     
26230     getAddressName: function() 
26231     {
26232         return this.gMapContext.locationName;
26233     },
26234     
26235     getAddressComponents: function() 
26236     {
26237         return this.gMapContext.addressComponents;
26238     },
26239     
26240     address_component_from_google_geocode: function(address_components) 
26241     {
26242         var result = {};
26243         
26244         for (var i = 0; i < address_components.length; i++) {
26245             var component = address_components[i];
26246             if (component.types.indexOf("postal_code") >= 0) {
26247                 result.postalCode = component.short_name;
26248             } else if (component.types.indexOf("street_number") >= 0) {
26249                 result.streetNumber = component.short_name;
26250             } else if (component.types.indexOf("route") >= 0) {
26251                 result.streetName = component.short_name;
26252             } else if (component.types.indexOf("neighborhood") >= 0) {
26253                 result.city = component.short_name;
26254             } else if (component.types.indexOf("locality") >= 0) {
26255                 result.city = component.short_name;
26256             } else if (component.types.indexOf("sublocality") >= 0) {
26257                 result.district = component.short_name;
26258             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26259                 result.stateOrProvince = component.short_name;
26260             } else if (component.types.indexOf("country") >= 0) {
26261                 result.country = component.short_name;
26262             }
26263         }
26264         
26265         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26266         result.addressLine2 = "";
26267         return result;
26268     },
26269     
26270     setZoomLevel: function(zoom)
26271     {
26272         this.gMapContext.map.setZoom(zoom);
26273     },
26274     
26275     show: function()
26276     {
26277         if(!this.el){
26278             return;
26279         }
26280         
26281         this.el.show();
26282         
26283         this.resize();
26284         
26285         this.fireEvent('show', this);
26286     },
26287     
26288     hide: function()
26289     {
26290         if(!this.el){
26291             return;
26292         }
26293         
26294         this.el.hide();
26295         
26296         this.fireEvent('hide', this);
26297     }
26298     
26299 });
26300
26301 Roo.apply(Roo.bootstrap.LocationPicker, {
26302     
26303     OverlayView : function(map, options)
26304     {
26305         options = options || {};
26306         
26307         this.setMap(map);
26308     }
26309     
26310     
26311 });/*
26312  * - LGPL
26313  *
26314  * Alert
26315  * 
26316  */
26317
26318 /**
26319  * @class Roo.bootstrap.Alert
26320  * @extends Roo.bootstrap.Component
26321  * Bootstrap Alert class
26322  * @cfg {String} title The title of alert
26323  * @cfg {String} html The content of alert
26324  * @cfg {String} weight (  success | info | warning | danger )
26325  * @cfg {String} faicon font-awesomeicon
26326  * 
26327  * @constructor
26328  * Create a new alert
26329  * @param {Object} config The config object
26330  */
26331
26332
26333 Roo.bootstrap.Alert = function(config){
26334     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26335     
26336 };
26337
26338 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26339     
26340     title: '',
26341     html: '',
26342     weight: false,
26343     faicon: false,
26344     
26345     getAutoCreate : function()
26346     {
26347         
26348         var cfg = {
26349             tag : 'div',
26350             cls : 'alert',
26351             cn : [
26352                 {
26353                     tag : 'i',
26354                     cls : 'roo-alert-icon'
26355                     
26356                 },
26357                 {
26358                     tag : 'b',
26359                     cls : 'roo-alert-title',
26360                     html : this.title
26361                 },
26362                 {
26363                     tag : 'span',
26364                     cls : 'roo-alert-text',
26365                     html : this.html
26366                 }
26367             ]
26368         };
26369         
26370         if(this.faicon){
26371             cfg.cn[0].cls += ' fa ' + this.faicon;
26372         }
26373         
26374         if(this.weight){
26375             cfg.cls += ' alert-' + this.weight;
26376         }
26377         
26378         return cfg;
26379     },
26380     
26381     initEvents: function() 
26382     {
26383         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26384     },
26385     
26386     setTitle : function(str)
26387     {
26388         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26389     },
26390     
26391     setText : function(str)
26392     {
26393         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26394     },
26395     
26396     setWeight : function(weight)
26397     {
26398         if(this.weight){
26399             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26400         }
26401         
26402         this.weight = weight;
26403         
26404         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26405     },
26406     
26407     setIcon : function(icon)
26408     {
26409         if(this.faicon){
26410             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26411         }
26412         
26413         this.faicon = icon;
26414         
26415         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26416     },
26417     
26418     hide: function() 
26419     {
26420         this.el.hide();   
26421     },
26422     
26423     show: function() 
26424     {  
26425         this.el.show();   
26426     }
26427     
26428 });
26429
26430  
26431 /*
26432 * Licence: LGPL
26433 */
26434
26435 /**
26436  * @class Roo.bootstrap.UploadCropbox
26437  * @extends Roo.bootstrap.Component
26438  * Bootstrap UploadCropbox class
26439  * @cfg {String} emptyText show when image has been loaded
26440  * @cfg {String} rotateNotify show when image too small to rotate
26441  * @cfg {Number} errorTimeout default 3000
26442  * @cfg {Number} minWidth default 300
26443  * @cfg {Number} minHeight default 300
26444  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26445  * @cfg {Boolean} isDocument (true|false) default false
26446  * @cfg {String} url action url
26447  * @cfg {String} paramName default 'imageUpload'
26448  * @cfg {String} method default POST
26449  * @cfg {Boolean} loadMask (true|false) default true
26450  * @cfg {Boolean} loadingText default 'Loading...'
26451  * 
26452  * @constructor
26453  * Create a new UploadCropbox
26454  * @param {Object} config The config object
26455  */
26456
26457 Roo.bootstrap.UploadCropbox = function(config){
26458     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26459     
26460     this.addEvents({
26461         /**
26462          * @event beforeselectfile
26463          * Fire before select file
26464          * @param {Roo.bootstrap.UploadCropbox} this
26465          */
26466         "beforeselectfile" : true,
26467         /**
26468          * @event initial
26469          * Fire after initEvent
26470          * @param {Roo.bootstrap.UploadCropbox} this
26471          */
26472         "initial" : true,
26473         /**
26474          * @event crop
26475          * Fire after initEvent
26476          * @param {Roo.bootstrap.UploadCropbox} this
26477          * @param {String} data
26478          */
26479         "crop" : true,
26480         /**
26481          * @event prepare
26482          * Fire when preparing the file data
26483          * @param {Roo.bootstrap.UploadCropbox} this
26484          * @param {Object} file
26485          */
26486         "prepare" : true,
26487         /**
26488          * @event exception
26489          * Fire when get exception
26490          * @param {Roo.bootstrap.UploadCropbox} this
26491          * @param {XMLHttpRequest} xhr
26492          */
26493         "exception" : true,
26494         /**
26495          * @event beforeloadcanvas
26496          * Fire before load the canvas
26497          * @param {Roo.bootstrap.UploadCropbox} this
26498          * @param {String} src
26499          */
26500         "beforeloadcanvas" : true,
26501         /**
26502          * @event trash
26503          * Fire when trash image
26504          * @param {Roo.bootstrap.UploadCropbox} this
26505          */
26506         "trash" : true,
26507         /**
26508          * @event download
26509          * Fire when download the image
26510          * @param {Roo.bootstrap.UploadCropbox} this
26511          */
26512         "download" : true,
26513         /**
26514          * @event footerbuttonclick
26515          * Fire when footerbuttonclick
26516          * @param {Roo.bootstrap.UploadCropbox} this
26517          * @param {String} type
26518          */
26519         "footerbuttonclick" : true,
26520         /**
26521          * @event resize
26522          * Fire when resize
26523          * @param {Roo.bootstrap.UploadCropbox} this
26524          */
26525         "resize" : true,
26526         /**
26527          * @event rotate
26528          * Fire when rotate the image
26529          * @param {Roo.bootstrap.UploadCropbox} this
26530          * @param {String} pos
26531          */
26532         "rotate" : true,
26533         /**
26534          * @event inspect
26535          * Fire when inspect the file
26536          * @param {Roo.bootstrap.UploadCropbox} this
26537          * @param {Object} file
26538          */
26539         "inspect" : true,
26540         /**
26541          * @event upload
26542          * Fire when xhr upload the file
26543          * @param {Roo.bootstrap.UploadCropbox} this
26544          * @param {Object} data
26545          */
26546         "upload" : true,
26547         /**
26548          * @event arrange
26549          * Fire when arrange the file data
26550          * @param {Roo.bootstrap.UploadCropbox} this
26551          * @param {Object} formData
26552          */
26553         "arrange" : true
26554     });
26555     
26556     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26557 };
26558
26559 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26560     
26561     emptyText : 'Click to upload image',
26562     rotateNotify : 'Image is too small to rotate',
26563     errorTimeout : 3000,
26564     scale : 0,
26565     baseScale : 1,
26566     rotate : 0,
26567     dragable : false,
26568     pinching : false,
26569     mouseX : 0,
26570     mouseY : 0,
26571     cropData : false,
26572     minWidth : 300,
26573     minHeight : 300,
26574     file : false,
26575     exif : {},
26576     baseRotate : 1,
26577     cropType : 'image/jpeg',
26578     buttons : false,
26579     canvasLoaded : false,
26580     isDocument : false,
26581     method : 'POST',
26582     paramName : 'imageUpload',
26583     loadMask : true,
26584     loadingText : 'Loading...',
26585     maskEl : false,
26586     
26587     getAutoCreate : function()
26588     {
26589         var cfg = {
26590             tag : 'div',
26591             cls : 'roo-upload-cropbox',
26592             cn : [
26593                 {
26594                     tag : 'input',
26595                     cls : 'roo-upload-cropbox-selector',
26596                     type : 'file'
26597                 },
26598                 {
26599                     tag : 'div',
26600                     cls : 'roo-upload-cropbox-body',
26601                     style : 'cursor:pointer',
26602                     cn : [
26603                         {
26604                             tag : 'div',
26605                             cls : 'roo-upload-cropbox-preview'
26606                         },
26607                         {
26608                             tag : 'div',
26609                             cls : 'roo-upload-cropbox-thumb'
26610                         },
26611                         {
26612                             tag : 'div',
26613                             cls : 'roo-upload-cropbox-empty-notify',
26614                             html : this.emptyText
26615                         },
26616                         {
26617                             tag : 'div',
26618                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26619                             html : this.rotateNotify
26620                         }
26621                     ]
26622                 },
26623                 {
26624                     tag : 'div',
26625                     cls : 'roo-upload-cropbox-footer',
26626                     cn : {
26627                         tag : 'div',
26628                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26629                         cn : []
26630                     }
26631                 }
26632             ]
26633         };
26634         
26635         return cfg;
26636     },
26637     
26638     onRender : function(ct, position)
26639     {
26640         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26641         
26642         if (this.buttons.length) {
26643             
26644             Roo.each(this.buttons, function(bb) {
26645                 
26646                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26647                 
26648                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26649                 
26650             }, this);
26651         }
26652         
26653         if(this.loadMask){
26654             this.maskEl = this.el;
26655         }
26656     },
26657     
26658     initEvents : function()
26659     {
26660         this.urlAPI = (window.createObjectURL && window) || 
26661                                 (window.URL && URL.revokeObjectURL && URL) || 
26662                                 (window.webkitURL && webkitURL);
26663                         
26664         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26665         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26666         
26667         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26668         this.selectorEl.hide();
26669         
26670         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26671         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26672         
26673         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26674         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26675         this.thumbEl.hide();
26676         
26677         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26678         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26679         
26680         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26681         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682         this.errorEl.hide();
26683         
26684         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26685         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686         this.footerEl.hide();
26687         
26688         this.setThumbBoxSize();
26689         
26690         this.bind();
26691         
26692         this.resize();
26693         
26694         this.fireEvent('initial', this);
26695     },
26696
26697     bind : function()
26698     {
26699         var _this = this;
26700         
26701         window.addEventListener("resize", function() { _this.resize(); } );
26702         
26703         this.bodyEl.on('click', this.beforeSelectFile, this);
26704         
26705         if(Roo.isTouch){
26706             this.bodyEl.on('touchstart', this.onTouchStart, this);
26707             this.bodyEl.on('touchmove', this.onTouchMove, this);
26708             this.bodyEl.on('touchend', this.onTouchEnd, this);
26709         }
26710         
26711         if(!Roo.isTouch){
26712             this.bodyEl.on('mousedown', this.onMouseDown, this);
26713             this.bodyEl.on('mousemove', this.onMouseMove, this);
26714             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26715             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26716             Roo.get(document).on('mouseup', this.onMouseUp, this);
26717         }
26718         
26719         this.selectorEl.on('change', this.onFileSelected, this);
26720     },
26721     
26722     reset : function()
26723     {    
26724         this.scale = 0;
26725         this.baseScale = 1;
26726         this.rotate = 0;
26727         this.baseRotate = 1;
26728         this.dragable = false;
26729         this.pinching = false;
26730         this.mouseX = 0;
26731         this.mouseY = 0;
26732         this.cropData = false;
26733         this.notifyEl.dom.innerHTML = this.emptyText;
26734         
26735         this.selectorEl.dom.value = '';
26736         
26737     },
26738     
26739     resize : function()
26740     {
26741         if(this.fireEvent('resize', this) != false){
26742             this.setThumbBoxPosition();
26743             this.setCanvasPosition();
26744         }
26745     },
26746     
26747     onFooterButtonClick : function(e, el, o, type)
26748     {
26749         switch (type) {
26750             case 'rotate-left' :
26751                 this.onRotateLeft(e);
26752                 break;
26753             case 'rotate-right' :
26754                 this.onRotateRight(e);
26755                 break;
26756             case 'picture' :
26757                 this.beforeSelectFile(e);
26758                 break;
26759             case 'trash' :
26760                 this.trash(e);
26761                 break;
26762             case 'crop' :
26763                 this.crop(e);
26764                 break;
26765             case 'download' :
26766                 this.download(e);
26767                 break;
26768             default :
26769                 break;
26770         }
26771         
26772         this.fireEvent('footerbuttonclick', this, type);
26773     },
26774     
26775     beforeSelectFile : function(e)
26776     {
26777         e.preventDefault();
26778         
26779         if(this.fireEvent('beforeselectfile', this) != false){
26780             this.selectorEl.dom.click();
26781         }
26782     },
26783     
26784     onFileSelected : function(e)
26785     {
26786         e.preventDefault();
26787         
26788         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26789             return;
26790         }
26791         
26792         var file = this.selectorEl.dom.files[0];
26793         
26794         if(this.fireEvent('inspect', this, file) != false){
26795             this.prepare(file);
26796         }
26797         
26798     },
26799     
26800     trash : function(e)
26801     {
26802         this.fireEvent('trash', this);
26803     },
26804     
26805     download : function(e)
26806     {
26807         this.fireEvent('download', this);
26808     },
26809     
26810     loadCanvas : function(src)
26811     {   
26812         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26813             
26814             this.reset();
26815             
26816             this.imageEl = document.createElement('img');
26817             
26818             var _this = this;
26819             
26820             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26821             
26822             this.imageEl.src = src;
26823         }
26824     },
26825     
26826     onLoadCanvas : function()
26827     {   
26828         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26829         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26830         
26831         this.bodyEl.un('click', this.beforeSelectFile, this);
26832         
26833         this.notifyEl.hide();
26834         this.thumbEl.show();
26835         this.footerEl.show();
26836         
26837         this.baseRotateLevel();
26838         
26839         if(this.isDocument){
26840             this.setThumbBoxSize();
26841         }
26842         
26843         this.setThumbBoxPosition();
26844         
26845         this.baseScaleLevel();
26846         
26847         this.draw();
26848         
26849         this.resize();
26850         
26851         this.canvasLoaded = true;
26852         
26853         if(this.loadMask){
26854             this.maskEl.unmask();
26855         }
26856         
26857     },
26858     
26859     setCanvasPosition : function()
26860     {   
26861         if(!this.canvasEl){
26862             return;
26863         }
26864         
26865         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26866         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26867         
26868         this.previewEl.setLeft(pw);
26869         this.previewEl.setTop(ph);
26870         
26871     },
26872     
26873     onMouseDown : function(e)
26874     {   
26875         e.stopEvent();
26876         
26877         this.dragable = true;
26878         this.pinching = false;
26879         
26880         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26881             this.dragable = false;
26882             return;
26883         }
26884         
26885         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26886         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26887         
26888     },
26889     
26890     onMouseMove : function(e)
26891     {   
26892         e.stopEvent();
26893         
26894         if(!this.canvasLoaded){
26895             return;
26896         }
26897         
26898         if (!this.dragable){
26899             return;
26900         }
26901         
26902         var minX = Math.ceil(this.thumbEl.getLeft(true));
26903         var minY = Math.ceil(this.thumbEl.getTop(true));
26904         
26905         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26906         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26907         
26908         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26909         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26910         
26911         x = x - this.mouseX;
26912         y = y - this.mouseY;
26913         
26914         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26915         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26916         
26917         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26918         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26919         
26920         this.previewEl.setLeft(bgX);
26921         this.previewEl.setTop(bgY);
26922         
26923         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26924         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26925     },
26926     
26927     onMouseUp : function(e)
26928     {   
26929         e.stopEvent();
26930         
26931         this.dragable = false;
26932     },
26933     
26934     onMouseWheel : function(e)
26935     {   
26936         e.stopEvent();
26937         
26938         this.startScale = this.scale;
26939         
26940         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26941         
26942         if(!this.zoomable()){
26943             this.scale = this.startScale;
26944             return;
26945         }
26946         
26947         this.draw();
26948         
26949         return;
26950     },
26951     
26952     zoomable : function()
26953     {
26954         var minScale = this.thumbEl.getWidth() / this.minWidth;
26955         
26956         if(this.minWidth < this.minHeight){
26957             minScale = this.thumbEl.getHeight() / this.minHeight;
26958         }
26959         
26960         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26961         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26962         
26963         if(
26964                 this.isDocument &&
26965                 (this.rotate == 0 || this.rotate == 180) && 
26966                 (
26967                     width > this.imageEl.OriginWidth || 
26968                     height > this.imageEl.OriginHeight ||
26969                     (width < this.minWidth && height < this.minHeight)
26970                 )
26971         ){
26972             return false;
26973         }
26974         
26975         if(
26976                 this.isDocument &&
26977                 (this.rotate == 90 || this.rotate == 270) && 
26978                 (
26979                     width > this.imageEl.OriginWidth || 
26980                     height > this.imageEl.OriginHeight ||
26981                     (width < this.minHeight && height < this.minWidth)
26982                 )
26983         ){
26984             return false;
26985         }
26986         
26987         if(
26988                 !this.isDocument &&
26989                 (this.rotate == 0 || this.rotate == 180) && 
26990                 (
26991                     width < this.minWidth || 
26992                     width > this.imageEl.OriginWidth || 
26993                     height < this.minHeight || 
26994                     height > this.imageEl.OriginHeight
26995                 )
26996         ){
26997             return false;
26998         }
26999         
27000         if(
27001                 !this.isDocument &&
27002                 (this.rotate == 90 || this.rotate == 270) && 
27003                 (
27004                     width < this.minHeight || 
27005                     width > this.imageEl.OriginWidth || 
27006                     height < this.minWidth || 
27007                     height > this.imageEl.OriginHeight
27008                 )
27009         ){
27010             return false;
27011         }
27012         
27013         return true;
27014         
27015     },
27016     
27017     onRotateLeft : function(e)
27018     {   
27019         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27020             
27021             var minScale = this.thumbEl.getWidth() / this.minWidth;
27022             
27023             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27024             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27025             
27026             this.startScale = this.scale;
27027             
27028             while (this.getScaleLevel() < minScale){
27029             
27030                 this.scale = this.scale + 1;
27031                 
27032                 if(!this.zoomable()){
27033                     break;
27034                 }
27035                 
27036                 if(
27037                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27038                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27039                 ){
27040                     continue;
27041                 }
27042                 
27043                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27044
27045                 this.draw();
27046                 
27047                 return;
27048             }
27049             
27050             this.scale = this.startScale;
27051             
27052             this.onRotateFail();
27053             
27054             return false;
27055         }
27056         
27057         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27058
27059         if(this.isDocument){
27060             this.setThumbBoxSize();
27061             this.setThumbBoxPosition();
27062             this.setCanvasPosition();
27063         }
27064         
27065         this.draw();
27066         
27067         this.fireEvent('rotate', this, 'left');
27068         
27069     },
27070     
27071     onRotateRight : function(e)
27072     {
27073         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27074             
27075             var minScale = this.thumbEl.getWidth() / this.minWidth;
27076         
27077             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27078             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27079             
27080             this.startScale = this.scale;
27081             
27082             while (this.getScaleLevel() < minScale){
27083             
27084                 this.scale = this.scale + 1;
27085                 
27086                 if(!this.zoomable()){
27087                     break;
27088                 }
27089                 
27090                 if(
27091                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27092                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27093                 ){
27094                     continue;
27095                 }
27096                 
27097                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27098
27099                 this.draw();
27100                 
27101                 return;
27102             }
27103             
27104             this.scale = this.startScale;
27105             
27106             this.onRotateFail();
27107             
27108             return false;
27109         }
27110         
27111         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27112
27113         if(this.isDocument){
27114             this.setThumbBoxSize();
27115             this.setThumbBoxPosition();
27116             this.setCanvasPosition();
27117         }
27118         
27119         this.draw();
27120         
27121         this.fireEvent('rotate', this, 'right');
27122     },
27123     
27124     onRotateFail : function()
27125     {
27126         this.errorEl.show(true);
27127         
27128         var _this = this;
27129         
27130         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27131     },
27132     
27133     draw : function()
27134     {
27135         this.previewEl.dom.innerHTML = '';
27136         
27137         var canvasEl = document.createElement("canvas");
27138         
27139         var contextEl = canvasEl.getContext("2d");
27140         
27141         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27142         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27143         var center = this.imageEl.OriginWidth / 2;
27144         
27145         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27146             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27147             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27148             center = this.imageEl.OriginHeight / 2;
27149         }
27150         
27151         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27152         
27153         contextEl.translate(center, center);
27154         contextEl.rotate(this.rotate * Math.PI / 180);
27155
27156         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27157         
27158         this.canvasEl = document.createElement("canvas");
27159         
27160         this.contextEl = this.canvasEl.getContext("2d");
27161         
27162         switch (this.rotate) {
27163             case 0 :
27164                 
27165                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27166                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27167                 
27168                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27169                 
27170                 break;
27171             case 90 : 
27172                 
27173                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27174                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27175                 
27176                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27177                     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);
27178                     break;
27179                 }
27180                 
27181                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27182                 
27183                 break;
27184             case 180 :
27185                 
27186                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27187                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27188                 
27189                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27190                     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);
27191                     break;
27192                 }
27193                 
27194                 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);
27195                 
27196                 break;
27197             case 270 :
27198                 
27199                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27200                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27201         
27202                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27203                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27204                     break;
27205                 }
27206                 
27207                 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);
27208                 
27209                 break;
27210             default : 
27211                 break;
27212         }
27213         
27214         this.previewEl.appendChild(this.canvasEl);
27215         
27216         this.setCanvasPosition();
27217     },
27218     
27219     crop : function()
27220     {
27221         if(!this.canvasLoaded){
27222             return;
27223         }
27224         
27225         var imageCanvas = document.createElement("canvas");
27226         
27227         var imageContext = imageCanvas.getContext("2d");
27228         
27229         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27230         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27231         
27232         var center = imageCanvas.width / 2;
27233         
27234         imageContext.translate(center, center);
27235         
27236         imageContext.rotate(this.rotate * Math.PI / 180);
27237         
27238         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27239         
27240         var canvas = document.createElement("canvas");
27241         
27242         var context = canvas.getContext("2d");
27243                 
27244         canvas.width = this.minWidth;
27245         canvas.height = this.minHeight;
27246
27247         switch (this.rotate) {
27248             case 0 :
27249                 
27250                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27251                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27252                 
27253                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27254                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27255                 
27256                 var targetWidth = this.minWidth - 2 * x;
27257                 var targetHeight = this.minHeight - 2 * y;
27258                 
27259                 var scale = 1;
27260                 
27261                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27262                     scale = targetWidth / width;
27263                 }
27264                 
27265                 if(x > 0 && y == 0){
27266                     scale = targetHeight / height;
27267                 }
27268                 
27269                 if(x > 0 && y > 0){
27270                     scale = targetWidth / width;
27271                     
27272                     if(width < height){
27273                         scale = targetHeight / height;
27274                     }
27275                 }
27276                 
27277                 context.scale(scale, scale);
27278                 
27279                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27280                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27281
27282                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27283                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27284
27285                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27286                 
27287                 break;
27288             case 90 : 
27289                 
27290                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27291                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27292                 
27293                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27294                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27295                 
27296                 var targetWidth = this.minWidth - 2 * x;
27297                 var targetHeight = this.minHeight - 2 * y;
27298                 
27299                 var scale = 1;
27300                 
27301                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27302                     scale = targetWidth / width;
27303                 }
27304                 
27305                 if(x > 0 && y == 0){
27306                     scale = targetHeight / height;
27307                 }
27308                 
27309                 if(x > 0 && y > 0){
27310                     scale = targetWidth / width;
27311                     
27312                     if(width < height){
27313                         scale = targetHeight / height;
27314                     }
27315                 }
27316                 
27317                 context.scale(scale, scale);
27318                 
27319                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27320                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27321
27322                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27323                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27324                 
27325                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27326                 
27327                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27328                 
27329                 break;
27330             case 180 :
27331                 
27332                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27333                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27334                 
27335                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27336                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27337                 
27338                 var targetWidth = this.minWidth - 2 * x;
27339                 var targetHeight = this.minHeight - 2 * y;
27340                 
27341                 var scale = 1;
27342                 
27343                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27344                     scale = targetWidth / width;
27345                 }
27346                 
27347                 if(x > 0 && y == 0){
27348                     scale = targetHeight / height;
27349                 }
27350                 
27351                 if(x > 0 && y > 0){
27352                     scale = targetWidth / width;
27353                     
27354                     if(width < height){
27355                         scale = targetHeight / height;
27356                     }
27357                 }
27358                 
27359                 context.scale(scale, scale);
27360                 
27361                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27362                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27363
27364                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27365                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27366
27367                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27368                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27369                 
27370                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27371                 
27372                 break;
27373             case 270 :
27374                 
27375                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27376                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27377                 
27378                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27379                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27380                 
27381                 var targetWidth = this.minWidth - 2 * x;
27382                 var targetHeight = this.minHeight - 2 * y;
27383                 
27384                 var scale = 1;
27385                 
27386                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27387                     scale = targetWidth / width;
27388                 }
27389                 
27390                 if(x > 0 && y == 0){
27391                     scale = targetHeight / height;
27392                 }
27393                 
27394                 if(x > 0 && y > 0){
27395                     scale = targetWidth / width;
27396                     
27397                     if(width < height){
27398                         scale = targetHeight / height;
27399                     }
27400                 }
27401                 
27402                 context.scale(scale, scale);
27403                 
27404                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27405                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27406
27407                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27408                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27409                 
27410                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27411                 
27412                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27413                 
27414                 break;
27415             default : 
27416                 break;
27417         }
27418         
27419         this.cropData = canvas.toDataURL(this.cropType);
27420         
27421         if(this.fireEvent('crop', this, this.cropData) !== false){
27422             this.process(this.file, this.cropData);
27423         }
27424         
27425         return;
27426         
27427     },
27428     
27429     setThumbBoxSize : function()
27430     {
27431         var width, height;
27432         
27433         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27434             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27435             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27436             
27437             this.minWidth = width;
27438             this.minHeight = height;
27439             
27440             if(this.rotate == 90 || this.rotate == 270){
27441                 this.minWidth = height;
27442                 this.minHeight = width;
27443             }
27444         }
27445         
27446         height = 300;
27447         width = Math.ceil(this.minWidth * height / this.minHeight);
27448         
27449         if(this.minWidth > this.minHeight){
27450             width = 300;
27451             height = Math.ceil(this.minHeight * width / this.minWidth);
27452         }
27453         
27454         this.thumbEl.setStyle({
27455             width : width + 'px',
27456             height : height + 'px'
27457         });
27458
27459         return;
27460             
27461     },
27462     
27463     setThumbBoxPosition : function()
27464     {
27465         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27466         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27467         
27468         this.thumbEl.setLeft(x);
27469         this.thumbEl.setTop(y);
27470         
27471     },
27472     
27473     baseRotateLevel : function()
27474     {
27475         this.baseRotate = 1;
27476         
27477         if(
27478                 typeof(this.exif) != 'undefined' &&
27479                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27480                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27481         ){
27482             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27483         }
27484         
27485         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27486         
27487     },
27488     
27489     baseScaleLevel : function()
27490     {
27491         var width, height;
27492         
27493         if(this.isDocument){
27494             
27495             if(this.baseRotate == 6 || this.baseRotate == 8){
27496             
27497                 height = this.thumbEl.getHeight();
27498                 this.baseScale = height / this.imageEl.OriginWidth;
27499
27500                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27501                     width = this.thumbEl.getWidth();
27502                     this.baseScale = width / this.imageEl.OriginHeight;
27503                 }
27504
27505                 return;
27506             }
27507
27508             height = this.thumbEl.getHeight();
27509             this.baseScale = height / this.imageEl.OriginHeight;
27510
27511             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27512                 width = this.thumbEl.getWidth();
27513                 this.baseScale = width / this.imageEl.OriginWidth;
27514             }
27515
27516             return;
27517         }
27518         
27519         if(this.baseRotate == 6 || this.baseRotate == 8){
27520             
27521             width = this.thumbEl.getHeight();
27522             this.baseScale = width / this.imageEl.OriginHeight;
27523             
27524             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27525                 height = this.thumbEl.getWidth();
27526                 this.baseScale = height / this.imageEl.OriginHeight;
27527             }
27528             
27529             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27530                 height = this.thumbEl.getWidth();
27531                 this.baseScale = height / this.imageEl.OriginHeight;
27532                 
27533                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27534                     width = this.thumbEl.getHeight();
27535                     this.baseScale = width / this.imageEl.OriginWidth;
27536                 }
27537             }
27538             
27539             return;
27540         }
27541         
27542         width = this.thumbEl.getWidth();
27543         this.baseScale = width / this.imageEl.OriginWidth;
27544         
27545         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27546             height = this.thumbEl.getHeight();
27547             this.baseScale = height / this.imageEl.OriginHeight;
27548         }
27549         
27550         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27551             
27552             height = this.thumbEl.getHeight();
27553             this.baseScale = height / this.imageEl.OriginHeight;
27554             
27555             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27556                 width = this.thumbEl.getWidth();
27557                 this.baseScale = width / this.imageEl.OriginWidth;
27558             }
27559             
27560         }
27561         
27562         return;
27563     },
27564     
27565     getScaleLevel : function()
27566     {
27567         return this.baseScale * Math.pow(1.1, this.scale);
27568     },
27569     
27570     onTouchStart : function(e)
27571     {
27572         if(!this.canvasLoaded){
27573             this.beforeSelectFile(e);
27574             return;
27575         }
27576         
27577         var touches = e.browserEvent.touches;
27578         
27579         if(!touches){
27580             return;
27581         }
27582         
27583         if(touches.length == 1){
27584             this.onMouseDown(e);
27585             return;
27586         }
27587         
27588         if(touches.length != 2){
27589             return;
27590         }
27591         
27592         var coords = [];
27593         
27594         for(var i = 0, finger; finger = touches[i]; i++){
27595             coords.push(finger.pageX, finger.pageY);
27596         }
27597         
27598         var x = Math.pow(coords[0] - coords[2], 2);
27599         var y = Math.pow(coords[1] - coords[3], 2);
27600         
27601         this.startDistance = Math.sqrt(x + y);
27602         
27603         this.startScale = this.scale;
27604         
27605         this.pinching = true;
27606         this.dragable = false;
27607         
27608     },
27609     
27610     onTouchMove : function(e)
27611     {
27612         if(!this.pinching && !this.dragable){
27613             return;
27614         }
27615         
27616         var touches = e.browserEvent.touches;
27617         
27618         if(!touches){
27619             return;
27620         }
27621         
27622         if(this.dragable){
27623             this.onMouseMove(e);
27624             return;
27625         }
27626         
27627         var coords = [];
27628         
27629         for(var i = 0, finger; finger = touches[i]; i++){
27630             coords.push(finger.pageX, finger.pageY);
27631         }
27632         
27633         var x = Math.pow(coords[0] - coords[2], 2);
27634         var y = Math.pow(coords[1] - coords[3], 2);
27635         
27636         this.endDistance = Math.sqrt(x + y);
27637         
27638         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27639         
27640         if(!this.zoomable()){
27641             this.scale = this.startScale;
27642             return;
27643         }
27644         
27645         this.draw();
27646         
27647     },
27648     
27649     onTouchEnd : function(e)
27650     {
27651         this.pinching = false;
27652         this.dragable = false;
27653         
27654     },
27655     
27656     process : function(file, crop)
27657     {
27658         if(this.loadMask){
27659             this.maskEl.mask(this.loadingText);
27660         }
27661         
27662         this.xhr = new XMLHttpRequest();
27663         
27664         file.xhr = this.xhr;
27665
27666         this.xhr.open(this.method, this.url, true);
27667         
27668         var headers = {
27669             "Accept": "application/json",
27670             "Cache-Control": "no-cache",
27671             "X-Requested-With": "XMLHttpRequest"
27672         };
27673         
27674         for (var headerName in headers) {
27675             var headerValue = headers[headerName];
27676             if (headerValue) {
27677                 this.xhr.setRequestHeader(headerName, headerValue);
27678             }
27679         }
27680         
27681         var _this = this;
27682         
27683         this.xhr.onload = function()
27684         {
27685             _this.xhrOnLoad(_this.xhr);
27686         }
27687         
27688         this.xhr.onerror = function()
27689         {
27690             _this.xhrOnError(_this.xhr);
27691         }
27692         
27693         var formData = new FormData();
27694
27695         formData.append('returnHTML', 'NO');
27696         
27697         if(crop){
27698             formData.append('crop', crop);
27699         }
27700         
27701         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27702             formData.append(this.paramName, file, file.name);
27703         }
27704         
27705         if(typeof(file.filename) != 'undefined'){
27706             formData.append('filename', file.filename);
27707         }
27708         
27709         if(typeof(file.mimetype) != 'undefined'){
27710             formData.append('mimetype', file.mimetype);
27711         }
27712         
27713         if(this.fireEvent('arrange', this, formData) != false){
27714             this.xhr.send(formData);
27715         };
27716     },
27717     
27718     xhrOnLoad : function(xhr)
27719     {
27720         if(this.loadMask){
27721             this.maskEl.unmask();
27722         }
27723         
27724         if (xhr.readyState !== 4) {
27725             this.fireEvent('exception', this, xhr);
27726             return;
27727         }
27728
27729         var response = Roo.decode(xhr.responseText);
27730         
27731         if(!response.success){
27732             this.fireEvent('exception', this, xhr);
27733             return;
27734         }
27735         
27736         var response = Roo.decode(xhr.responseText);
27737         
27738         this.fireEvent('upload', this, response);
27739         
27740     },
27741     
27742     xhrOnError : function()
27743     {
27744         if(this.loadMask){
27745             this.maskEl.unmask();
27746         }
27747         
27748         Roo.log('xhr on error');
27749         
27750         var response = Roo.decode(xhr.responseText);
27751           
27752         Roo.log(response);
27753         
27754     },
27755     
27756     prepare : function(file)
27757     {   
27758         if(this.loadMask){
27759             this.maskEl.mask(this.loadingText);
27760         }
27761         
27762         this.file = false;
27763         this.exif = {};
27764         
27765         if(typeof(file) === 'string'){
27766             this.loadCanvas(file);
27767             return;
27768         }
27769         
27770         if(!file || !this.urlAPI){
27771             return;
27772         }
27773         
27774         this.file = file;
27775         this.cropType = file.type;
27776         
27777         var _this = this;
27778         
27779         if(this.fireEvent('prepare', this, this.file) != false){
27780             
27781             var reader = new FileReader();
27782             
27783             reader.onload = function (e) {
27784                 if (e.target.error) {
27785                     Roo.log(e.target.error);
27786                     return;
27787                 }
27788                 
27789                 var buffer = e.target.result,
27790                     dataView = new DataView(buffer),
27791                     offset = 2,
27792                     maxOffset = dataView.byteLength - 4,
27793                     markerBytes,
27794                     markerLength;
27795                 
27796                 if (dataView.getUint16(0) === 0xffd8) {
27797                     while (offset < maxOffset) {
27798                         markerBytes = dataView.getUint16(offset);
27799                         
27800                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27801                             markerLength = dataView.getUint16(offset + 2) + 2;
27802                             if (offset + markerLength > dataView.byteLength) {
27803                                 Roo.log('Invalid meta data: Invalid segment size.');
27804                                 break;
27805                             }
27806                             
27807                             if(markerBytes == 0xffe1){
27808                                 _this.parseExifData(
27809                                     dataView,
27810                                     offset,
27811                                     markerLength
27812                                 );
27813                             }
27814                             
27815                             offset += markerLength;
27816                             
27817                             continue;
27818                         }
27819                         
27820                         break;
27821                     }
27822                     
27823                 }
27824                 
27825                 var url = _this.urlAPI.createObjectURL(_this.file);
27826                 
27827                 _this.loadCanvas(url);
27828                 
27829                 return;
27830             }
27831             
27832             reader.readAsArrayBuffer(this.file);
27833             
27834         }
27835         
27836     },
27837     
27838     parseExifData : function(dataView, offset, length)
27839     {
27840         var tiffOffset = offset + 10,
27841             littleEndian,
27842             dirOffset;
27843     
27844         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27845             // No Exif data, might be XMP data instead
27846             return;
27847         }
27848         
27849         // Check for the ASCII code for "Exif" (0x45786966):
27850         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27851             // No Exif data, might be XMP data instead
27852             return;
27853         }
27854         if (tiffOffset + 8 > dataView.byteLength) {
27855             Roo.log('Invalid Exif data: Invalid segment size.');
27856             return;
27857         }
27858         // Check for the two null bytes:
27859         if (dataView.getUint16(offset + 8) !== 0x0000) {
27860             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27861             return;
27862         }
27863         // Check the byte alignment:
27864         switch (dataView.getUint16(tiffOffset)) {
27865         case 0x4949:
27866             littleEndian = true;
27867             break;
27868         case 0x4D4D:
27869             littleEndian = false;
27870             break;
27871         default:
27872             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27873             return;
27874         }
27875         // Check for the TIFF tag marker (0x002A):
27876         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27877             Roo.log('Invalid Exif data: Missing TIFF marker.');
27878             return;
27879         }
27880         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27881         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27882         
27883         this.parseExifTags(
27884             dataView,
27885             tiffOffset,
27886             tiffOffset + dirOffset,
27887             littleEndian
27888         );
27889     },
27890     
27891     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27892     {
27893         var tagsNumber,
27894             dirEndOffset,
27895             i;
27896         if (dirOffset + 6 > dataView.byteLength) {
27897             Roo.log('Invalid Exif data: Invalid directory offset.');
27898             return;
27899         }
27900         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27901         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27902         if (dirEndOffset + 4 > dataView.byteLength) {
27903             Roo.log('Invalid Exif data: Invalid directory size.');
27904             return;
27905         }
27906         for (i = 0; i < tagsNumber; i += 1) {
27907             this.parseExifTag(
27908                 dataView,
27909                 tiffOffset,
27910                 dirOffset + 2 + 12 * i, // tag offset
27911                 littleEndian
27912             );
27913         }
27914         // Return the offset to the next directory:
27915         return dataView.getUint32(dirEndOffset, littleEndian);
27916     },
27917     
27918     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27919     {
27920         var tag = dataView.getUint16(offset, littleEndian);
27921         
27922         this.exif[tag] = this.getExifValue(
27923             dataView,
27924             tiffOffset,
27925             offset,
27926             dataView.getUint16(offset + 2, littleEndian), // tag type
27927             dataView.getUint32(offset + 4, littleEndian), // tag length
27928             littleEndian
27929         );
27930     },
27931     
27932     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27933     {
27934         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27935             tagSize,
27936             dataOffset,
27937             values,
27938             i,
27939             str,
27940             c;
27941     
27942         if (!tagType) {
27943             Roo.log('Invalid Exif data: Invalid tag type.');
27944             return;
27945         }
27946         
27947         tagSize = tagType.size * length;
27948         // Determine if the value is contained in the dataOffset bytes,
27949         // or if the value at the dataOffset is a pointer to the actual data:
27950         dataOffset = tagSize > 4 ?
27951                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27952         if (dataOffset + tagSize > dataView.byteLength) {
27953             Roo.log('Invalid Exif data: Invalid data offset.');
27954             return;
27955         }
27956         if (length === 1) {
27957             return tagType.getValue(dataView, dataOffset, littleEndian);
27958         }
27959         values = [];
27960         for (i = 0; i < length; i += 1) {
27961             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27962         }
27963         
27964         if (tagType.ascii) {
27965             str = '';
27966             // Concatenate the chars:
27967             for (i = 0; i < values.length; i += 1) {
27968                 c = values[i];
27969                 // Ignore the terminating NULL byte(s):
27970                 if (c === '\u0000') {
27971                     break;
27972                 }
27973                 str += c;
27974             }
27975             return str;
27976         }
27977         return values;
27978     }
27979     
27980 });
27981
27982 Roo.apply(Roo.bootstrap.UploadCropbox, {
27983     tags : {
27984         'Orientation': 0x0112
27985     },
27986     
27987     Orientation: {
27988             1: 0, //'top-left',
27989 //            2: 'top-right',
27990             3: 180, //'bottom-right',
27991 //            4: 'bottom-left',
27992 //            5: 'left-top',
27993             6: 90, //'right-top',
27994 //            7: 'right-bottom',
27995             8: 270 //'left-bottom'
27996     },
27997     
27998     exifTagTypes : {
27999         // byte, 8-bit unsigned int:
28000         1: {
28001             getValue: function (dataView, dataOffset) {
28002                 return dataView.getUint8(dataOffset);
28003             },
28004             size: 1
28005         },
28006         // ascii, 8-bit byte:
28007         2: {
28008             getValue: function (dataView, dataOffset) {
28009                 return String.fromCharCode(dataView.getUint8(dataOffset));
28010             },
28011             size: 1,
28012             ascii: true
28013         },
28014         // short, 16 bit int:
28015         3: {
28016             getValue: function (dataView, dataOffset, littleEndian) {
28017                 return dataView.getUint16(dataOffset, littleEndian);
28018             },
28019             size: 2
28020         },
28021         // long, 32 bit int:
28022         4: {
28023             getValue: function (dataView, dataOffset, littleEndian) {
28024                 return dataView.getUint32(dataOffset, littleEndian);
28025             },
28026             size: 4
28027         },
28028         // rational = two long values, first is numerator, second is denominator:
28029         5: {
28030             getValue: function (dataView, dataOffset, littleEndian) {
28031                 return dataView.getUint32(dataOffset, littleEndian) /
28032                     dataView.getUint32(dataOffset + 4, littleEndian);
28033             },
28034             size: 8
28035         },
28036         // slong, 32 bit signed int:
28037         9: {
28038             getValue: function (dataView, dataOffset, littleEndian) {
28039                 return dataView.getInt32(dataOffset, littleEndian);
28040             },
28041             size: 4
28042         },
28043         // srational, two slongs, first is numerator, second is denominator:
28044         10: {
28045             getValue: function (dataView, dataOffset, littleEndian) {
28046                 return dataView.getInt32(dataOffset, littleEndian) /
28047                     dataView.getInt32(dataOffset + 4, littleEndian);
28048             },
28049             size: 8
28050         }
28051     },
28052     
28053     footer : {
28054         STANDARD : [
28055             {
28056                 tag : 'div',
28057                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28058                 action : 'rotate-left',
28059                 cn : [
28060                     {
28061                         tag : 'button',
28062                         cls : 'btn btn-default',
28063                         html : '<i class="fa fa-undo"></i>'
28064                     }
28065                 ]
28066             },
28067             {
28068                 tag : 'div',
28069                 cls : 'btn-group roo-upload-cropbox-picture',
28070                 action : 'picture',
28071                 cn : [
28072                     {
28073                         tag : 'button',
28074                         cls : 'btn btn-default',
28075                         html : '<i class="fa fa-picture-o"></i>'
28076                     }
28077                 ]
28078             },
28079             {
28080                 tag : 'div',
28081                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28082                 action : 'rotate-right',
28083                 cn : [
28084                     {
28085                         tag : 'button',
28086                         cls : 'btn btn-default',
28087                         html : '<i class="fa fa-repeat"></i>'
28088                     }
28089                 ]
28090             }
28091         ],
28092         DOCUMENT : [
28093             {
28094                 tag : 'div',
28095                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28096                 action : 'rotate-left',
28097                 cn : [
28098                     {
28099                         tag : 'button',
28100                         cls : 'btn btn-default',
28101                         html : '<i class="fa fa-undo"></i>'
28102                     }
28103                 ]
28104             },
28105             {
28106                 tag : 'div',
28107                 cls : 'btn-group roo-upload-cropbox-download',
28108                 action : 'download',
28109                 cn : [
28110                     {
28111                         tag : 'button',
28112                         cls : 'btn btn-default',
28113                         html : '<i class="fa fa-download"></i>'
28114                     }
28115                 ]
28116             },
28117             {
28118                 tag : 'div',
28119                 cls : 'btn-group roo-upload-cropbox-crop',
28120                 action : 'crop',
28121                 cn : [
28122                     {
28123                         tag : 'button',
28124                         cls : 'btn btn-default',
28125                         html : '<i class="fa fa-crop"></i>'
28126                     }
28127                 ]
28128             },
28129             {
28130                 tag : 'div',
28131                 cls : 'btn-group roo-upload-cropbox-trash',
28132                 action : 'trash',
28133                 cn : [
28134                     {
28135                         tag : 'button',
28136                         cls : 'btn btn-default',
28137                         html : '<i class="fa fa-trash"></i>'
28138                     }
28139                 ]
28140             },
28141             {
28142                 tag : 'div',
28143                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28144                 action : 'rotate-right',
28145                 cn : [
28146                     {
28147                         tag : 'button',
28148                         cls : 'btn btn-default',
28149                         html : '<i class="fa fa-repeat"></i>'
28150                     }
28151                 ]
28152             }
28153         ],
28154         ROTATOR : [
28155             {
28156                 tag : 'div',
28157                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28158                 action : 'rotate-left',
28159                 cn : [
28160                     {
28161                         tag : 'button',
28162                         cls : 'btn btn-default',
28163                         html : '<i class="fa fa-undo"></i>'
28164                     }
28165                 ]
28166             },
28167             {
28168                 tag : 'div',
28169                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28170                 action : 'rotate-right',
28171                 cn : [
28172                     {
28173                         tag : 'button',
28174                         cls : 'btn btn-default',
28175                         html : '<i class="fa fa-repeat"></i>'
28176                     }
28177                 ]
28178             }
28179         ]
28180     }
28181 });
28182
28183 /*
28184 * Licence: LGPL
28185 */
28186
28187 /**
28188  * @class Roo.bootstrap.DocumentManager
28189  * @extends Roo.bootstrap.Component
28190  * Bootstrap DocumentManager class
28191  * @cfg {String} paramName default 'imageUpload'
28192  * @cfg {String} toolTipName default 'filename'
28193  * @cfg {String} method default POST
28194  * @cfg {String} url action url
28195  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28196  * @cfg {Boolean} multiple multiple upload default true
28197  * @cfg {Number} thumbSize default 300
28198  * @cfg {String} fieldLabel
28199  * @cfg {Number} labelWidth default 4
28200  * @cfg {String} labelAlign (left|top) default left
28201  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28202 * @cfg {Number} labellg set the width of label (1-12)
28203  * @cfg {Number} labelmd set the width of label (1-12)
28204  * @cfg {Number} labelsm set the width of label (1-12)
28205  * @cfg {Number} labelxs set the width of label (1-12)
28206  * 
28207  * @constructor
28208  * Create a new DocumentManager
28209  * @param {Object} config The config object
28210  */
28211
28212 Roo.bootstrap.DocumentManager = function(config){
28213     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28214     
28215     this.files = [];
28216     this.delegates = [];
28217     
28218     this.addEvents({
28219         /**
28220          * @event initial
28221          * Fire when initial the DocumentManager
28222          * @param {Roo.bootstrap.DocumentManager} this
28223          */
28224         "initial" : true,
28225         /**
28226          * @event inspect
28227          * inspect selected file
28228          * @param {Roo.bootstrap.DocumentManager} this
28229          * @param {File} file
28230          */
28231         "inspect" : true,
28232         /**
28233          * @event exception
28234          * Fire when xhr load exception
28235          * @param {Roo.bootstrap.DocumentManager} this
28236          * @param {XMLHttpRequest} xhr
28237          */
28238         "exception" : true,
28239         /**
28240          * @event afterupload
28241          * Fire when xhr load exception
28242          * @param {Roo.bootstrap.DocumentManager} this
28243          * @param {XMLHttpRequest} xhr
28244          */
28245         "afterupload" : true,
28246         /**
28247          * @event prepare
28248          * prepare the form data
28249          * @param {Roo.bootstrap.DocumentManager} this
28250          * @param {Object} formData
28251          */
28252         "prepare" : true,
28253         /**
28254          * @event remove
28255          * Fire when remove the file
28256          * @param {Roo.bootstrap.DocumentManager} this
28257          * @param {Object} file
28258          */
28259         "remove" : true,
28260         /**
28261          * @event refresh
28262          * Fire after refresh the file
28263          * @param {Roo.bootstrap.DocumentManager} this
28264          */
28265         "refresh" : true,
28266         /**
28267          * @event click
28268          * Fire after click the image
28269          * @param {Roo.bootstrap.DocumentManager} this
28270          * @param {Object} file
28271          */
28272         "click" : true,
28273         /**
28274          * @event edit
28275          * Fire when upload a image and editable set to true
28276          * @param {Roo.bootstrap.DocumentManager} this
28277          * @param {Object} file
28278          */
28279         "edit" : true,
28280         /**
28281          * @event beforeselectfile
28282          * Fire before select file
28283          * @param {Roo.bootstrap.DocumentManager} this
28284          */
28285         "beforeselectfile" : true,
28286         /**
28287          * @event process
28288          * Fire before process file
28289          * @param {Roo.bootstrap.DocumentManager} this
28290          * @param {Object} file
28291          */
28292         "process" : true
28293         
28294     });
28295 };
28296
28297 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28298     
28299     boxes : 0,
28300     inputName : '',
28301     thumbSize : 300,
28302     multiple : true,
28303     files : false,
28304     method : 'POST',
28305     url : '',
28306     paramName : 'imageUpload',
28307     toolTipName : 'filename',
28308     fieldLabel : '',
28309     labelWidth : 4,
28310     labelAlign : 'left',
28311     editable : true,
28312     delegates : false,
28313     xhr : false, 
28314     
28315     labellg : 0,
28316     labelmd : 0,
28317     labelsm : 0,
28318     labelxs : 0,
28319     
28320     getAutoCreate : function()
28321     {   
28322         var managerWidget = {
28323             tag : 'div',
28324             cls : 'roo-document-manager',
28325             cn : [
28326                 {
28327                     tag : 'input',
28328                     cls : 'roo-document-manager-selector',
28329                     type : 'file'
28330                 },
28331                 {
28332                     tag : 'div',
28333                     cls : 'roo-document-manager-uploader',
28334                     cn : [
28335                         {
28336                             tag : 'div',
28337                             cls : 'roo-document-manager-upload-btn',
28338                             html : '<i class="fa fa-plus"></i>'
28339                         }
28340                     ]
28341                     
28342                 }
28343             ]
28344         };
28345         
28346         var content = [
28347             {
28348                 tag : 'div',
28349                 cls : 'column col-md-12',
28350                 cn : managerWidget
28351             }
28352         ];
28353         
28354         if(this.fieldLabel.length){
28355             
28356             content = [
28357                 {
28358                     tag : 'div',
28359                     cls : 'column col-md-12',
28360                     html : this.fieldLabel
28361                 },
28362                 {
28363                     tag : 'div',
28364                     cls : 'column col-md-12',
28365                     cn : managerWidget
28366                 }
28367             ];
28368
28369             if(this.labelAlign == 'left'){
28370                 content = [
28371                     {
28372                         tag : 'div',
28373                         cls : 'column',
28374                         html : this.fieldLabel
28375                     },
28376                     {
28377                         tag : 'div',
28378                         cls : 'column',
28379                         cn : managerWidget
28380                     }
28381                 ];
28382                 
28383                 if(this.labelWidth > 12){
28384                     content[0].style = "width: " + this.labelWidth + 'px';
28385                 }
28386
28387                 if(this.labelWidth < 13 && this.labelmd == 0){
28388                     this.labelmd = this.labelWidth;
28389                 }
28390
28391                 if(this.labellg > 0){
28392                     content[0].cls += ' col-lg-' + this.labellg;
28393                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28394                 }
28395
28396                 if(this.labelmd > 0){
28397                     content[0].cls += ' col-md-' + this.labelmd;
28398                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28399                 }
28400
28401                 if(this.labelsm > 0){
28402                     content[0].cls += ' col-sm-' + this.labelsm;
28403                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28404                 }
28405
28406                 if(this.labelxs > 0){
28407                     content[0].cls += ' col-xs-' + this.labelxs;
28408                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28409                 }
28410                 
28411             }
28412         }
28413         
28414         var cfg = {
28415             tag : 'div',
28416             cls : 'row clearfix',
28417             cn : content
28418         };
28419         
28420         return cfg;
28421         
28422     },
28423     
28424     initEvents : function()
28425     {
28426         this.managerEl = this.el.select('.roo-document-manager', true).first();
28427         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28428         
28429         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28430         this.selectorEl.hide();
28431         
28432         if(this.multiple){
28433             this.selectorEl.attr('multiple', 'multiple');
28434         }
28435         
28436         this.selectorEl.on('change', this.onFileSelected, this);
28437         
28438         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28439         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28440         
28441         this.uploader.on('click', this.onUploaderClick, this);
28442         
28443         this.renderProgressDialog();
28444         
28445         var _this = this;
28446         
28447         window.addEventListener("resize", function() { _this.refresh(); } );
28448         
28449         this.fireEvent('initial', this);
28450     },
28451     
28452     renderProgressDialog : function()
28453     {
28454         var _this = this;
28455         
28456         this.progressDialog = new Roo.bootstrap.Modal({
28457             cls : 'roo-document-manager-progress-dialog',
28458             allow_close : false,
28459             title : '',
28460             buttons : [
28461                 {
28462                     name  :'cancel',
28463                     weight : 'danger',
28464                     html : 'Cancel'
28465                 }
28466             ], 
28467             listeners : { 
28468                 btnclick : function() {
28469                     _this.uploadCancel();
28470                     this.hide();
28471                 }
28472             }
28473         });
28474          
28475         this.progressDialog.render(Roo.get(document.body));
28476          
28477         this.progress = new Roo.bootstrap.Progress({
28478             cls : 'roo-document-manager-progress',
28479             active : true,
28480             striped : true
28481         });
28482         
28483         this.progress.render(this.progressDialog.getChildContainer());
28484         
28485         this.progressBar = new Roo.bootstrap.ProgressBar({
28486             cls : 'roo-document-manager-progress-bar',
28487             aria_valuenow : 0,
28488             aria_valuemin : 0,
28489             aria_valuemax : 12,
28490             panel : 'success'
28491         });
28492         
28493         this.progressBar.render(this.progress.getChildContainer());
28494     },
28495     
28496     onUploaderClick : function(e)
28497     {
28498         e.preventDefault();
28499      
28500         if(this.fireEvent('beforeselectfile', this) != false){
28501             this.selectorEl.dom.click();
28502         }
28503         
28504     },
28505     
28506     onFileSelected : function(e)
28507     {
28508         e.preventDefault();
28509         
28510         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28511             return;
28512         }
28513         
28514         Roo.each(this.selectorEl.dom.files, function(file){
28515             if(this.fireEvent('inspect', this, file) != false){
28516                 this.files.push(file);
28517             }
28518         }, this);
28519         
28520         this.queue();
28521         
28522     },
28523     
28524     queue : function()
28525     {
28526         this.selectorEl.dom.value = '';
28527         
28528         if(!this.files.length){
28529             return;
28530         }
28531         
28532         if(this.boxes > 0 && this.files.length > this.boxes){
28533             this.files = this.files.slice(0, this.boxes);
28534         }
28535         
28536         this.uploader.show();
28537         
28538         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28539             this.uploader.hide();
28540         }
28541         
28542         var _this = this;
28543         
28544         var files = [];
28545         
28546         var docs = [];
28547         
28548         Roo.each(this.files, function(file){
28549             
28550             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28551                 var f = this.renderPreview(file);
28552                 files.push(f);
28553                 return;
28554             }
28555             
28556             if(file.type.indexOf('image') != -1){
28557                 this.delegates.push(
28558                     (function(){
28559                         _this.process(file);
28560                     }).createDelegate(this)
28561                 );
28562         
28563                 return;
28564             }
28565             
28566             docs.push(
28567                 (function(){
28568                     _this.process(file);
28569                 }).createDelegate(this)
28570             );
28571             
28572         }, this);
28573         
28574         this.files = files;
28575         
28576         this.delegates = this.delegates.concat(docs);
28577         
28578         if(!this.delegates.length){
28579             this.refresh();
28580             return;
28581         }
28582         
28583         this.progressBar.aria_valuemax = this.delegates.length;
28584         
28585         this.arrange();
28586         
28587         return;
28588     },
28589     
28590     arrange : function()
28591     {
28592         if(!this.delegates.length){
28593             this.progressDialog.hide();
28594             this.refresh();
28595             return;
28596         }
28597         
28598         var delegate = this.delegates.shift();
28599         
28600         this.progressDialog.show();
28601         
28602         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28603         
28604         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28605         
28606         delegate();
28607     },
28608     
28609     refresh : function()
28610     {
28611         this.uploader.show();
28612         
28613         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28614             this.uploader.hide();
28615         }
28616         
28617         Roo.isTouch ? this.closable(false) : this.closable(true);
28618         
28619         this.fireEvent('refresh', this);
28620     },
28621     
28622     onRemove : function(e, el, o)
28623     {
28624         e.preventDefault();
28625         
28626         this.fireEvent('remove', this, o);
28627         
28628     },
28629     
28630     remove : function(o)
28631     {
28632         var files = [];
28633         
28634         Roo.each(this.files, function(file){
28635             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28636                 files.push(file);
28637                 return;
28638             }
28639
28640             o.target.remove();
28641
28642         }, this);
28643         
28644         this.files = files;
28645         
28646         this.refresh();
28647     },
28648     
28649     clear : function()
28650     {
28651         Roo.each(this.files, function(file){
28652             if(!file.target){
28653                 return;
28654             }
28655             
28656             file.target.remove();
28657
28658         }, this);
28659         
28660         this.files = [];
28661         
28662         this.refresh();
28663     },
28664     
28665     onClick : function(e, el, o)
28666     {
28667         e.preventDefault();
28668         
28669         this.fireEvent('click', this, o);
28670         
28671     },
28672     
28673     closable : function(closable)
28674     {
28675         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28676             
28677             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28678             
28679             if(closable){
28680                 el.show();
28681                 return;
28682             }
28683             
28684             el.hide();
28685             
28686         }, this);
28687     },
28688     
28689     xhrOnLoad : function(xhr)
28690     {
28691         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28692             el.remove();
28693         }, this);
28694         
28695         if (xhr.readyState !== 4) {
28696             this.arrange();
28697             this.fireEvent('exception', this, xhr);
28698             return;
28699         }
28700
28701         var response = Roo.decode(xhr.responseText);
28702         
28703         if(!response.success){
28704             this.arrange();
28705             this.fireEvent('exception', this, xhr);
28706             return;
28707         }
28708         
28709         var file = this.renderPreview(response.data);
28710         
28711         this.files.push(file);
28712         
28713         this.arrange();
28714         
28715         this.fireEvent('afterupload', this, xhr);
28716         
28717     },
28718     
28719     xhrOnError : function(xhr)
28720     {
28721         Roo.log('xhr on error');
28722         
28723         var response = Roo.decode(xhr.responseText);
28724           
28725         Roo.log(response);
28726         
28727         this.arrange();
28728     },
28729     
28730     process : function(file)
28731     {
28732         if(this.fireEvent('process', this, file) !== false){
28733             if(this.editable && file.type.indexOf('image') != -1){
28734                 this.fireEvent('edit', this, file);
28735                 return;
28736             }
28737
28738             this.uploadStart(file, false);
28739
28740             return;
28741         }
28742         
28743     },
28744     
28745     uploadStart : function(file, crop)
28746     {
28747         this.xhr = new XMLHttpRequest();
28748         
28749         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28750             this.arrange();
28751             return;
28752         }
28753         
28754         file.xhr = this.xhr;
28755             
28756         this.managerEl.createChild({
28757             tag : 'div',
28758             cls : 'roo-document-manager-loading',
28759             cn : [
28760                 {
28761                     tag : 'div',
28762                     tooltip : file.name,
28763                     cls : 'roo-document-manager-thumb',
28764                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28765                 }
28766             ]
28767
28768         });
28769
28770         this.xhr.open(this.method, this.url, true);
28771         
28772         var headers = {
28773             "Accept": "application/json",
28774             "Cache-Control": "no-cache",
28775             "X-Requested-With": "XMLHttpRequest"
28776         };
28777         
28778         for (var headerName in headers) {
28779             var headerValue = headers[headerName];
28780             if (headerValue) {
28781                 this.xhr.setRequestHeader(headerName, headerValue);
28782             }
28783         }
28784         
28785         var _this = this;
28786         
28787         this.xhr.onload = function()
28788         {
28789             _this.xhrOnLoad(_this.xhr);
28790         }
28791         
28792         this.xhr.onerror = function()
28793         {
28794             _this.xhrOnError(_this.xhr);
28795         }
28796         
28797         var formData = new FormData();
28798
28799         formData.append('returnHTML', 'NO');
28800         
28801         if(crop){
28802             formData.append('crop', crop);
28803         }
28804         
28805         formData.append(this.paramName, file, file.name);
28806         
28807         var options = {
28808             file : file, 
28809             manually : false
28810         };
28811         
28812         if(this.fireEvent('prepare', this, formData, options) != false){
28813             
28814             if(options.manually){
28815                 return;
28816             }
28817             
28818             this.xhr.send(formData);
28819             return;
28820         };
28821         
28822         this.uploadCancel();
28823     },
28824     
28825     uploadCancel : function()
28826     {
28827         if (this.xhr) {
28828             this.xhr.abort();
28829         }
28830         
28831         this.delegates = [];
28832         
28833         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28834             el.remove();
28835         }, this);
28836         
28837         this.arrange();
28838     },
28839     
28840     renderPreview : function(file)
28841     {
28842         if(typeof(file.target) != 'undefined' && file.target){
28843             return file;
28844         }
28845         
28846         var previewEl = this.managerEl.createChild({
28847             tag : 'div',
28848             cls : 'roo-document-manager-preview',
28849             cn : [
28850                 {
28851                     tag : 'div',
28852                     tooltip : file[this.toolTipName],
28853                     cls : 'roo-document-manager-thumb',
28854                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28855                 },
28856                 {
28857                     tag : 'button',
28858                     cls : 'close',
28859                     html : '<i class="fa fa-times-circle"></i>'
28860                 }
28861             ]
28862         });
28863
28864         var close = previewEl.select('button.close', true).first();
28865
28866         close.on('click', this.onRemove, this, file);
28867
28868         file.target = previewEl;
28869
28870         var image = previewEl.select('img', true).first();
28871         
28872         var _this = this;
28873         
28874         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28875         
28876         image.on('click', this.onClick, this, file);
28877         
28878         return file;
28879         
28880     },
28881     
28882     onPreviewLoad : function(file, image)
28883     {
28884         if(typeof(file.target) == 'undefined' || !file.target){
28885             return;
28886         }
28887         
28888         var width = image.dom.naturalWidth || image.dom.width;
28889         var height = image.dom.naturalHeight || image.dom.height;
28890         
28891         if(width > height){
28892             file.target.addClass('wide');
28893             return;
28894         }
28895         
28896         file.target.addClass('tall');
28897         return;
28898         
28899     },
28900     
28901     uploadFromSource : function(file, crop)
28902     {
28903         this.xhr = new XMLHttpRequest();
28904         
28905         this.managerEl.createChild({
28906             tag : 'div',
28907             cls : 'roo-document-manager-loading',
28908             cn : [
28909                 {
28910                     tag : 'div',
28911                     tooltip : file.name,
28912                     cls : 'roo-document-manager-thumb',
28913                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28914                 }
28915             ]
28916
28917         });
28918
28919         this.xhr.open(this.method, this.url, true);
28920         
28921         var headers = {
28922             "Accept": "application/json",
28923             "Cache-Control": "no-cache",
28924             "X-Requested-With": "XMLHttpRequest"
28925         };
28926         
28927         for (var headerName in headers) {
28928             var headerValue = headers[headerName];
28929             if (headerValue) {
28930                 this.xhr.setRequestHeader(headerName, headerValue);
28931             }
28932         }
28933         
28934         var _this = this;
28935         
28936         this.xhr.onload = function()
28937         {
28938             _this.xhrOnLoad(_this.xhr);
28939         }
28940         
28941         this.xhr.onerror = function()
28942         {
28943             _this.xhrOnError(_this.xhr);
28944         }
28945         
28946         var formData = new FormData();
28947
28948         formData.append('returnHTML', 'NO');
28949         
28950         formData.append('crop', crop);
28951         
28952         if(typeof(file.filename) != 'undefined'){
28953             formData.append('filename', file.filename);
28954         }
28955         
28956         if(typeof(file.mimetype) != 'undefined'){
28957             formData.append('mimetype', file.mimetype);
28958         }
28959         
28960         Roo.log(formData);
28961         
28962         if(this.fireEvent('prepare', this, formData) != false){
28963             this.xhr.send(formData);
28964         };
28965     }
28966 });
28967
28968 /*
28969 * Licence: LGPL
28970 */
28971
28972 /**
28973  * @class Roo.bootstrap.DocumentViewer
28974  * @extends Roo.bootstrap.Component
28975  * Bootstrap DocumentViewer class
28976  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28977  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28978  * 
28979  * @constructor
28980  * Create a new DocumentViewer
28981  * @param {Object} config The config object
28982  */
28983
28984 Roo.bootstrap.DocumentViewer = function(config){
28985     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28986     
28987     this.addEvents({
28988         /**
28989          * @event initial
28990          * Fire after initEvent
28991          * @param {Roo.bootstrap.DocumentViewer} this
28992          */
28993         "initial" : true,
28994         /**
28995          * @event click
28996          * Fire after click
28997          * @param {Roo.bootstrap.DocumentViewer} this
28998          */
28999         "click" : true,
29000         /**
29001          * @event download
29002          * Fire after download button
29003          * @param {Roo.bootstrap.DocumentViewer} this
29004          */
29005         "download" : true,
29006         /**
29007          * @event trash
29008          * Fire after trash button
29009          * @param {Roo.bootstrap.DocumentViewer} this
29010          */
29011         "trash" : true
29012         
29013     });
29014 };
29015
29016 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29017     
29018     showDownload : true,
29019     
29020     showTrash : true,
29021     
29022     getAutoCreate : function()
29023     {
29024         var cfg = {
29025             tag : 'div',
29026             cls : 'roo-document-viewer',
29027             cn : [
29028                 {
29029                     tag : 'div',
29030                     cls : 'roo-document-viewer-body',
29031                     cn : [
29032                         {
29033                             tag : 'div',
29034                             cls : 'roo-document-viewer-thumb',
29035                             cn : [
29036                                 {
29037                                     tag : 'img',
29038                                     cls : 'roo-document-viewer-image'
29039                                 }
29040                             ]
29041                         }
29042                     ]
29043                 },
29044                 {
29045                     tag : 'div',
29046                     cls : 'roo-document-viewer-footer',
29047                     cn : {
29048                         tag : 'div',
29049                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29050                         cn : [
29051                             {
29052                                 tag : 'div',
29053                                 cls : 'btn-group roo-document-viewer-download',
29054                                 cn : [
29055                                     {
29056                                         tag : 'button',
29057                                         cls : 'btn btn-default',
29058                                         html : '<i class="fa fa-download"></i>'
29059                                     }
29060                                 ]
29061                             },
29062                             {
29063                                 tag : 'div',
29064                                 cls : 'btn-group roo-document-viewer-trash',
29065                                 cn : [
29066                                     {
29067                                         tag : 'button',
29068                                         cls : 'btn btn-default',
29069                                         html : '<i class="fa fa-trash"></i>'
29070                                     }
29071                                 ]
29072                             }
29073                         ]
29074                     }
29075                 }
29076             ]
29077         };
29078         
29079         return cfg;
29080     },
29081     
29082     initEvents : function()
29083     {
29084         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29085         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29086         
29087         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29088         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29089         
29090         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29091         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29092         
29093         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29094         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29095         
29096         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29097         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29098         
29099         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29100         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29101         
29102         this.bodyEl.on('click', this.onClick, this);
29103         this.downloadBtn.on('click', this.onDownload, this);
29104         this.trashBtn.on('click', this.onTrash, this);
29105         
29106         this.downloadBtn.hide();
29107         this.trashBtn.hide();
29108         
29109         if(this.showDownload){
29110             this.downloadBtn.show();
29111         }
29112         
29113         if(this.showTrash){
29114             this.trashBtn.show();
29115         }
29116         
29117         if(!this.showDownload && !this.showTrash) {
29118             this.footerEl.hide();
29119         }
29120         
29121     },
29122     
29123     initial : function()
29124     {
29125         this.fireEvent('initial', this);
29126         
29127     },
29128     
29129     onClick : function(e)
29130     {
29131         e.preventDefault();
29132         
29133         this.fireEvent('click', this);
29134     },
29135     
29136     onDownload : function(e)
29137     {
29138         e.preventDefault();
29139         
29140         this.fireEvent('download', this);
29141     },
29142     
29143     onTrash : function(e)
29144     {
29145         e.preventDefault();
29146         
29147         this.fireEvent('trash', this);
29148     }
29149     
29150 });
29151 /*
29152  * - LGPL
29153  *
29154  * nav progress bar
29155  * 
29156  */
29157
29158 /**
29159  * @class Roo.bootstrap.NavProgressBar
29160  * @extends Roo.bootstrap.Component
29161  * Bootstrap NavProgressBar class
29162  * 
29163  * @constructor
29164  * Create a new nav progress bar
29165  * @param {Object} config The config object
29166  */
29167
29168 Roo.bootstrap.NavProgressBar = function(config){
29169     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29170
29171     this.bullets = this.bullets || [];
29172    
29173 //    Roo.bootstrap.NavProgressBar.register(this);
29174      this.addEvents({
29175         /**
29176              * @event changed
29177              * Fires when the active item changes
29178              * @param {Roo.bootstrap.NavProgressBar} this
29179              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29180              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29181          */
29182         'changed': true
29183      });
29184     
29185 };
29186
29187 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29188     
29189     bullets : [],
29190     barItems : [],
29191     
29192     getAutoCreate : function()
29193     {
29194         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29195         
29196         cfg = {
29197             tag : 'div',
29198             cls : 'roo-navigation-bar-group',
29199             cn : [
29200                 {
29201                     tag : 'div',
29202                     cls : 'roo-navigation-top-bar'
29203                 },
29204                 {
29205                     tag : 'div',
29206                     cls : 'roo-navigation-bullets-bar',
29207                     cn : [
29208                         {
29209                             tag : 'ul',
29210                             cls : 'roo-navigation-bar'
29211                         }
29212                     ]
29213                 },
29214                 
29215                 {
29216                     tag : 'div',
29217                     cls : 'roo-navigation-bottom-bar'
29218                 }
29219             ]
29220             
29221         };
29222         
29223         return cfg;
29224         
29225     },
29226     
29227     initEvents: function() 
29228     {
29229         
29230     },
29231     
29232     onRender : function(ct, position) 
29233     {
29234         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29235         
29236         if(this.bullets.length){
29237             Roo.each(this.bullets, function(b){
29238                this.addItem(b);
29239             }, this);
29240         }
29241         
29242         this.format();
29243         
29244     },
29245     
29246     addItem : function(cfg)
29247     {
29248         var item = new Roo.bootstrap.NavProgressItem(cfg);
29249         
29250         item.parentId = this.id;
29251         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29252         
29253         if(cfg.html){
29254             var top = new Roo.bootstrap.Element({
29255                 tag : 'div',
29256                 cls : 'roo-navigation-bar-text'
29257             });
29258             
29259             var bottom = new Roo.bootstrap.Element({
29260                 tag : 'div',
29261                 cls : 'roo-navigation-bar-text'
29262             });
29263             
29264             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29265             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29266             
29267             var topText = new Roo.bootstrap.Element({
29268                 tag : 'span',
29269                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29270             });
29271             
29272             var bottomText = new Roo.bootstrap.Element({
29273                 tag : 'span',
29274                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29275             });
29276             
29277             topText.onRender(top.el, null);
29278             bottomText.onRender(bottom.el, null);
29279             
29280             item.topEl = top;
29281             item.bottomEl = bottom;
29282         }
29283         
29284         this.barItems.push(item);
29285         
29286         return item;
29287     },
29288     
29289     getActive : function()
29290     {
29291         var active = false;
29292         
29293         Roo.each(this.barItems, function(v){
29294             
29295             if (!v.isActive()) {
29296                 return;
29297             }
29298             
29299             active = v;
29300             return false;
29301             
29302         });
29303         
29304         return active;
29305     },
29306     
29307     setActiveItem : function(item)
29308     {
29309         var prev = false;
29310         
29311         Roo.each(this.barItems, function(v){
29312             if (v.rid == item.rid) {
29313                 return ;
29314             }
29315             
29316             if (v.isActive()) {
29317                 v.setActive(false);
29318                 prev = v;
29319             }
29320         });
29321
29322         item.setActive(true);
29323         
29324         this.fireEvent('changed', this, item, prev);
29325     },
29326     
29327     getBarItem: function(rid)
29328     {
29329         var ret = false;
29330         
29331         Roo.each(this.barItems, function(e) {
29332             if (e.rid != rid) {
29333                 return;
29334             }
29335             
29336             ret =  e;
29337             return false;
29338         });
29339         
29340         return ret;
29341     },
29342     
29343     indexOfItem : function(item)
29344     {
29345         var index = false;
29346         
29347         Roo.each(this.barItems, function(v, i){
29348             
29349             if (v.rid != item.rid) {
29350                 return;
29351             }
29352             
29353             index = i;
29354             return false
29355         });
29356         
29357         return index;
29358     },
29359     
29360     setActiveNext : function()
29361     {
29362         var i = this.indexOfItem(this.getActive());
29363         
29364         if (i > this.barItems.length) {
29365             return;
29366         }
29367         
29368         this.setActiveItem(this.barItems[i+1]);
29369     },
29370     
29371     setActivePrev : function()
29372     {
29373         var i = this.indexOfItem(this.getActive());
29374         
29375         if (i  < 1) {
29376             return;
29377         }
29378         
29379         this.setActiveItem(this.barItems[i-1]);
29380     },
29381     
29382     format : function()
29383     {
29384         if(!this.barItems.length){
29385             return;
29386         }
29387      
29388         var width = 100 / this.barItems.length;
29389         
29390         Roo.each(this.barItems, function(i){
29391             i.el.setStyle('width', width + '%');
29392             i.topEl.el.setStyle('width', width + '%');
29393             i.bottomEl.el.setStyle('width', width + '%');
29394         }, this);
29395         
29396     }
29397     
29398 });
29399 /*
29400  * - LGPL
29401  *
29402  * Nav Progress Item
29403  * 
29404  */
29405
29406 /**
29407  * @class Roo.bootstrap.NavProgressItem
29408  * @extends Roo.bootstrap.Component
29409  * Bootstrap NavProgressItem class
29410  * @cfg {String} rid the reference id
29411  * @cfg {Boolean} active (true|false) Is item active default false
29412  * @cfg {Boolean} disabled (true|false) Is item active default false
29413  * @cfg {String} html
29414  * @cfg {String} position (top|bottom) text position default bottom
29415  * @cfg {String} icon show icon instead of number
29416  * 
29417  * @constructor
29418  * Create a new NavProgressItem
29419  * @param {Object} config The config object
29420  */
29421 Roo.bootstrap.NavProgressItem = function(config){
29422     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29423     this.addEvents({
29424         // raw events
29425         /**
29426          * @event click
29427          * The raw click event for the entire grid.
29428          * @param {Roo.bootstrap.NavProgressItem} this
29429          * @param {Roo.EventObject} e
29430          */
29431         "click" : true
29432     });
29433    
29434 };
29435
29436 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29437     
29438     rid : '',
29439     active : false,
29440     disabled : false,
29441     html : '',
29442     position : 'bottom',
29443     icon : false,
29444     
29445     getAutoCreate : function()
29446     {
29447         var iconCls = 'roo-navigation-bar-item-icon';
29448         
29449         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29450         
29451         var cfg = {
29452             tag: 'li',
29453             cls: 'roo-navigation-bar-item',
29454             cn : [
29455                 {
29456                     tag : 'i',
29457                     cls : iconCls
29458                 }
29459             ]
29460         };
29461         
29462         if(this.active){
29463             cfg.cls += ' active';
29464         }
29465         if(this.disabled){
29466             cfg.cls += ' disabled';
29467         }
29468         
29469         return cfg;
29470     },
29471     
29472     disable : function()
29473     {
29474         this.setDisabled(true);
29475     },
29476     
29477     enable : function()
29478     {
29479         this.setDisabled(false);
29480     },
29481     
29482     initEvents: function() 
29483     {
29484         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29485         
29486         this.iconEl.on('click', this.onClick, this);
29487     },
29488     
29489     onClick : function(e)
29490     {
29491         e.preventDefault();
29492         
29493         if(this.disabled){
29494             return;
29495         }
29496         
29497         if(this.fireEvent('click', this, e) === false){
29498             return;
29499         };
29500         
29501         this.parent().setActiveItem(this);
29502     },
29503     
29504     isActive: function () 
29505     {
29506         return this.active;
29507     },
29508     
29509     setActive : function(state)
29510     {
29511         if(this.active == state){
29512             return;
29513         }
29514         
29515         this.active = state;
29516         
29517         if (state) {
29518             this.el.addClass('active');
29519             return;
29520         }
29521         
29522         this.el.removeClass('active');
29523         
29524         return;
29525     },
29526     
29527     setDisabled : function(state)
29528     {
29529         if(this.disabled == state){
29530             return;
29531         }
29532         
29533         this.disabled = state;
29534         
29535         if (state) {
29536             this.el.addClass('disabled');
29537             return;
29538         }
29539         
29540         this.el.removeClass('disabled');
29541     },
29542     
29543     tooltipEl : function()
29544     {
29545         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29546     }
29547 });
29548  
29549
29550  /*
29551  * - LGPL
29552  *
29553  * FieldLabel
29554  * 
29555  */
29556
29557 /**
29558  * @class Roo.bootstrap.FieldLabel
29559  * @extends Roo.bootstrap.Component
29560  * Bootstrap FieldLabel class
29561  * @cfg {String} html contents of the element
29562  * @cfg {String} tag tag of the element default label
29563  * @cfg {String} cls class of the element
29564  * @cfg {String} target label target 
29565  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29566  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29567  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29568  * @cfg {String} iconTooltip default "This field is required"
29569  * 
29570  * @constructor
29571  * Create a new FieldLabel
29572  * @param {Object} config The config object
29573  */
29574
29575 Roo.bootstrap.FieldLabel = function(config){
29576     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29577     
29578     this.addEvents({
29579             /**
29580              * @event invalid
29581              * Fires after the field has been marked as invalid.
29582              * @param {Roo.form.FieldLabel} this
29583              * @param {String} msg The validation message
29584              */
29585             invalid : true,
29586             /**
29587              * @event valid
29588              * Fires after the field has been validated with no errors.
29589              * @param {Roo.form.FieldLabel} this
29590              */
29591             valid : true
29592         });
29593 };
29594
29595 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29596     
29597     tag: 'label',
29598     cls: '',
29599     html: '',
29600     target: '',
29601     allowBlank : true,
29602     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29603     validClass : 'text-success fa fa-lg fa-check',
29604     iconTooltip : 'This field is required',
29605     
29606     getAutoCreate : function(){
29607         
29608         var cfg = {
29609             tag : this.tag,
29610             cls : 'roo-bootstrap-field-label ' + this.cls,
29611             for : this.target,
29612             cn : [
29613                 {
29614                     tag : 'i',
29615                     cls : '',
29616                     tooltip : this.iconTooltip
29617                 },
29618                 {
29619                     tag : 'span',
29620                     html : this.html
29621                 }
29622             ] 
29623         };
29624         
29625         return cfg;
29626     },
29627     
29628     initEvents: function() 
29629     {
29630         Roo.bootstrap.Element.superclass.initEvents.call(this);
29631         
29632         this.iconEl = this.el.select('i', true).first();
29633         
29634         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29635         
29636         Roo.bootstrap.FieldLabel.register(this);
29637     },
29638     
29639     /**
29640      * Mark this field as valid
29641      */
29642     markValid : function()
29643     {
29644         this.iconEl.show();
29645         
29646         this.iconEl.removeClass(this.invalidClass);
29647         
29648         this.iconEl.addClass(this.validClass);
29649         
29650         this.fireEvent('valid', this);
29651     },
29652     
29653     /**
29654      * Mark this field as invalid
29655      * @param {String} msg The validation message
29656      */
29657     markInvalid : function(msg)
29658     {
29659         this.iconEl.show();
29660         
29661         this.iconEl.removeClass(this.validClass);
29662         
29663         this.iconEl.addClass(this.invalidClass);
29664         
29665         this.fireEvent('invalid', this, msg);
29666     }
29667     
29668    
29669 });
29670
29671 Roo.apply(Roo.bootstrap.FieldLabel, {
29672     
29673     groups: {},
29674     
29675      /**
29676     * register a FieldLabel Group
29677     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29678     */
29679     register : function(label)
29680     {
29681         if(this.groups.hasOwnProperty(label.target)){
29682             return;
29683         }
29684      
29685         this.groups[label.target] = label;
29686         
29687     },
29688     /**
29689     * fetch a FieldLabel Group based on the target
29690     * @param {string} target
29691     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29692     */
29693     get: function(target) {
29694         if (typeof(this.groups[target]) == 'undefined') {
29695             return false;
29696         }
29697         
29698         return this.groups[target] ;
29699     }
29700 });
29701
29702  
29703
29704  /*
29705  * - LGPL
29706  *
29707  * page DateSplitField.
29708  * 
29709  */
29710
29711
29712 /**
29713  * @class Roo.bootstrap.DateSplitField
29714  * @extends Roo.bootstrap.Component
29715  * Bootstrap DateSplitField class
29716  * @cfg {string} fieldLabel - the label associated
29717  * @cfg {Number} labelWidth set the width of label (0-12)
29718  * @cfg {String} labelAlign (top|left)
29719  * @cfg {Boolean} dayAllowBlank (true|false) default false
29720  * @cfg {Boolean} monthAllowBlank (true|false) default false
29721  * @cfg {Boolean} yearAllowBlank (true|false) default false
29722  * @cfg {string} dayPlaceholder 
29723  * @cfg {string} monthPlaceholder
29724  * @cfg {string} yearPlaceholder
29725  * @cfg {string} dayFormat default 'd'
29726  * @cfg {string} monthFormat default 'm'
29727  * @cfg {string} yearFormat default 'Y'
29728  * @cfg {Number} labellg set the width of label (1-12)
29729  * @cfg {Number} labelmd set the width of label (1-12)
29730  * @cfg {Number} labelsm set the width of label (1-12)
29731  * @cfg {Number} labelxs set the width of label (1-12)
29732
29733  *     
29734  * @constructor
29735  * Create a new DateSplitField
29736  * @param {Object} config The config object
29737  */
29738
29739 Roo.bootstrap.DateSplitField = function(config){
29740     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29741     
29742     this.addEvents({
29743         // raw events
29744          /**
29745          * @event years
29746          * getting the data of years
29747          * @param {Roo.bootstrap.DateSplitField} this
29748          * @param {Object} years
29749          */
29750         "years" : true,
29751         /**
29752          * @event days
29753          * getting the data of days
29754          * @param {Roo.bootstrap.DateSplitField} this
29755          * @param {Object} days
29756          */
29757         "days" : true,
29758         /**
29759          * @event invalid
29760          * Fires after the field has been marked as invalid.
29761          * @param {Roo.form.Field} this
29762          * @param {String} msg The validation message
29763          */
29764         invalid : true,
29765        /**
29766          * @event valid
29767          * Fires after the field has been validated with no errors.
29768          * @param {Roo.form.Field} this
29769          */
29770         valid : true
29771     });
29772 };
29773
29774 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29775     
29776     fieldLabel : '',
29777     labelAlign : 'top',
29778     labelWidth : 3,
29779     dayAllowBlank : false,
29780     monthAllowBlank : false,
29781     yearAllowBlank : false,
29782     dayPlaceholder : '',
29783     monthPlaceholder : '',
29784     yearPlaceholder : '',
29785     dayFormat : 'd',
29786     monthFormat : 'm',
29787     yearFormat : 'Y',
29788     isFormField : true,
29789     labellg : 0,
29790     labelmd : 0,
29791     labelsm : 0,
29792     labelxs : 0,
29793     
29794     getAutoCreate : function()
29795     {
29796         var cfg = {
29797             tag : 'div',
29798             cls : 'row roo-date-split-field-group',
29799             cn : [
29800                 {
29801                     tag : 'input',
29802                     type : 'hidden',
29803                     cls : 'form-hidden-field roo-date-split-field-group-value',
29804                     name : this.name
29805                 }
29806             ]
29807         };
29808         
29809         var labelCls = 'col-md-12';
29810         var contentCls = 'col-md-4';
29811         
29812         if(this.fieldLabel){
29813             
29814             var label = {
29815                 tag : 'div',
29816                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29817                 cn : [
29818                     {
29819                         tag : 'label',
29820                         html : this.fieldLabel
29821                     }
29822                 ]
29823             };
29824             
29825             if(this.labelAlign == 'left'){
29826             
29827                 if(this.labelWidth > 12){
29828                     label.style = "width: " + this.labelWidth + 'px';
29829                 }
29830
29831                 if(this.labelWidth < 13 && this.labelmd == 0){
29832                     this.labelmd = this.labelWidth;
29833                 }
29834
29835                 if(this.labellg > 0){
29836                     labelCls = ' col-lg-' + this.labellg;
29837                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29838                 }
29839
29840                 if(this.labelmd > 0){
29841                     labelCls = ' col-md-' + this.labelmd;
29842                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29843                 }
29844
29845                 if(this.labelsm > 0){
29846                     labelCls = ' col-sm-' + this.labelsm;
29847                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29848                 }
29849
29850                 if(this.labelxs > 0){
29851                     labelCls = ' col-xs-' + this.labelxs;
29852                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29853                 }
29854             }
29855             
29856             label.cls += ' ' + labelCls;
29857             
29858             cfg.cn.push(label);
29859         }
29860         
29861         Roo.each(['day', 'month', 'year'], function(t){
29862             cfg.cn.push({
29863                 tag : 'div',
29864                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29865             });
29866         }, this);
29867         
29868         return cfg;
29869     },
29870     
29871     inputEl: function ()
29872     {
29873         return this.el.select('.roo-date-split-field-group-value', true).first();
29874     },
29875     
29876     onRender : function(ct, position) 
29877     {
29878         var _this = this;
29879         
29880         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29881         
29882         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29883         
29884         this.dayField = new Roo.bootstrap.ComboBox({
29885             allowBlank : this.dayAllowBlank,
29886             alwaysQuery : true,
29887             displayField : 'value',
29888             editable : false,
29889             fieldLabel : '',
29890             forceSelection : true,
29891             mode : 'local',
29892             placeholder : this.dayPlaceholder,
29893             selectOnFocus : true,
29894             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29895             triggerAction : 'all',
29896             typeAhead : true,
29897             valueField : 'value',
29898             store : new Roo.data.SimpleStore({
29899                 data : (function() {    
29900                     var days = [];
29901                     _this.fireEvent('days', _this, days);
29902                     return days;
29903                 })(),
29904                 fields : [ 'value' ]
29905             }),
29906             listeners : {
29907                 select : function (_self, record, index)
29908                 {
29909                     _this.setValue(_this.getValue());
29910                 }
29911             }
29912         });
29913
29914         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29915         
29916         this.monthField = new Roo.bootstrap.MonthField({
29917             after : '<i class=\"fa fa-calendar\"></i>',
29918             allowBlank : this.monthAllowBlank,
29919             placeholder : this.monthPlaceholder,
29920             readOnly : true,
29921             listeners : {
29922                 render : function (_self)
29923                 {
29924                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29925                         e.preventDefault();
29926                         _self.focus();
29927                     });
29928                 },
29929                 select : function (_self, oldvalue, newvalue)
29930                 {
29931                     _this.setValue(_this.getValue());
29932                 }
29933             }
29934         });
29935         
29936         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29937         
29938         this.yearField = new Roo.bootstrap.ComboBox({
29939             allowBlank : this.yearAllowBlank,
29940             alwaysQuery : true,
29941             displayField : 'value',
29942             editable : false,
29943             fieldLabel : '',
29944             forceSelection : true,
29945             mode : 'local',
29946             placeholder : this.yearPlaceholder,
29947             selectOnFocus : true,
29948             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29949             triggerAction : 'all',
29950             typeAhead : true,
29951             valueField : 'value',
29952             store : new Roo.data.SimpleStore({
29953                 data : (function() {
29954                     var years = [];
29955                     _this.fireEvent('years', _this, years);
29956                     return years;
29957                 })(),
29958                 fields : [ 'value' ]
29959             }),
29960             listeners : {
29961                 select : function (_self, record, index)
29962                 {
29963                     _this.setValue(_this.getValue());
29964                 }
29965             }
29966         });
29967
29968         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29969     },
29970     
29971     setValue : function(v, format)
29972     {
29973         this.inputEl.dom.value = v;
29974         
29975         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29976         
29977         var d = Date.parseDate(v, f);
29978         
29979         if(!d){
29980             this.validate();
29981             return;
29982         }
29983         
29984         this.setDay(d.format(this.dayFormat));
29985         this.setMonth(d.format(this.monthFormat));
29986         this.setYear(d.format(this.yearFormat));
29987         
29988         this.validate();
29989         
29990         return;
29991     },
29992     
29993     setDay : function(v)
29994     {
29995         this.dayField.setValue(v);
29996         this.inputEl.dom.value = this.getValue();
29997         this.validate();
29998         return;
29999     },
30000     
30001     setMonth : function(v)
30002     {
30003         this.monthField.setValue(v, true);
30004         this.inputEl.dom.value = this.getValue();
30005         this.validate();
30006         return;
30007     },
30008     
30009     setYear : function(v)
30010     {
30011         this.yearField.setValue(v);
30012         this.inputEl.dom.value = this.getValue();
30013         this.validate();
30014         return;
30015     },
30016     
30017     getDay : function()
30018     {
30019         return this.dayField.getValue();
30020     },
30021     
30022     getMonth : function()
30023     {
30024         return this.monthField.getValue();
30025     },
30026     
30027     getYear : function()
30028     {
30029         return this.yearField.getValue();
30030     },
30031     
30032     getValue : function()
30033     {
30034         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30035         
30036         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30037         
30038         return date;
30039     },
30040     
30041     reset : function()
30042     {
30043         this.setDay('');
30044         this.setMonth('');
30045         this.setYear('');
30046         this.inputEl.dom.value = '';
30047         this.validate();
30048         return;
30049     },
30050     
30051     validate : function()
30052     {
30053         var d = this.dayField.validate();
30054         var m = this.monthField.validate();
30055         var y = this.yearField.validate();
30056         
30057         var valid = true;
30058         
30059         if(
30060                 (!this.dayAllowBlank && !d) ||
30061                 (!this.monthAllowBlank && !m) ||
30062                 (!this.yearAllowBlank && !y)
30063         ){
30064             valid = false;
30065         }
30066         
30067         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30068             return valid;
30069         }
30070         
30071         if(valid){
30072             this.markValid();
30073             return valid;
30074         }
30075         
30076         this.markInvalid();
30077         
30078         return valid;
30079     },
30080     
30081     markValid : function()
30082     {
30083         
30084         var label = this.el.select('label', true).first();
30085         var icon = this.el.select('i.fa-star', true).first();
30086
30087         if(label && icon){
30088             icon.remove();
30089         }
30090         
30091         this.fireEvent('valid', this);
30092     },
30093     
30094      /**
30095      * Mark this field as invalid
30096      * @param {String} msg The validation message
30097      */
30098     markInvalid : function(msg)
30099     {
30100         
30101         var label = this.el.select('label', true).first();
30102         var icon = this.el.select('i.fa-star', true).first();
30103
30104         if(label && !icon){
30105             this.el.select('.roo-date-split-field-label', true).createChild({
30106                 tag : 'i',
30107                 cls : 'text-danger fa fa-lg fa-star',
30108                 tooltip : 'This field is required',
30109                 style : 'margin-right:5px;'
30110             }, label, true);
30111         }
30112         
30113         this.fireEvent('invalid', this, msg);
30114     },
30115     
30116     clearInvalid : function()
30117     {
30118         var label = this.el.select('label', true).first();
30119         var icon = this.el.select('i.fa-star', true).first();
30120
30121         if(label && icon){
30122             icon.remove();
30123         }
30124         
30125         this.fireEvent('valid', this);
30126     },
30127     
30128     getName: function()
30129     {
30130         return this.name;
30131     }
30132     
30133 });
30134
30135  /**
30136  *
30137  * This is based on 
30138  * http://masonry.desandro.com
30139  *
30140  * The idea is to render all the bricks based on vertical width...
30141  *
30142  * The original code extends 'outlayer' - we might need to use that....
30143  * 
30144  */
30145
30146
30147 /**
30148  * @class Roo.bootstrap.LayoutMasonry
30149  * @extends Roo.bootstrap.Component
30150  * Bootstrap Layout Masonry class
30151  * 
30152  * @constructor
30153  * Create a new Element
30154  * @param {Object} config The config object
30155  */
30156
30157 Roo.bootstrap.LayoutMasonry = function(config){
30158     
30159     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30160     
30161     this.bricks = [];
30162     
30163     Roo.bootstrap.LayoutMasonry.register(this);
30164     
30165     this.addEvents({
30166         // raw events
30167         /**
30168          * @event layout
30169          * Fire after layout the items
30170          * @param {Roo.bootstrap.LayoutMasonry} this
30171          * @param {Roo.EventObject} e
30172          */
30173         "layout" : true
30174     });
30175     
30176 };
30177
30178 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30179     
30180     /**
30181      * @cfg {Boolean} isLayoutInstant = no animation?
30182      */   
30183     isLayoutInstant : false, // needed?
30184    
30185     /**
30186      * @cfg {Number} boxWidth  width of the columns
30187      */   
30188     boxWidth : 450,
30189     
30190       /**
30191      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30192      */   
30193     boxHeight : 0,
30194     
30195     /**
30196      * @cfg {Number} padWidth padding below box..
30197      */   
30198     padWidth : 10, 
30199     
30200     /**
30201      * @cfg {Number} gutter gutter width..
30202      */   
30203     gutter : 10,
30204     
30205      /**
30206      * @cfg {Number} maxCols maximum number of columns
30207      */   
30208     
30209     maxCols: 0,
30210     
30211     /**
30212      * @cfg {Boolean} isAutoInitial defalut true
30213      */   
30214     isAutoInitial : true, 
30215     
30216     containerWidth: 0,
30217     
30218     /**
30219      * @cfg {Boolean} isHorizontal defalut false
30220      */   
30221     isHorizontal : false, 
30222
30223     currentSize : null,
30224     
30225     tag: 'div',
30226     
30227     cls: '',
30228     
30229     bricks: null, //CompositeElement
30230     
30231     cols : 1,
30232     
30233     _isLayoutInited : false,
30234     
30235 //    isAlternative : false, // only use for vertical layout...
30236     
30237     /**
30238      * @cfg {Number} alternativePadWidth padding below box..
30239      */   
30240     alternativePadWidth : 50,
30241     
30242     selectedBrick : [],
30243     
30244     getAutoCreate : function(){
30245         
30246         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30247         
30248         var cfg = {
30249             tag: this.tag,
30250             cls: 'blog-masonary-wrapper ' + this.cls,
30251             cn : {
30252                 cls : 'mas-boxes masonary'
30253             }
30254         };
30255         
30256         return cfg;
30257     },
30258     
30259     getChildContainer: function( )
30260     {
30261         if (this.boxesEl) {
30262             return this.boxesEl;
30263         }
30264         
30265         this.boxesEl = this.el.select('.mas-boxes').first();
30266         
30267         return this.boxesEl;
30268     },
30269     
30270     
30271     initEvents : function()
30272     {
30273         var _this = this;
30274         
30275         if(this.isAutoInitial){
30276             Roo.log('hook children rendered');
30277             this.on('childrenrendered', function() {
30278                 Roo.log('children rendered');
30279                 _this.initial();
30280             } ,this);
30281         }
30282     },
30283     
30284     initial : function()
30285     {
30286         this.selectedBrick = [];
30287         
30288         this.currentSize = this.el.getBox(true);
30289         
30290         Roo.EventManager.onWindowResize(this.resize, this); 
30291
30292         if(!this.isAutoInitial){
30293             this.layout();
30294             return;
30295         }
30296         
30297         this.layout();
30298         
30299         return;
30300         //this.layout.defer(500,this);
30301         
30302     },
30303     
30304     resize : function()
30305     {
30306         var cs = this.el.getBox(true);
30307         
30308         if (
30309                 this.currentSize.width == cs.width && 
30310                 this.currentSize.x == cs.x && 
30311                 this.currentSize.height == cs.height && 
30312                 this.currentSize.y == cs.y 
30313         ) {
30314             Roo.log("no change in with or X or Y");
30315             return;
30316         }
30317         
30318         this.currentSize = cs;
30319         
30320         this.layout();
30321         
30322     },
30323     
30324     layout : function()
30325     {   
30326         this._resetLayout();
30327         
30328         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30329         
30330         this.layoutItems( isInstant );
30331       
30332         this._isLayoutInited = true;
30333         
30334         this.fireEvent('layout', this);
30335         
30336     },
30337     
30338     _resetLayout : function()
30339     {
30340         if(this.isHorizontal){
30341             this.horizontalMeasureColumns();
30342             return;
30343         }
30344         
30345         this.verticalMeasureColumns();
30346         
30347     },
30348     
30349     verticalMeasureColumns : function()
30350     {
30351         this.getContainerWidth();
30352         
30353 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30354 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30355 //            return;
30356 //        }
30357         
30358         var boxWidth = this.boxWidth + this.padWidth;
30359         
30360         if(this.containerWidth < this.boxWidth){
30361             boxWidth = this.containerWidth
30362         }
30363         
30364         var containerWidth = this.containerWidth;
30365         
30366         var cols = Math.floor(containerWidth / boxWidth);
30367         
30368         this.cols = Math.max( cols, 1 );
30369         
30370         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30371         
30372         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30373         
30374         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30375         
30376         this.colWidth = boxWidth + avail - this.padWidth;
30377         
30378         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30379         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30380     },
30381     
30382     horizontalMeasureColumns : function()
30383     {
30384         this.getContainerWidth();
30385         
30386         var boxWidth = this.boxWidth;
30387         
30388         if(this.containerWidth < boxWidth){
30389             boxWidth = this.containerWidth;
30390         }
30391         
30392         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30393         
30394         this.el.setHeight(boxWidth);
30395         
30396     },
30397     
30398     getContainerWidth : function()
30399     {
30400         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30401     },
30402     
30403     layoutItems : function( isInstant )
30404     {
30405         Roo.log(this.bricks);
30406         
30407         var items = Roo.apply([], this.bricks);
30408         
30409         if(this.isHorizontal){
30410             this._horizontalLayoutItems( items , isInstant );
30411             return;
30412         }
30413         
30414 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30415 //            this._verticalAlternativeLayoutItems( items , isInstant );
30416 //            return;
30417 //        }
30418         
30419         this._verticalLayoutItems( items , isInstant );
30420         
30421     },
30422     
30423     _verticalLayoutItems : function ( items , isInstant)
30424     {
30425         if ( !items || !items.length ) {
30426             return;
30427         }
30428         
30429         var standard = [
30430             ['xs', 'xs', 'xs', 'tall'],
30431             ['xs', 'xs', 'tall'],
30432             ['xs', 'xs', 'sm'],
30433             ['xs', 'xs', 'xs'],
30434             ['xs', 'tall'],
30435             ['xs', 'sm'],
30436             ['xs', 'xs'],
30437             ['xs'],
30438             
30439             ['sm', 'xs', 'xs'],
30440             ['sm', 'xs'],
30441             ['sm'],
30442             
30443             ['tall', 'xs', 'xs', 'xs'],
30444             ['tall', 'xs', 'xs'],
30445             ['tall', 'xs'],
30446             ['tall']
30447             
30448         ];
30449         
30450         var queue = [];
30451         
30452         var boxes = [];
30453         
30454         var box = [];
30455         
30456         Roo.each(items, function(item, k){
30457             
30458             switch (item.size) {
30459                 // these layouts take up a full box,
30460                 case 'md' :
30461                 case 'md-left' :
30462                 case 'md-right' :
30463                 case 'wide' :
30464                     
30465                     if(box.length){
30466                         boxes.push(box);
30467                         box = [];
30468                     }
30469                     
30470                     boxes.push([item]);
30471                     
30472                     break;
30473                     
30474                 case 'xs' :
30475                 case 'sm' :
30476                 case 'tall' :
30477                     
30478                     box.push(item);
30479                     
30480                     break;
30481                 default :
30482                     break;
30483                     
30484             }
30485             
30486         }, this);
30487         
30488         if(box.length){
30489             boxes.push(box);
30490             box = [];
30491         }
30492         
30493         var filterPattern = function(box, length)
30494         {
30495             if(!box.length){
30496                 return;
30497             }
30498             
30499             var match = false;
30500             
30501             var pattern = box.slice(0, length);
30502             
30503             var format = [];
30504             
30505             Roo.each(pattern, function(i){
30506                 format.push(i.size);
30507             }, this);
30508             
30509             Roo.each(standard, function(s){
30510                 
30511                 if(String(s) != String(format)){
30512                     return;
30513                 }
30514                 
30515                 match = true;
30516                 return false;
30517                 
30518             }, this);
30519             
30520             if(!match && length == 1){
30521                 return;
30522             }
30523             
30524             if(!match){
30525                 filterPattern(box, length - 1);
30526                 return;
30527             }
30528                 
30529             queue.push(pattern);
30530
30531             box = box.slice(length, box.length);
30532
30533             filterPattern(box, 4);
30534
30535             return;
30536             
30537         }
30538         
30539         Roo.each(boxes, function(box, k){
30540             
30541             if(!box.length){
30542                 return;
30543             }
30544             
30545             if(box.length == 1){
30546                 queue.push(box);
30547                 return;
30548             }
30549             
30550             filterPattern(box, 4);
30551             
30552         }, this);
30553         
30554         this._processVerticalLayoutQueue( queue, isInstant );
30555         
30556     },
30557     
30558 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30559 //    {
30560 //        if ( !items || !items.length ) {
30561 //            return;
30562 //        }
30563 //
30564 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30565 //        
30566 //    },
30567     
30568     _horizontalLayoutItems : function ( items , isInstant)
30569     {
30570         if ( !items || !items.length || items.length < 3) {
30571             return;
30572         }
30573         
30574         items.reverse();
30575         
30576         var eItems = items.slice(0, 3);
30577         
30578         items = items.slice(3, items.length);
30579         
30580         var standard = [
30581             ['xs', 'xs', 'xs', 'wide'],
30582             ['xs', 'xs', 'wide'],
30583             ['xs', 'xs', 'sm'],
30584             ['xs', 'xs', 'xs'],
30585             ['xs', 'wide'],
30586             ['xs', 'sm'],
30587             ['xs', 'xs'],
30588             ['xs'],
30589             
30590             ['sm', 'xs', 'xs'],
30591             ['sm', 'xs'],
30592             ['sm'],
30593             
30594             ['wide', 'xs', 'xs', 'xs'],
30595             ['wide', 'xs', 'xs'],
30596             ['wide', 'xs'],
30597             ['wide'],
30598             
30599             ['wide-thin']
30600         ];
30601         
30602         var queue = [];
30603         
30604         var boxes = [];
30605         
30606         var box = [];
30607         
30608         Roo.each(items, function(item, k){
30609             
30610             switch (item.size) {
30611                 case 'md' :
30612                 case 'md-left' :
30613                 case 'md-right' :
30614                 case 'tall' :
30615                     
30616                     if(box.length){
30617                         boxes.push(box);
30618                         box = [];
30619                     }
30620                     
30621                     boxes.push([item]);
30622                     
30623                     break;
30624                     
30625                 case 'xs' :
30626                 case 'sm' :
30627                 case 'wide' :
30628                 case 'wide-thin' :
30629                     
30630                     box.push(item);
30631                     
30632                     break;
30633                 default :
30634                     break;
30635                     
30636             }
30637             
30638         }, this);
30639         
30640         if(box.length){
30641             boxes.push(box);
30642             box = [];
30643         }
30644         
30645         var filterPattern = function(box, length)
30646         {
30647             if(!box.length){
30648                 return;
30649             }
30650             
30651             var match = false;
30652             
30653             var pattern = box.slice(0, length);
30654             
30655             var format = [];
30656             
30657             Roo.each(pattern, function(i){
30658                 format.push(i.size);
30659             }, this);
30660             
30661             Roo.each(standard, function(s){
30662                 
30663                 if(String(s) != String(format)){
30664                     return;
30665                 }
30666                 
30667                 match = true;
30668                 return false;
30669                 
30670             }, this);
30671             
30672             if(!match && length == 1){
30673                 return;
30674             }
30675             
30676             if(!match){
30677                 filterPattern(box, length - 1);
30678                 return;
30679             }
30680                 
30681             queue.push(pattern);
30682
30683             box = box.slice(length, box.length);
30684
30685             filterPattern(box, 4);
30686
30687             return;
30688             
30689         }
30690         
30691         Roo.each(boxes, function(box, k){
30692             
30693             if(!box.length){
30694                 return;
30695             }
30696             
30697             if(box.length == 1){
30698                 queue.push(box);
30699                 return;
30700             }
30701             
30702             filterPattern(box, 4);
30703             
30704         }, this);
30705         
30706         
30707         var prune = [];
30708         
30709         var pos = this.el.getBox(true);
30710         
30711         var minX = pos.x;
30712         
30713         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30714         
30715         var hit_end = false;
30716         
30717         Roo.each(queue, function(box){
30718             
30719             if(hit_end){
30720                 
30721                 Roo.each(box, function(b){
30722                 
30723                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30724                     b.el.hide();
30725
30726                 }, this);
30727
30728                 return;
30729             }
30730             
30731             var mx = 0;
30732             
30733             Roo.each(box, function(b){
30734                 
30735                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30736                 b.el.show();
30737
30738                 mx = Math.max(mx, b.x);
30739                 
30740             }, this);
30741             
30742             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30743             
30744             if(maxX < minX){
30745                 
30746                 Roo.each(box, function(b){
30747                 
30748                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30749                     b.el.hide();
30750                     
30751                 }, this);
30752                 
30753                 hit_end = true;
30754                 
30755                 return;
30756             }
30757             
30758             prune.push(box);
30759             
30760         }, this);
30761         
30762         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30763     },
30764     
30765     /** Sets position of item in DOM
30766     * @param {Element} item
30767     * @param {Number} x - horizontal position
30768     * @param {Number} y - vertical position
30769     * @param {Boolean} isInstant - disables transitions
30770     */
30771     _processVerticalLayoutQueue : function( queue, isInstant )
30772     {
30773         var pos = this.el.getBox(true);
30774         var x = pos.x;
30775         var y = pos.y;
30776         var maxY = [];
30777         
30778         for (var i = 0; i < this.cols; i++){
30779             maxY[i] = pos.y;
30780         }
30781         
30782         Roo.each(queue, function(box, k){
30783             
30784             var col = k % this.cols;
30785             
30786             Roo.each(box, function(b,kk){
30787                 
30788                 b.el.position('absolute');
30789                 
30790                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30791                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30792                 
30793                 if(b.size == 'md-left' || b.size == 'md-right'){
30794                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30795                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30796                 }
30797                 
30798                 b.el.setWidth(width);
30799                 b.el.setHeight(height);
30800                 // iframe?
30801                 b.el.select('iframe',true).setSize(width,height);
30802                 
30803             }, this);
30804             
30805             for (var i = 0; i < this.cols; i++){
30806                 
30807                 if(maxY[i] < maxY[col]){
30808                     col = i;
30809                     continue;
30810                 }
30811                 
30812                 col = Math.min(col, i);
30813                 
30814             }
30815             
30816             x = pos.x + col * (this.colWidth + this.padWidth);
30817             
30818             y = maxY[col];
30819             
30820             var positions = [];
30821             
30822             switch (box.length){
30823                 case 1 :
30824                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30825                     break;
30826                 case 2 :
30827                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30828                     break;
30829                 case 3 :
30830                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30831                     break;
30832                 case 4 :
30833                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30834                     break;
30835                 default :
30836                     break;
30837             }
30838             
30839             Roo.each(box, function(b,kk){
30840                 
30841                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30842                 
30843                 var sz = b.el.getSize();
30844                 
30845                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30846                 
30847             }, this);
30848             
30849         }, this);
30850         
30851         var mY = 0;
30852         
30853         for (var i = 0; i < this.cols; i++){
30854             mY = Math.max(mY, maxY[i]);
30855         }
30856         
30857         this.el.setHeight(mY - pos.y);
30858         
30859     },
30860     
30861 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30862 //    {
30863 //        var pos = this.el.getBox(true);
30864 //        var x = pos.x;
30865 //        var y = pos.y;
30866 //        var maxX = pos.right;
30867 //        
30868 //        var maxHeight = 0;
30869 //        
30870 //        Roo.each(items, function(item, k){
30871 //            
30872 //            var c = k % 2;
30873 //            
30874 //            item.el.position('absolute');
30875 //                
30876 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30877 //
30878 //            item.el.setWidth(width);
30879 //
30880 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30881 //
30882 //            item.el.setHeight(height);
30883 //            
30884 //            if(c == 0){
30885 //                item.el.setXY([x, y], isInstant ? false : true);
30886 //            } else {
30887 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30888 //            }
30889 //            
30890 //            y = y + height + this.alternativePadWidth;
30891 //            
30892 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30893 //            
30894 //        }, this);
30895 //        
30896 //        this.el.setHeight(maxHeight);
30897 //        
30898 //    },
30899     
30900     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30901     {
30902         var pos = this.el.getBox(true);
30903         
30904         var minX = pos.x;
30905         var minY = pos.y;
30906         
30907         var maxX = pos.right;
30908         
30909         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30910         
30911         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30912         
30913         Roo.each(queue, function(box, k){
30914             
30915             Roo.each(box, function(b, kk){
30916                 
30917                 b.el.position('absolute');
30918                 
30919                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30920                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30921                 
30922                 if(b.size == 'md-left' || b.size == 'md-right'){
30923                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30924                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30925                 }
30926                 
30927                 b.el.setWidth(width);
30928                 b.el.setHeight(height);
30929                 
30930             }, this);
30931             
30932             if(!box.length){
30933                 return;
30934             }
30935             
30936             var positions = [];
30937             
30938             switch (box.length){
30939                 case 1 :
30940                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30941                     break;
30942                 case 2 :
30943                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30944                     break;
30945                 case 3 :
30946                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30947                     break;
30948                 case 4 :
30949                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30950                     break;
30951                 default :
30952                     break;
30953             }
30954             
30955             Roo.each(box, function(b,kk){
30956                 
30957                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30958                 
30959                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30960                 
30961             }, this);
30962             
30963         }, this);
30964         
30965     },
30966     
30967     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30968     {
30969         Roo.each(eItems, function(b,k){
30970             
30971             b.size = (k == 0) ? 'sm' : 'xs';
30972             b.x = (k == 0) ? 2 : 1;
30973             b.y = (k == 0) ? 2 : 1;
30974             
30975             b.el.position('absolute');
30976             
30977             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30978                 
30979             b.el.setWidth(width);
30980             
30981             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30982             
30983             b.el.setHeight(height);
30984             
30985         }, this);
30986
30987         var positions = [];
30988         
30989         positions.push({
30990             x : maxX - this.unitWidth * 2 - this.gutter,
30991             y : minY
30992         });
30993         
30994         positions.push({
30995             x : maxX - this.unitWidth,
30996             y : minY + (this.unitWidth + this.gutter) * 2
30997         });
30998         
30999         positions.push({
31000             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31001             y : minY
31002         });
31003         
31004         Roo.each(eItems, function(b,k){
31005             
31006             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31007
31008         }, this);
31009         
31010     },
31011     
31012     getVerticalOneBoxColPositions : function(x, y, box)
31013     {
31014         var pos = [];
31015         
31016         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31017         
31018         if(box[0].size == 'md-left'){
31019             rand = 0;
31020         }
31021         
31022         if(box[0].size == 'md-right'){
31023             rand = 1;
31024         }
31025         
31026         pos.push({
31027             x : x + (this.unitWidth + this.gutter) * rand,
31028             y : y
31029         });
31030         
31031         return pos;
31032     },
31033     
31034     getVerticalTwoBoxColPositions : function(x, y, box)
31035     {
31036         var pos = [];
31037         
31038         if(box[0].size == 'xs'){
31039             
31040             pos.push({
31041                 x : x,
31042                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31043             });
31044
31045             pos.push({
31046                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31047                 y : y
31048             });
31049             
31050             return pos;
31051             
31052         }
31053         
31054         pos.push({
31055             x : x,
31056             y : y
31057         });
31058
31059         pos.push({
31060             x : x + (this.unitWidth + this.gutter) * 2,
31061             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31062         });
31063         
31064         return pos;
31065         
31066     },
31067     
31068     getVerticalThreeBoxColPositions : function(x, y, box)
31069     {
31070         var pos = [];
31071         
31072         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31073             
31074             pos.push({
31075                 x : x,
31076                 y : y
31077             });
31078
31079             pos.push({
31080                 x : x + (this.unitWidth + this.gutter) * 1,
31081                 y : y
31082             });
31083             
31084             pos.push({
31085                 x : x + (this.unitWidth + this.gutter) * 2,
31086                 y : y
31087             });
31088             
31089             return pos;
31090             
31091         }
31092         
31093         if(box[0].size == 'xs' && box[1].size == 'xs'){
31094             
31095             pos.push({
31096                 x : x,
31097                 y : y
31098             });
31099
31100             pos.push({
31101                 x : x,
31102                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31103             });
31104             
31105             pos.push({
31106                 x : x + (this.unitWidth + this.gutter) * 1,
31107                 y : y
31108             });
31109             
31110             return pos;
31111             
31112         }
31113         
31114         pos.push({
31115             x : x,
31116             y : y
31117         });
31118
31119         pos.push({
31120             x : x + (this.unitWidth + this.gutter) * 2,
31121             y : y
31122         });
31123
31124         pos.push({
31125             x : x + (this.unitWidth + this.gutter) * 2,
31126             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31127         });
31128             
31129         return pos;
31130         
31131     },
31132     
31133     getVerticalFourBoxColPositions : function(x, y, box)
31134     {
31135         var pos = [];
31136         
31137         if(box[0].size == 'xs'){
31138             
31139             pos.push({
31140                 x : x,
31141                 y : y
31142             });
31143
31144             pos.push({
31145                 x : x,
31146                 y : y + (this.unitHeight + this.gutter) * 1
31147             });
31148             
31149             pos.push({
31150                 x : x,
31151                 y : y + (this.unitHeight + this.gutter) * 2
31152             });
31153             
31154             pos.push({
31155                 x : x + (this.unitWidth + this.gutter) * 1,
31156                 y : y
31157             });
31158             
31159             return pos;
31160             
31161         }
31162         
31163         pos.push({
31164             x : x,
31165             y : y
31166         });
31167
31168         pos.push({
31169             x : x + (this.unitWidth + this.gutter) * 2,
31170             y : y
31171         });
31172
31173         pos.push({
31174             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31175             y : y + (this.unitHeight + this.gutter) * 1
31176         });
31177
31178         pos.push({
31179             x : x + (this.unitWidth + this.gutter) * 2,
31180             y : y + (this.unitWidth + this.gutter) * 2
31181         });
31182
31183         return pos;
31184         
31185     },
31186     
31187     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31188     {
31189         var pos = [];
31190         
31191         if(box[0].size == 'md-left'){
31192             pos.push({
31193                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31194                 y : minY
31195             });
31196             
31197             return pos;
31198         }
31199         
31200         if(box[0].size == 'md-right'){
31201             pos.push({
31202                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31203                 y : minY + (this.unitWidth + this.gutter) * 1
31204             });
31205             
31206             return pos;
31207         }
31208         
31209         var rand = Math.floor(Math.random() * (4 - box[0].y));
31210         
31211         pos.push({
31212             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31213             y : minY + (this.unitWidth + this.gutter) * rand
31214         });
31215         
31216         return pos;
31217         
31218     },
31219     
31220     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31221     {
31222         var pos = [];
31223         
31224         if(box[0].size == 'xs'){
31225             
31226             pos.push({
31227                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31228                 y : minY
31229             });
31230
31231             pos.push({
31232                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31233                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31234             });
31235             
31236             return pos;
31237             
31238         }
31239         
31240         pos.push({
31241             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31242             y : minY
31243         });
31244
31245         pos.push({
31246             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31247             y : minY + (this.unitWidth + this.gutter) * 2
31248         });
31249         
31250         return pos;
31251         
31252     },
31253     
31254     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31255     {
31256         var pos = [];
31257         
31258         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31259             
31260             pos.push({
31261                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31262                 y : minY
31263             });
31264
31265             pos.push({
31266                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31267                 y : minY + (this.unitWidth + this.gutter) * 1
31268             });
31269             
31270             pos.push({
31271                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31272                 y : minY + (this.unitWidth + this.gutter) * 2
31273             });
31274             
31275             return pos;
31276             
31277         }
31278         
31279         if(box[0].size == 'xs' && box[1].size == 'xs'){
31280             
31281             pos.push({
31282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31283                 y : minY
31284             });
31285
31286             pos.push({
31287                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31288                 y : minY
31289             });
31290             
31291             pos.push({
31292                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31293                 y : minY + (this.unitWidth + this.gutter) * 1
31294             });
31295             
31296             return pos;
31297             
31298         }
31299         
31300         pos.push({
31301             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31302             y : minY
31303         });
31304
31305         pos.push({
31306             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31307             y : minY + (this.unitWidth + this.gutter) * 2
31308         });
31309
31310         pos.push({
31311             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31312             y : minY + (this.unitWidth + this.gutter) * 2
31313         });
31314             
31315         return pos;
31316         
31317     },
31318     
31319     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31320     {
31321         var pos = [];
31322         
31323         if(box[0].size == 'xs'){
31324             
31325             pos.push({
31326                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31327                 y : minY
31328             });
31329
31330             pos.push({
31331                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31332                 y : minY
31333             });
31334             
31335             pos.push({
31336                 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),
31337                 y : minY
31338             });
31339             
31340             pos.push({
31341                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31342                 y : minY + (this.unitWidth + this.gutter) * 1
31343             });
31344             
31345             return pos;
31346             
31347         }
31348         
31349         pos.push({
31350             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31351             y : minY
31352         });
31353         
31354         pos.push({
31355             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31356             y : minY + (this.unitWidth + this.gutter) * 2
31357         });
31358         
31359         pos.push({
31360             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31361             y : minY + (this.unitWidth + this.gutter) * 2
31362         });
31363         
31364         pos.push({
31365             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),
31366             y : minY + (this.unitWidth + this.gutter) * 2
31367         });
31368
31369         return pos;
31370         
31371     },
31372     
31373     /**
31374     * remove a Masonry Brick
31375     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31376     */
31377     removeBrick : function(brick_id)
31378     {
31379         if (!brick_id) {
31380             return;
31381         }
31382         
31383         for (var i = 0; i<this.bricks.length; i++) {
31384             if (this.bricks[i].id == brick_id) {
31385                 this.bricks.splice(i,1);
31386                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31387                 this.initial();
31388             }
31389         }
31390     },
31391     
31392     /**
31393     * adds a Masonry Brick
31394     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31395     */
31396     addBrick : function(cfg)
31397     {
31398         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31399         //this.register(cn);
31400         cn.parentId = this.id;
31401         cn.onRender(this.el, null);
31402         return cn;
31403     },
31404     
31405     /**
31406     * register a Masonry Brick
31407     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31408     */
31409     
31410     register : function(brick)
31411     {
31412         this.bricks.push(brick);
31413         brick.masonryId = this.id;
31414     },
31415     
31416     /**
31417     * clear all the Masonry Brick
31418     */
31419     clearAll : function()
31420     {
31421         this.bricks = [];
31422         //this.getChildContainer().dom.innerHTML = "";
31423         this.el.dom.innerHTML = '';
31424     },
31425     
31426     getSelected : function()
31427     {
31428         if (!this.selectedBrick) {
31429             return false;
31430         }
31431         
31432         return this.selectedBrick;
31433     }
31434 });
31435
31436 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31437     
31438     groups: {},
31439      /**
31440     * register a Masonry Layout
31441     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31442     */
31443     
31444     register : function(layout)
31445     {
31446         this.groups[layout.id] = layout;
31447     },
31448     /**
31449     * fetch a  Masonry Layout based on the masonry layout ID
31450     * @param {string} the masonry layout to add
31451     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31452     */
31453     
31454     get: function(layout_id) {
31455         if (typeof(this.groups[layout_id]) == 'undefined') {
31456             return false;
31457         }
31458         return this.groups[layout_id] ;
31459     }
31460     
31461     
31462     
31463 });
31464
31465  
31466
31467  /**
31468  *
31469  * This is based on 
31470  * http://masonry.desandro.com
31471  *
31472  * The idea is to render all the bricks based on vertical width...
31473  *
31474  * The original code extends 'outlayer' - we might need to use that....
31475  * 
31476  */
31477
31478
31479 /**
31480  * @class Roo.bootstrap.LayoutMasonryAuto
31481  * @extends Roo.bootstrap.Component
31482  * Bootstrap Layout Masonry class
31483  * 
31484  * @constructor
31485  * Create a new Element
31486  * @param {Object} config The config object
31487  */
31488
31489 Roo.bootstrap.LayoutMasonryAuto = function(config){
31490     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31491 };
31492
31493 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31494     
31495       /**
31496      * @cfg {Boolean} isFitWidth  - resize the width..
31497      */   
31498     isFitWidth : false,  // options..
31499     /**
31500      * @cfg {Boolean} isOriginLeft = left align?
31501      */   
31502     isOriginLeft : true,
31503     /**
31504      * @cfg {Boolean} isOriginTop = top align?
31505      */   
31506     isOriginTop : false,
31507     /**
31508      * @cfg {Boolean} isLayoutInstant = no animation?
31509      */   
31510     isLayoutInstant : false, // needed?
31511     /**
31512      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31513      */   
31514     isResizingContainer : true,
31515     /**
31516      * @cfg {Number} columnWidth  width of the columns 
31517      */   
31518     
31519     columnWidth : 0,
31520     
31521     /**
31522      * @cfg {Number} maxCols maximum number of columns
31523      */   
31524     
31525     maxCols: 0,
31526     /**
31527      * @cfg {Number} padHeight padding below box..
31528      */   
31529     
31530     padHeight : 10, 
31531     
31532     /**
31533      * @cfg {Boolean} isAutoInitial defalut true
31534      */   
31535     
31536     isAutoInitial : true, 
31537     
31538     // private?
31539     gutter : 0,
31540     
31541     containerWidth: 0,
31542     initialColumnWidth : 0,
31543     currentSize : null,
31544     
31545     colYs : null, // array.
31546     maxY : 0,
31547     padWidth: 10,
31548     
31549     
31550     tag: 'div',
31551     cls: '',
31552     bricks: null, //CompositeElement
31553     cols : 0, // array?
31554     // element : null, // wrapped now this.el
31555     _isLayoutInited : null, 
31556     
31557     
31558     getAutoCreate : function(){
31559         
31560         var cfg = {
31561             tag: this.tag,
31562             cls: 'blog-masonary-wrapper ' + this.cls,
31563             cn : {
31564                 cls : 'mas-boxes masonary'
31565             }
31566         };
31567         
31568         return cfg;
31569     },
31570     
31571     getChildContainer: function( )
31572     {
31573         if (this.boxesEl) {
31574             return this.boxesEl;
31575         }
31576         
31577         this.boxesEl = this.el.select('.mas-boxes').first();
31578         
31579         return this.boxesEl;
31580     },
31581     
31582     
31583     initEvents : function()
31584     {
31585         var _this = this;
31586         
31587         if(this.isAutoInitial){
31588             Roo.log('hook children rendered');
31589             this.on('childrenrendered', function() {
31590                 Roo.log('children rendered');
31591                 _this.initial();
31592             } ,this);
31593         }
31594         
31595     },
31596     
31597     initial : function()
31598     {
31599         this.reloadItems();
31600
31601         this.currentSize = this.el.getBox(true);
31602
31603         /// was window resize... - let's see if this works..
31604         Roo.EventManager.onWindowResize(this.resize, this); 
31605
31606         if(!this.isAutoInitial){
31607             this.layout();
31608             return;
31609         }
31610         
31611         this.layout.defer(500,this);
31612     },
31613     
31614     reloadItems: function()
31615     {
31616         this.bricks = this.el.select('.masonry-brick', true);
31617         
31618         this.bricks.each(function(b) {
31619             //Roo.log(b.getSize());
31620             if (!b.attr('originalwidth')) {
31621                 b.attr('originalwidth',  b.getSize().width);
31622             }
31623             
31624         });
31625         
31626         Roo.log(this.bricks.elements.length);
31627     },
31628     
31629     resize : function()
31630     {
31631         Roo.log('resize');
31632         var cs = this.el.getBox(true);
31633         
31634         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31635             Roo.log("no change in with or X");
31636             return;
31637         }
31638         this.currentSize = cs;
31639         this.layout();
31640     },
31641     
31642     layout : function()
31643     {
31644          Roo.log('layout');
31645         this._resetLayout();
31646         //this._manageStamps();
31647       
31648         // don't animate first layout
31649         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31650         this.layoutItems( isInstant );
31651       
31652         // flag for initalized
31653         this._isLayoutInited = true;
31654     },
31655     
31656     layoutItems : function( isInstant )
31657     {
31658         //var items = this._getItemsForLayout( this.items );
31659         // original code supports filtering layout items.. we just ignore it..
31660         
31661         this._layoutItems( this.bricks , isInstant );
31662       
31663         this._postLayout();
31664     },
31665     _layoutItems : function ( items , isInstant)
31666     {
31667        //this.fireEvent( 'layout', this, items );
31668     
31669
31670         if ( !items || !items.elements.length ) {
31671           // no items, emit event with empty array
31672             return;
31673         }
31674
31675         var queue = [];
31676         items.each(function(item) {
31677             Roo.log("layout item");
31678             Roo.log(item);
31679             // get x/y object from method
31680             var position = this._getItemLayoutPosition( item );
31681             // enqueue
31682             position.item = item;
31683             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31684             queue.push( position );
31685         }, this);
31686       
31687         this._processLayoutQueue( queue );
31688     },
31689     /** Sets position of item in DOM
31690     * @param {Element} item
31691     * @param {Number} x - horizontal position
31692     * @param {Number} y - vertical position
31693     * @param {Boolean} isInstant - disables transitions
31694     */
31695     _processLayoutQueue : function( queue )
31696     {
31697         for ( var i=0, len = queue.length; i < len; i++ ) {
31698             var obj = queue[i];
31699             obj.item.position('absolute');
31700             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31701         }
31702     },
31703       
31704     
31705     /**
31706     * Any logic you want to do after each layout,
31707     * i.e. size the container
31708     */
31709     _postLayout : function()
31710     {
31711         this.resizeContainer();
31712     },
31713     
31714     resizeContainer : function()
31715     {
31716         if ( !this.isResizingContainer ) {
31717             return;
31718         }
31719         var size = this._getContainerSize();
31720         if ( size ) {
31721             this.el.setSize(size.width,size.height);
31722             this.boxesEl.setSize(size.width,size.height);
31723         }
31724     },
31725     
31726     
31727     
31728     _resetLayout : function()
31729     {
31730         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31731         this.colWidth = this.el.getWidth();
31732         //this.gutter = this.el.getWidth(); 
31733         
31734         this.measureColumns();
31735
31736         // reset column Y
31737         var i = this.cols;
31738         this.colYs = [];
31739         while (i--) {
31740             this.colYs.push( 0 );
31741         }
31742     
31743         this.maxY = 0;
31744     },
31745
31746     measureColumns : function()
31747     {
31748         this.getContainerWidth();
31749       // if columnWidth is 0, default to outerWidth of first item
31750         if ( !this.columnWidth ) {
31751             var firstItem = this.bricks.first();
31752             Roo.log(firstItem);
31753             this.columnWidth  = this.containerWidth;
31754             if (firstItem && firstItem.attr('originalwidth') ) {
31755                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31756             }
31757             // columnWidth fall back to item of first element
31758             Roo.log("set column width?");
31759                         this.initialColumnWidth = this.columnWidth  ;
31760
31761             // if first elem has no width, default to size of container
31762             
31763         }
31764         
31765         
31766         if (this.initialColumnWidth) {
31767             this.columnWidth = this.initialColumnWidth;
31768         }
31769         
31770         
31771             
31772         // column width is fixed at the top - however if container width get's smaller we should
31773         // reduce it...
31774         
31775         // this bit calcs how man columns..
31776             
31777         var columnWidth = this.columnWidth += this.gutter;
31778       
31779         // calculate columns
31780         var containerWidth = this.containerWidth + this.gutter;
31781         
31782         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31783         // fix rounding errors, typically with gutters
31784         var excess = columnWidth - containerWidth % columnWidth;
31785         
31786         
31787         // if overshoot is less than a pixel, round up, otherwise floor it
31788         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31789         cols = Math[ mathMethod ]( cols );
31790         this.cols = Math.max( cols, 1 );
31791         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31792         
31793          // padding positioning..
31794         var totalColWidth = this.cols * this.columnWidth;
31795         var padavail = this.containerWidth - totalColWidth;
31796         // so for 2 columns - we need 3 'pads'
31797         
31798         var padNeeded = (1+this.cols) * this.padWidth;
31799         
31800         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31801         
31802         this.columnWidth += padExtra
31803         //this.padWidth = Math.floor(padavail /  ( this.cols));
31804         
31805         // adjust colum width so that padding is fixed??
31806         
31807         // we have 3 columns ... total = width * 3
31808         // we have X left over... that should be used by 
31809         
31810         //if (this.expandC) {
31811             
31812         //}
31813         
31814         
31815         
31816     },
31817     
31818     getContainerWidth : function()
31819     {
31820        /* // container is parent if fit width
31821         var container = this.isFitWidth ? this.element.parentNode : this.element;
31822         // check that this.size and size are there
31823         // IE8 triggers resize on body size change, so they might not be
31824         
31825         var size = getSize( container );  //FIXME
31826         this.containerWidth = size && size.innerWidth; //FIXME
31827         */
31828          
31829         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31830         
31831     },
31832     
31833     _getItemLayoutPosition : function( item )  // what is item?
31834     {
31835         // we resize the item to our columnWidth..
31836       
31837         item.setWidth(this.columnWidth);
31838         item.autoBoxAdjust  = false;
31839         
31840         var sz = item.getSize();
31841  
31842         // how many columns does this brick span
31843         var remainder = this.containerWidth % this.columnWidth;
31844         
31845         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31846         // round if off by 1 pixel, otherwise use ceil
31847         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31848         colSpan = Math.min( colSpan, this.cols );
31849         
31850         // normally this should be '1' as we dont' currently allow multi width columns..
31851         
31852         var colGroup = this._getColGroup( colSpan );
31853         // get the minimum Y value from the columns
31854         var minimumY = Math.min.apply( Math, colGroup );
31855         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31856         
31857         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31858          
31859         // position the brick
31860         var position = {
31861             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31862             y: this.currentSize.y + minimumY + this.padHeight
31863         };
31864         
31865         Roo.log(position);
31866         // apply setHeight to necessary columns
31867         var setHeight = minimumY + sz.height + this.padHeight;
31868         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31869         
31870         var setSpan = this.cols + 1 - colGroup.length;
31871         for ( var i = 0; i < setSpan; i++ ) {
31872           this.colYs[ shortColIndex + i ] = setHeight ;
31873         }
31874       
31875         return position;
31876     },
31877     
31878     /**
31879      * @param {Number} colSpan - number of columns the element spans
31880      * @returns {Array} colGroup
31881      */
31882     _getColGroup : function( colSpan )
31883     {
31884         if ( colSpan < 2 ) {
31885           // if brick spans only one column, use all the column Ys
31886           return this.colYs;
31887         }
31888       
31889         var colGroup = [];
31890         // how many different places could this brick fit horizontally
31891         var groupCount = this.cols + 1 - colSpan;
31892         // for each group potential horizontal position
31893         for ( var i = 0; i < groupCount; i++ ) {
31894           // make an array of colY values for that one group
31895           var groupColYs = this.colYs.slice( i, i + colSpan );
31896           // and get the max value of the array
31897           colGroup[i] = Math.max.apply( Math, groupColYs );
31898         }
31899         return colGroup;
31900     },
31901     /*
31902     _manageStamp : function( stamp )
31903     {
31904         var stampSize =  stamp.getSize();
31905         var offset = stamp.getBox();
31906         // get the columns that this stamp affects
31907         var firstX = this.isOriginLeft ? offset.x : offset.right;
31908         var lastX = firstX + stampSize.width;
31909         var firstCol = Math.floor( firstX / this.columnWidth );
31910         firstCol = Math.max( 0, firstCol );
31911         
31912         var lastCol = Math.floor( lastX / this.columnWidth );
31913         // lastCol should not go over if multiple of columnWidth #425
31914         lastCol -= lastX % this.columnWidth ? 0 : 1;
31915         lastCol = Math.min( this.cols - 1, lastCol );
31916         
31917         // set colYs to bottom of the stamp
31918         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31919             stampSize.height;
31920             
31921         for ( var i = firstCol; i <= lastCol; i++ ) {
31922           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31923         }
31924     },
31925     */
31926     
31927     _getContainerSize : function()
31928     {
31929         this.maxY = Math.max.apply( Math, this.colYs );
31930         var size = {
31931             height: this.maxY
31932         };
31933       
31934         if ( this.isFitWidth ) {
31935             size.width = this._getContainerFitWidth();
31936         }
31937       
31938         return size;
31939     },
31940     
31941     _getContainerFitWidth : function()
31942     {
31943         var unusedCols = 0;
31944         // count unused columns
31945         var i = this.cols;
31946         while ( --i ) {
31947           if ( this.colYs[i] !== 0 ) {
31948             break;
31949           }
31950           unusedCols++;
31951         }
31952         // fit container to columns that have been used
31953         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31954     },
31955     
31956     needsResizeLayout : function()
31957     {
31958         var previousWidth = this.containerWidth;
31959         this.getContainerWidth();
31960         return previousWidth !== this.containerWidth;
31961     }
31962  
31963 });
31964
31965  
31966
31967  /*
31968  * - LGPL
31969  *
31970  * element
31971  * 
31972  */
31973
31974 /**
31975  * @class Roo.bootstrap.MasonryBrick
31976  * @extends Roo.bootstrap.Component
31977  * Bootstrap MasonryBrick class
31978  * 
31979  * @constructor
31980  * Create a new MasonryBrick
31981  * @param {Object} config The config object
31982  */
31983
31984 Roo.bootstrap.MasonryBrick = function(config){
31985     
31986     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31987     
31988     Roo.bootstrap.MasonryBrick.register(this);
31989     
31990     this.addEvents({
31991         // raw events
31992         /**
31993          * @event click
31994          * When a MasonryBrick is clcik
31995          * @param {Roo.bootstrap.MasonryBrick} this
31996          * @param {Roo.EventObject} e
31997          */
31998         "click" : true
31999     });
32000 };
32001
32002 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32003     
32004     /**
32005      * @cfg {String} title
32006      */   
32007     title : '',
32008     /**
32009      * @cfg {String} html
32010      */   
32011     html : '',
32012     /**
32013      * @cfg {String} bgimage
32014      */   
32015     bgimage : '',
32016     /**
32017      * @cfg {String} videourl
32018      */   
32019     videourl : '',
32020     /**
32021      * @cfg {String} cls
32022      */   
32023     cls : '',
32024     /**
32025      * @cfg {String} href
32026      */   
32027     href : '',
32028     /**
32029      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32030      */   
32031     size : 'xs',
32032     
32033     /**
32034      * @cfg {String} placetitle (center|bottom)
32035      */   
32036     placetitle : '',
32037     
32038     /**
32039      * @cfg {Boolean} isFitContainer defalut true
32040      */   
32041     isFitContainer : true, 
32042     
32043     /**
32044      * @cfg {Boolean} preventDefault defalut false
32045      */   
32046     preventDefault : false, 
32047     
32048     /**
32049      * @cfg {Boolean} inverse defalut false
32050      */   
32051     maskInverse : false, 
32052     
32053     getAutoCreate : function()
32054     {
32055         if(!this.isFitContainer){
32056             return this.getSplitAutoCreate();
32057         }
32058         
32059         var cls = 'masonry-brick masonry-brick-full';
32060         
32061         if(this.href.length){
32062             cls += ' masonry-brick-link';
32063         }
32064         
32065         if(this.bgimage.length){
32066             cls += ' masonry-brick-image';
32067         }
32068         
32069         if(this.maskInverse){
32070             cls += ' mask-inverse';
32071         }
32072         
32073         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32074             cls += ' enable-mask';
32075         }
32076         
32077         if(this.size){
32078             cls += ' masonry-' + this.size + '-brick';
32079         }
32080         
32081         if(this.placetitle.length){
32082             
32083             switch (this.placetitle) {
32084                 case 'center' :
32085                     cls += ' masonry-center-title';
32086                     break;
32087                 case 'bottom' :
32088                     cls += ' masonry-bottom-title';
32089                     break;
32090                 default:
32091                     break;
32092             }
32093             
32094         } else {
32095             if(!this.html.length && !this.bgimage.length){
32096                 cls += ' masonry-center-title';
32097             }
32098
32099             if(!this.html.length && this.bgimage.length){
32100                 cls += ' masonry-bottom-title';
32101             }
32102         }
32103         
32104         if(this.cls){
32105             cls += ' ' + this.cls;
32106         }
32107         
32108         var cfg = {
32109             tag: (this.href.length) ? 'a' : 'div',
32110             cls: cls,
32111             cn: [
32112                 {
32113                     tag: 'div',
32114                     cls: 'masonry-brick-mask'
32115                 },
32116                 {
32117                     tag: 'div',
32118                     cls: 'masonry-brick-paragraph',
32119                     cn: []
32120                 }
32121             ]
32122         };
32123         
32124         if(this.href.length){
32125             cfg.href = this.href;
32126         }
32127         
32128         var cn = cfg.cn[1].cn;
32129         
32130         if(this.title.length){
32131             cn.push({
32132                 tag: 'h4',
32133                 cls: 'masonry-brick-title',
32134                 html: this.title
32135             });
32136         }
32137         
32138         if(this.html.length){
32139             cn.push({
32140                 tag: 'p',
32141                 cls: 'masonry-brick-text',
32142                 html: this.html
32143             });
32144         }
32145         
32146         if (!this.title.length && !this.html.length) {
32147             cfg.cn[1].cls += ' hide';
32148         }
32149         
32150         if(this.bgimage.length){
32151             cfg.cn.push({
32152                 tag: 'img',
32153                 cls: 'masonry-brick-image-view',
32154                 src: this.bgimage
32155             });
32156         }
32157         
32158         if(this.videourl.length){
32159             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32160             // youtube support only?
32161             cfg.cn.push({
32162                 tag: 'iframe',
32163                 cls: 'masonry-brick-image-view',
32164                 src: vurl,
32165                 frameborder : 0,
32166                 allowfullscreen : true
32167             });
32168         }
32169         
32170         return cfg;
32171         
32172     },
32173     
32174     getSplitAutoCreate : function()
32175     {
32176         var cls = 'masonry-brick masonry-brick-split';
32177         
32178         if(this.href.length){
32179             cls += ' masonry-brick-link';
32180         }
32181         
32182         if(this.bgimage.length){
32183             cls += ' masonry-brick-image';
32184         }
32185         
32186         if(this.size){
32187             cls += ' masonry-' + this.size + '-brick';
32188         }
32189         
32190         switch (this.placetitle) {
32191             case 'center' :
32192                 cls += ' masonry-center-title';
32193                 break;
32194             case 'bottom' :
32195                 cls += ' masonry-bottom-title';
32196                 break;
32197             default:
32198                 if(!this.bgimage.length){
32199                     cls += ' masonry-center-title';
32200                 }
32201
32202                 if(this.bgimage.length){
32203                     cls += ' masonry-bottom-title';
32204                 }
32205                 break;
32206         }
32207         
32208         if(this.cls){
32209             cls += ' ' + this.cls;
32210         }
32211         
32212         var cfg = {
32213             tag: (this.href.length) ? 'a' : 'div',
32214             cls: cls,
32215             cn: [
32216                 {
32217                     tag: 'div',
32218                     cls: 'masonry-brick-split-head',
32219                     cn: [
32220                         {
32221                             tag: 'div',
32222                             cls: 'masonry-brick-paragraph',
32223                             cn: []
32224                         }
32225                     ]
32226                 },
32227                 {
32228                     tag: 'div',
32229                     cls: 'masonry-brick-split-body',
32230                     cn: []
32231                 }
32232             ]
32233         };
32234         
32235         if(this.href.length){
32236             cfg.href = this.href;
32237         }
32238         
32239         if(this.title.length){
32240             cfg.cn[0].cn[0].cn.push({
32241                 tag: 'h4',
32242                 cls: 'masonry-brick-title',
32243                 html: this.title
32244             });
32245         }
32246         
32247         if(this.html.length){
32248             cfg.cn[1].cn.push({
32249                 tag: 'p',
32250                 cls: 'masonry-brick-text',
32251                 html: this.html
32252             });
32253         }
32254
32255         if(this.bgimage.length){
32256             cfg.cn[0].cn.push({
32257                 tag: 'img',
32258                 cls: 'masonry-brick-image-view',
32259                 src: this.bgimage
32260             });
32261         }
32262         
32263         if(this.videourl.length){
32264             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32265             // youtube support only?
32266             cfg.cn[0].cn.cn.push({
32267                 tag: 'iframe',
32268                 cls: 'masonry-brick-image-view',
32269                 src: vurl,
32270                 frameborder : 0,
32271                 allowfullscreen : true
32272             });
32273         }
32274         
32275         return cfg;
32276     },
32277     
32278     initEvents: function() 
32279     {
32280         switch (this.size) {
32281             case 'xs' :
32282                 this.x = 1;
32283                 this.y = 1;
32284                 break;
32285             case 'sm' :
32286                 this.x = 2;
32287                 this.y = 2;
32288                 break;
32289             case 'md' :
32290             case 'md-left' :
32291             case 'md-right' :
32292                 this.x = 3;
32293                 this.y = 3;
32294                 break;
32295             case 'tall' :
32296                 this.x = 2;
32297                 this.y = 3;
32298                 break;
32299             case 'wide' :
32300                 this.x = 3;
32301                 this.y = 2;
32302                 break;
32303             case 'wide-thin' :
32304                 this.x = 3;
32305                 this.y = 1;
32306                 break;
32307                         
32308             default :
32309                 break;
32310         }
32311         
32312         if(Roo.isTouch){
32313             this.el.on('touchstart', this.onTouchStart, this);
32314             this.el.on('touchmove', this.onTouchMove, this);
32315             this.el.on('touchend', this.onTouchEnd, this);
32316             this.el.on('contextmenu', this.onContextMenu, this);
32317         } else {
32318             this.el.on('mouseenter'  ,this.enter, this);
32319             this.el.on('mouseleave', this.leave, this);
32320             this.el.on('click', this.onClick, this);
32321         }
32322         
32323         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32324             this.parent().bricks.push(this);   
32325         }
32326         
32327     },
32328     
32329     onClick: function(e, el)
32330     {
32331         var time = this.endTimer - this.startTimer;
32332         // Roo.log(e.preventDefault());
32333         if(Roo.isTouch){
32334             if(time > 1000){
32335                 e.preventDefault();
32336                 return;
32337             }
32338         }
32339         
32340         if(!this.preventDefault){
32341             return;
32342         }
32343         
32344         e.preventDefault();
32345         
32346         if (this.activcClass != '') {
32347             this.selectBrick();
32348         }
32349         
32350         this.fireEvent('click', this);
32351     },
32352     
32353     enter: function(e, el)
32354     {
32355         e.preventDefault();
32356         
32357         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32358             return;
32359         }
32360         
32361         if(this.bgimage.length && this.html.length){
32362             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32363         }
32364     },
32365     
32366     leave: function(e, el)
32367     {
32368         e.preventDefault();
32369         
32370         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32371             return;
32372         }
32373         
32374         if(this.bgimage.length && this.html.length){
32375             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32376         }
32377     },
32378     
32379     onTouchStart: function(e, el)
32380     {
32381 //        e.preventDefault();
32382         
32383         this.touchmoved = false;
32384         
32385         if(!this.isFitContainer){
32386             return;
32387         }
32388         
32389         if(!this.bgimage.length || !this.html.length){
32390             return;
32391         }
32392         
32393         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32394         
32395         this.timer = new Date().getTime();
32396         
32397     },
32398     
32399     onTouchMove: function(e, el)
32400     {
32401         this.touchmoved = true;
32402     },
32403     
32404     onContextMenu : function(e,el)
32405     {
32406         e.preventDefault();
32407         e.stopPropagation();
32408         return false;
32409     },
32410     
32411     onTouchEnd: function(e, el)
32412     {
32413 //        e.preventDefault();
32414         
32415         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32416         
32417             this.leave(e,el);
32418             
32419             return;
32420         }
32421         
32422         if(!this.bgimage.length || !this.html.length){
32423             
32424             if(this.href.length){
32425                 window.location.href = this.href;
32426             }
32427             
32428             return;
32429         }
32430         
32431         if(!this.isFitContainer){
32432             return;
32433         }
32434         
32435         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32436         
32437         window.location.href = this.href;
32438     },
32439     
32440     //selection on single brick only
32441     selectBrick : function() {
32442         
32443         if (!this.parentId) {
32444             return;
32445         }
32446         
32447         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32448         var index = m.selectedBrick.indexOf(this.id);
32449         
32450         if ( index > -1) {
32451             m.selectedBrick.splice(index,1);
32452             this.el.removeClass(this.activeClass);
32453             return;
32454         }
32455         
32456         for(var i = 0; i < m.selectedBrick.length; i++) {
32457             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32458             b.el.removeClass(b.activeClass);
32459         }
32460         
32461         m.selectedBrick = [];
32462         
32463         m.selectedBrick.push(this.id);
32464         this.el.addClass(this.activeClass);
32465         return;
32466     }
32467     
32468 });
32469
32470 Roo.apply(Roo.bootstrap.MasonryBrick, {
32471     
32472     //groups: {},
32473     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32474      /**
32475     * register a Masonry Brick
32476     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32477     */
32478     
32479     register : function(brick)
32480     {
32481         //this.groups[brick.id] = brick;
32482         this.groups.add(brick.id, brick);
32483     },
32484     /**
32485     * fetch a  masonry brick based on the masonry brick ID
32486     * @param {string} the masonry brick to add
32487     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32488     */
32489     
32490     get: function(brick_id) 
32491     {
32492         // if (typeof(this.groups[brick_id]) == 'undefined') {
32493         //     return false;
32494         // }
32495         // return this.groups[brick_id] ;
32496         
32497         if(this.groups.key(brick_id)) {
32498             return this.groups.key(brick_id);
32499         }
32500         
32501         return false;
32502     }
32503     
32504     
32505     
32506 });
32507
32508  /*
32509  * - LGPL
32510  *
32511  * element
32512  * 
32513  */
32514
32515 /**
32516  * @class Roo.bootstrap.Brick
32517  * @extends Roo.bootstrap.Component
32518  * Bootstrap Brick class
32519  * 
32520  * @constructor
32521  * Create a new Brick
32522  * @param {Object} config The config object
32523  */
32524
32525 Roo.bootstrap.Brick = function(config){
32526     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32527     
32528     this.addEvents({
32529         // raw events
32530         /**
32531          * @event click
32532          * When a Brick is click
32533          * @param {Roo.bootstrap.Brick} this
32534          * @param {Roo.EventObject} e
32535          */
32536         "click" : true
32537     });
32538 };
32539
32540 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32541     
32542     /**
32543      * @cfg {String} title
32544      */   
32545     title : '',
32546     /**
32547      * @cfg {String} html
32548      */   
32549     html : '',
32550     /**
32551      * @cfg {String} bgimage
32552      */   
32553     bgimage : '',
32554     /**
32555      * @cfg {String} cls
32556      */   
32557     cls : '',
32558     /**
32559      * @cfg {String} href
32560      */   
32561     href : '',
32562     /**
32563      * @cfg {String} video
32564      */   
32565     video : '',
32566     /**
32567      * @cfg {Boolean} square
32568      */   
32569     square : true,
32570     
32571     getAutoCreate : function()
32572     {
32573         var cls = 'roo-brick';
32574         
32575         if(this.href.length){
32576             cls += ' roo-brick-link';
32577         }
32578         
32579         if(this.bgimage.length){
32580             cls += ' roo-brick-image';
32581         }
32582         
32583         if(!this.html.length && !this.bgimage.length){
32584             cls += ' roo-brick-center-title';
32585         }
32586         
32587         if(!this.html.length && this.bgimage.length){
32588             cls += ' roo-brick-bottom-title';
32589         }
32590         
32591         if(this.cls){
32592             cls += ' ' + this.cls;
32593         }
32594         
32595         var cfg = {
32596             tag: (this.href.length) ? 'a' : 'div',
32597             cls: cls,
32598             cn: [
32599                 {
32600                     tag: 'div',
32601                     cls: 'roo-brick-paragraph',
32602                     cn: []
32603                 }
32604             ]
32605         };
32606         
32607         if(this.href.length){
32608             cfg.href = this.href;
32609         }
32610         
32611         var cn = cfg.cn[0].cn;
32612         
32613         if(this.title.length){
32614             cn.push({
32615                 tag: 'h4',
32616                 cls: 'roo-brick-title',
32617                 html: this.title
32618             });
32619         }
32620         
32621         if(this.html.length){
32622             cn.push({
32623                 tag: 'p',
32624                 cls: 'roo-brick-text',
32625                 html: this.html
32626             });
32627         } else {
32628             cn.cls += ' hide';
32629         }
32630         
32631         if(this.bgimage.length){
32632             cfg.cn.push({
32633                 tag: 'img',
32634                 cls: 'roo-brick-image-view',
32635                 src: this.bgimage
32636             });
32637         }
32638         
32639         return cfg;
32640     },
32641     
32642     initEvents: function() 
32643     {
32644         if(this.title.length || this.html.length){
32645             this.el.on('mouseenter'  ,this.enter, this);
32646             this.el.on('mouseleave', this.leave, this);
32647         }
32648         
32649         Roo.EventManager.onWindowResize(this.resize, this); 
32650         
32651         if(this.bgimage.length){
32652             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32653             this.imageEl.on('load', this.onImageLoad, this);
32654             return;
32655         }
32656         
32657         this.resize();
32658     },
32659     
32660     onImageLoad : function()
32661     {
32662         this.resize();
32663     },
32664     
32665     resize : function()
32666     {
32667         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32668         
32669         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32670         
32671         if(this.bgimage.length){
32672             var image = this.el.select('.roo-brick-image-view', true).first();
32673             
32674             image.setWidth(paragraph.getWidth());
32675             
32676             if(this.square){
32677                 image.setHeight(paragraph.getWidth());
32678             }
32679             
32680             this.el.setHeight(image.getHeight());
32681             paragraph.setHeight(image.getHeight());
32682             
32683         }
32684         
32685     },
32686     
32687     enter: function(e, el)
32688     {
32689         e.preventDefault();
32690         
32691         if(this.bgimage.length){
32692             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32693             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32694         }
32695     },
32696     
32697     leave: function(e, el)
32698     {
32699         e.preventDefault();
32700         
32701         if(this.bgimage.length){
32702             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32703             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32704         }
32705     }
32706     
32707 });
32708
32709  
32710
32711  /*
32712  * - LGPL
32713  *
32714  * Input
32715  * 
32716  */
32717
32718 /**
32719  * @class Roo.bootstrap.NumberField
32720  * @extends Roo.bootstrap.Input
32721  * Bootstrap NumberField class
32722  * 
32723  * 
32724  * 
32725  * 
32726  * @constructor
32727  * Create a new NumberField
32728  * @param {Object} config The config object
32729  */
32730
32731 Roo.bootstrap.NumberField = function(config){
32732     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32733 };
32734
32735 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32736     
32737     /**
32738      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32739      */
32740     allowDecimals : true,
32741     /**
32742      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32743      */
32744     decimalSeparator : ".",
32745     /**
32746      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32747      */
32748     decimalPrecision : 2,
32749     /**
32750      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32751      */
32752     allowNegative : true,
32753     /**
32754      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32755      */
32756     minValue : Number.NEGATIVE_INFINITY,
32757     /**
32758      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32759      */
32760     maxValue : Number.MAX_VALUE,
32761     /**
32762      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32763      */
32764     minText : "The minimum value for this field is {0}",
32765     /**
32766      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32767      */
32768     maxText : "The maximum value for this field is {0}",
32769     /**
32770      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32771      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32772      */
32773     nanText : "{0} is not a valid number",
32774     /**
32775      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32776      */
32777     castInt : true,
32778
32779     // private
32780     initEvents : function()
32781     {   
32782         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32783         
32784         var allowed = "0123456789";
32785         
32786         if(this.allowDecimals){
32787             allowed += this.decimalSeparator;
32788         }
32789         
32790         if(this.allowNegative){
32791             allowed += "-";
32792         }
32793         
32794         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32795         
32796         var keyPress = function(e){
32797             
32798             var k = e.getKey();
32799             
32800             var c = e.getCharCode();
32801             
32802             if(
32803                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32804                     allowed.indexOf(String.fromCharCode(c)) === -1
32805             ){
32806                 e.stopEvent();
32807                 return;
32808             }
32809             
32810             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32811                 return;
32812             }
32813             
32814             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32815                 e.stopEvent();
32816             }
32817         };
32818         
32819         this.el.on("keypress", keyPress, this);
32820     },
32821     
32822     validateValue : function(value)
32823     {
32824         
32825         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32826             return false;
32827         }
32828         
32829         var num = this.parseValue(value);
32830         
32831         if(isNaN(num)){
32832             this.markInvalid(String.format(this.nanText, value));
32833             return false;
32834         }
32835         
32836         if(num < this.minValue){
32837             this.markInvalid(String.format(this.minText, this.minValue));
32838             return false;
32839         }
32840         
32841         if(num > this.maxValue){
32842             this.markInvalid(String.format(this.maxText, this.maxValue));
32843             return false;
32844         }
32845         
32846         return true;
32847     },
32848
32849     getValue : function()
32850     {
32851         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32852     },
32853
32854     parseValue : function(value)
32855     {
32856         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32857         return isNaN(value) ? '' : value;
32858     },
32859
32860     fixPrecision : function(value)
32861     {
32862         var nan = isNaN(value);
32863         
32864         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32865             return nan ? '' : value;
32866         }
32867         return parseFloat(value).toFixed(this.decimalPrecision);
32868     },
32869
32870     setValue : function(v)
32871     {
32872         v = this.fixPrecision(v);
32873         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32874     },
32875
32876     decimalPrecisionFcn : function(v)
32877     {
32878         return Math.floor(v);
32879     },
32880
32881     beforeBlur : function()
32882     {
32883         if(!this.castInt){
32884             return;
32885         }
32886         
32887         var v = this.parseValue(this.getRawValue());
32888         if(v){
32889             this.setValue(v);
32890         }
32891     }
32892     
32893 });
32894
32895  
32896
32897 /*
32898 * Licence: LGPL
32899 */
32900
32901 /**
32902  * @class Roo.bootstrap.DocumentSlider
32903  * @extends Roo.bootstrap.Component
32904  * Bootstrap DocumentSlider class
32905  * 
32906  * @constructor
32907  * Create a new DocumentViewer
32908  * @param {Object} config The config object
32909  */
32910
32911 Roo.bootstrap.DocumentSlider = function(config){
32912     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32913     
32914     this.files = [];
32915     
32916     this.addEvents({
32917         /**
32918          * @event initial
32919          * Fire after initEvent
32920          * @param {Roo.bootstrap.DocumentSlider} this
32921          */
32922         "initial" : true,
32923         /**
32924          * @event update
32925          * Fire after update
32926          * @param {Roo.bootstrap.DocumentSlider} this
32927          */
32928         "update" : true,
32929         /**
32930          * @event click
32931          * Fire after click
32932          * @param {Roo.bootstrap.DocumentSlider} this
32933          */
32934         "click" : true
32935     });
32936 };
32937
32938 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32939     
32940     files : false,
32941     
32942     indicator : 0,
32943     
32944     getAutoCreate : function()
32945     {
32946         var cfg = {
32947             tag : 'div',
32948             cls : 'roo-document-slider',
32949             cn : [
32950                 {
32951                     tag : 'div',
32952                     cls : 'roo-document-slider-header',
32953                     cn : [
32954                         {
32955                             tag : 'div',
32956                             cls : 'roo-document-slider-header-title'
32957                         }
32958                     ]
32959                 },
32960                 {
32961                     tag : 'div',
32962                     cls : 'roo-document-slider-body',
32963                     cn : [
32964                         {
32965                             tag : 'div',
32966                             cls : 'roo-document-slider-prev',
32967                             cn : [
32968                                 {
32969                                     tag : 'i',
32970                                     cls : 'fa fa-chevron-left'
32971                                 }
32972                             ]
32973                         },
32974                         {
32975                             tag : 'div',
32976                             cls : 'roo-document-slider-thumb',
32977                             cn : [
32978                                 {
32979                                     tag : 'img',
32980                                     cls : 'roo-document-slider-image'
32981                                 }
32982                             ]
32983                         },
32984                         {
32985                             tag : 'div',
32986                             cls : 'roo-document-slider-next',
32987                             cn : [
32988                                 {
32989                                     tag : 'i',
32990                                     cls : 'fa fa-chevron-right'
32991                                 }
32992                             ]
32993                         }
32994                     ]
32995                 }
32996             ]
32997         };
32998         
32999         return cfg;
33000     },
33001     
33002     initEvents : function()
33003     {
33004         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33005         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33006         
33007         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33008         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33009         
33010         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33011         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33012         
33013         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33014         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33015         
33016         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33017         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33018         
33019         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33020         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33021         
33022         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33023         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33024         
33025         this.thumbEl.on('click', this.onClick, this);
33026         
33027         this.prevIndicator.on('click', this.prev, this);
33028         
33029         this.nextIndicator.on('click', this.next, this);
33030         
33031     },
33032     
33033     initial : function()
33034     {
33035         if(this.files.length){
33036             this.indicator = 1;
33037             this.update()
33038         }
33039         
33040         this.fireEvent('initial', this);
33041     },
33042     
33043     update : function()
33044     {
33045         this.imageEl.attr('src', this.files[this.indicator - 1]);
33046         
33047         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33048         
33049         this.prevIndicator.show();
33050         
33051         if(this.indicator == 1){
33052             this.prevIndicator.hide();
33053         }
33054         
33055         this.nextIndicator.show();
33056         
33057         if(this.indicator == this.files.length){
33058             this.nextIndicator.hide();
33059         }
33060         
33061         this.thumbEl.scrollTo('top');
33062         
33063         this.fireEvent('update', this);
33064     },
33065     
33066     onClick : function(e)
33067     {
33068         e.preventDefault();
33069         
33070         this.fireEvent('click', this);
33071     },
33072     
33073     prev : function(e)
33074     {
33075         e.preventDefault();
33076         
33077         this.indicator = Math.max(1, this.indicator - 1);
33078         
33079         this.update();
33080     },
33081     
33082     next : function(e)
33083     {
33084         e.preventDefault();
33085         
33086         this.indicator = Math.min(this.files.length, this.indicator + 1);
33087         
33088         this.update();
33089     }
33090 });
33091 /*
33092  * - LGPL
33093  *
33094  * RadioSet
33095  *
33096  *
33097  */
33098
33099 /**
33100  * @class Roo.bootstrap.RadioSet
33101  * @extends Roo.bootstrap.Input
33102  * Bootstrap RadioSet class
33103  * @cfg {String} indicatorpos (left|right) default left
33104  * @cfg {Boolean} inline (true|false) inline the element (default true)
33105  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33106  * @constructor
33107  * Create a new RadioSet
33108  * @param {Object} config The config object
33109  */
33110
33111 Roo.bootstrap.RadioSet = function(config){
33112     
33113     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33114     
33115     this.radioes = [];
33116     
33117     Roo.bootstrap.RadioSet.register(this);
33118     
33119     this.addEvents({
33120         /**
33121         * @event check
33122         * Fires when the element is checked or unchecked.
33123         * @param {Roo.bootstrap.RadioSet} this This radio
33124         * @param {Roo.bootstrap.Radio} item The checked item
33125         */
33126        check : true
33127     });
33128     
33129 };
33130
33131 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33132
33133     radioes : false,
33134     
33135     inline : true,
33136     
33137     weight : '',
33138     
33139     indicatorpos : 'left',
33140     
33141     getAutoCreate : function()
33142     {
33143         var label = {
33144             tag : 'label',
33145             cls : 'roo-radio-set-label',
33146             cn : [
33147                 {
33148                     tag : 'span',
33149                     html : this.fieldLabel
33150                 }
33151             ]
33152         };
33153         
33154         if(this.indicatorpos == 'left'){
33155             label.cn.unshift({
33156                 tag : 'i',
33157                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33158                 tooltip : 'This field is required'
33159             });
33160         } else {
33161             label.cn.push({
33162                 tag : 'i',
33163                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33164                 tooltip : 'This field is required'
33165             });
33166         }
33167         
33168         var items = {
33169             tag : 'div',
33170             cls : 'roo-radio-set-items'
33171         };
33172         
33173         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33174         
33175         if (align === 'left' && this.fieldLabel.length) {
33176             
33177             items = {
33178                 cls : "roo-radio-set-right", 
33179                 cn: [
33180                     items
33181                 ]
33182             };
33183             
33184             if(this.labelWidth > 12){
33185                 label.style = "width: " + this.labelWidth + 'px';
33186             }
33187             
33188             if(this.labelWidth < 13 && this.labelmd == 0){
33189                 this.labelmd = this.labelWidth;
33190             }
33191             
33192             if(this.labellg > 0){
33193                 label.cls += ' col-lg-' + this.labellg;
33194                 items.cls += ' col-lg-' + (12 - this.labellg);
33195             }
33196             
33197             if(this.labelmd > 0){
33198                 label.cls += ' col-md-' + this.labelmd;
33199                 items.cls += ' col-md-' + (12 - this.labelmd);
33200             }
33201             
33202             if(this.labelsm > 0){
33203                 label.cls += ' col-sm-' + this.labelsm;
33204                 items.cls += ' col-sm-' + (12 - this.labelsm);
33205             }
33206             
33207             if(this.labelxs > 0){
33208                 label.cls += ' col-xs-' + this.labelxs;
33209                 items.cls += ' col-xs-' + (12 - this.labelxs);
33210             }
33211         }
33212         
33213         var cfg = {
33214             tag : 'div',
33215             cls : 'roo-radio-set',
33216             cn : [
33217                 {
33218                     tag : 'input',
33219                     cls : 'roo-radio-set-input',
33220                     type : 'hidden',
33221                     name : this.name,
33222                     value : this.value ? this.value :  ''
33223                 },
33224                 label,
33225                 items
33226             ]
33227         };
33228         
33229         if(this.weight.length){
33230             cfg.cls += ' roo-radio-' + this.weight;
33231         }
33232         
33233         if(this.inline) {
33234             cfg.cls += ' roo-radio-set-inline';
33235         }
33236         
33237         return cfg;
33238         
33239     },
33240
33241     initEvents : function()
33242     {
33243         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33244         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         if(!this.fieldLabel.length){
33247             this.labelEl.hide();
33248         }
33249         
33250         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33251         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33252         
33253         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33254         this.indicatorEl().hide();
33255         
33256         this.originalValue = this.getValue();
33257         
33258     },
33259     
33260     inputEl: function ()
33261     {
33262         return this.el.select('.roo-radio-set-input', true).first();
33263     },
33264     
33265     getChildContainer : function()
33266     {
33267         return this.itemsEl;
33268     },
33269     
33270     register : function(item)
33271     {
33272         this.radioes.push(item);
33273         
33274     },
33275     
33276     validate : function()
33277     {   
33278         var valid = false;
33279         
33280         Roo.each(this.radioes, function(i){
33281             if(!i.checked){
33282                 return;
33283             }
33284             
33285             valid = true;
33286             return false;
33287         });
33288         
33289         if(this.allowBlank) {
33290             return true;
33291         }
33292         
33293         if(this.disabled || valid){
33294             this.markValid();
33295             return true;
33296         }
33297         
33298         this.markInvalid();
33299         return false;
33300         
33301     },
33302     
33303     markValid : function()
33304     {
33305         if(this.labelEl.isVisible(true)){
33306             this.indicatorEl().hide();
33307         }
33308         
33309         this.el.removeClass([this.invalidClass, this.validClass]);
33310         this.el.addClass(this.validClass);
33311         
33312         this.fireEvent('valid', this);
33313     },
33314     
33315     markInvalid : function(msg)
33316     {
33317         if(this.allowBlank || this.disabled){
33318             return;
33319         }
33320         
33321         if(this.labelEl.isVisible(true)){
33322             this.indicatorEl().show();
33323         }
33324         
33325         this.el.removeClass([this.invalidClass, this.validClass]);
33326         this.el.addClass(this.invalidClass);
33327         
33328         this.fireEvent('invalid', this, msg);
33329         
33330     },
33331     
33332     setValue : function(v, suppressEvent)
33333     {   
33334         this.value = v;
33335         if(this.rendered){
33336             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33337         }
33338         
33339         Roo.each(this.radioes, function(i){
33340             
33341             i.checked = false;
33342             i.el.removeClass('checked');
33343             
33344             if(i.value === v || i.value.toString() === v.toString()){
33345                 i.checked = true;
33346                 i.el.addClass('checked');
33347                 
33348                 if(suppressEvent !== true){
33349                     this.fireEvent('check', this, i);
33350                 }
33351             }
33352             
33353         }, this);
33354         
33355         this.validate();
33356     },
33357     
33358     clearInvalid : function(){
33359         
33360         if(!this.el || this.preventMark){
33361             return;
33362         }
33363         
33364         this.el.removeClass([this.invalidClass]);
33365         
33366         this.fireEvent('valid', this);
33367     }
33368     
33369 });
33370
33371 Roo.apply(Roo.bootstrap.RadioSet, {
33372     
33373     groups: {},
33374     
33375     register : function(set)
33376     {
33377         this.groups[set.name] = set;
33378     },
33379     
33380     get: function(name) 
33381     {
33382         if (typeof(this.groups[name]) == 'undefined') {
33383             return false;
33384         }
33385         
33386         return this.groups[name] ;
33387     }
33388     
33389 });
33390 /*
33391  * Based on:
33392  * Ext JS Library 1.1.1
33393  * Copyright(c) 2006-2007, Ext JS, LLC.
33394  *
33395  * Originally Released Under LGPL - original licence link has changed is not relivant.
33396  *
33397  * Fork - LGPL
33398  * <script type="text/javascript">
33399  */
33400
33401
33402 /**
33403  * @class Roo.bootstrap.SplitBar
33404  * @extends Roo.util.Observable
33405  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33406  * <br><br>
33407  * Usage:
33408  * <pre><code>
33409 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33410                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33411 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33412 split.minSize = 100;
33413 split.maxSize = 600;
33414 split.animate = true;
33415 split.on('moved', splitterMoved);
33416 </code></pre>
33417  * @constructor
33418  * Create a new SplitBar
33419  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33420  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33421  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33422  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33423                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33424                         position of the SplitBar).
33425  */
33426 Roo.bootstrap.SplitBar = function(cfg){
33427     
33428     /** @private */
33429     
33430     //{
33431     //  dragElement : elm
33432     //  resizingElement: el,
33433         // optional..
33434     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33435     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33436         // existingProxy ???
33437     //}
33438     
33439     this.el = Roo.get(cfg.dragElement, true);
33440     this.el.dom.unselectable = "on";
33441     /** @private */
33442     this.resizingEl = Roo.get(cfg.resizingElement, true);
33443
33444     /**
33445      * @private
33446      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33447      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33448      * @type Number
33449      */
33450     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33451     
33452     /**
33453      * The minimum size of the resizing element. (Defaults to 0)
33454      * @type Number
33455      */
33456     this.minSize = 0;
33457     
33458     /**
33459      * The maximum size of the resizing element. (Defaults to 2000)
33460      * @type Number
33461      */
33462     this.maxSize = 2000;
33463     
33464     /**
33465      * Whether to animate the transition to the new size
33466      * @type Boolean
33467      */
33468     this.animate = false;
33469     
33470     /**
33471      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33472      * @type Boolean
33473      */
33474     this.useShim = false;
33475     
33476     /** @private */
33477     this.shim = null;
33478     
33479     if(!cfg.existingProxy){
33480         /** @private */
33481         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33482     }else{
33483         this.proxy = Roo.get(cfg.existingProxy).dom;
33484     }
33485     /** @private */
33486     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33487     
33488     /** @private */
33489     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33490     
33491     /** @private */
33492     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33493     
33494     /** @private */
33495     this.dragSpecs = {};
33496     
33497     /**
33498      * @private The adapter to use to positon and resize elements
33499      */
33500     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33501     this.adapter.init(this);
33502     
33503     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33504         /** @private */
33505         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33506         this.el.addClass("roo-splitbar-h");
33507     }else{
33508         /** @private */
33509         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33510         this.el.addClass("roo-splitbar-v");
33511     }
33512     
33513     this.addEvents({
33514         /**
33515          * @event resize
33516          * Fires when the splitter is moved (alias for {@link #event-moved})
33517          * @param {Roo.bootstrap.SplitBar} this
33518          * @param {Number} newSize the new width or height
33519          */
33520         "resize" : true,
33521         /**
33522          * @event moved
33523          * Fires when the splitter is moved
33524          * @param {Roo.bootstrap.SplitBar} this
33525          * @param {Number} newSize the new width or height
33526          */
33527         "moved" : true,
33528         /**
33529          * @event beforeresize
33530          * Fires before the splitter is dragged
33531          * @param {Roo.bootstrap.SplitBar} this
33532          */
33533         "beforeresize" : true,
33534
33535         "beforeapply" : true
33536     });
33537
33538     Roo.util.Observable.call(this);
33539 };
33540
33541 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33542     onStartProxyDrag : function(x, y){
33543         this.fireEvent("beforeresize", this);
33544         if(!this.overlay){
33545             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33546             o.unselectable();
33547             o.enableDisplayMode("block");
33548             // all splitbars share the same overlay
33549             Roo.bootstrap.SplitBar.prototype.overlay = o;
33550         }
33551         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33552         this.overlay.show();
33553         Roo.get(this.proxy).setDisplayed("block");
33554         var size = this.adapter.getElementSize(this);
33555         this.activeMinSize = this.getMinimumSize();;
33556         this.activeMaxSize = this.getMaximumSize();;
33557         var c1 = size - this.activeMinSize;
33558         var c2 = Math.max(this.activeMaxSize - size, 0);
33559         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33560             this.dd.resetConstraints();
33561             this.dd.setXConstraint(
33562                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33563                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33564             );
33565             this.dd.setYConstraint(0, 0);
33566         }else{
33567             this.dd.resetConstraints();
33568             this.dd.setXConstraint(0, 0);
33569             this.dd.setYConstraint(
33570                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33571                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33572             );
33573          }
33574         this.dragSpecs.startSize = size;
33575         this.dragSpecs.startPoint = [x, y];
33576         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33577     },
33578     
33579     /** 
33580      * @private Called after the drag operation by the DDProxy
33581      */
33582     onEndProxyDrag : function(e){
33583         Roo.get(this.proxy).setDisplayed(false);
33584         var endPoint = Roo.lib.Event.getXY(e);
33585         if(this.overlay){
33586             this.overlay.hide();
33587         }
33588         var newSize;
33589         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33590             newSize = this.dragSpecs.startSize + 
33591                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33592                     endPoint[0] - this.dragSpecs.startPoint[0] :
33593                     this.dragSpecs.startPoint[0] - endPoint[0]
33594                 );
33595         }else{
33596             newSize = this.dragSpecs.startSize + 
33597                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33598                     endPoint[1] - this.dragSpecs.startPoint[1] :
33599                     this.dragSpecs.startPoint[1] - endPoint[1]
33600                 );
33601         }
33602         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33603         if(newSize != this.dragSpecs.startSize){
33604             if(this.fireEvent('beforeapply', this, newSize) !== false){
33605                 this.adapter.setElementSize(this, newSize);
33606                 this.fireEvent("moved", this, newSize);
33607                 this.fireEvent("resize", this, newSize);
33608             }
33609         }
33610     },
33611     
33612     /**
33613      * Get the adapter this SplitBar uses
33614      * @return The adapter object
33615      */
33616     getAdapter : function(){
33617         return this.adapter;
33618     },
33619     
33620     /**
33621      * Set the adapter this SplitBar uses
33622      * @param {Object} adapter A SplitBar adapter object
33623      */
33624     setAdapter : function(adapter){
33625         this.adapter = adapter;
33626         this.adapter.init(this);
33627     },
33628     
33629     /**
33630      * Gets the minimum size for the resizing element
33631      * @return {Number} The minimum size
33632      */
33633     getMinimumSize : function(){
33634         return this.minSize;
33635     },
33636     
33637     /**
33638      * Sets the minimum size for the resizing element
33639      * @param {Number} minSize The minimum size
33640      */
33641     setMinimumSize : function(minSize){
33642         this.minSize = minSize;
33643     },
33644     
33645     /**
33646      * Gets the maximum size for the resizing element
33647      * @return {Number} The maximum size
33648      */
33649     getMaximumSize : function(){
33650         return this.maxSize;
33651     },
33652     
33653     /**
33654      * Sets the maximum size for the resizing element
33655      * @param {Number} maxSize The maximum size
33656      */
33657     setMaximumSize : function(maxSize){
33658         this.maxSize = maxSize;
33659     },
33660     
33661     /**
33662      * Sets the initialize size for the resizing element
33663      * @param {Number} size The initial size
33664      */
33665     setCurrentSize : function(size){
33666         var oldAnimate = this.animate;
33667         this.animate = false;
33668         this.adapter.setElementSize(this, size);
33669         this.animate = oldAnimate;
33670     },
33671     
33672     /**
33673      * Destroy this splitbar. 
33674      * @param {Boolean} removeEl True to remove the element
33675      */
33676     destroy : function(removeEl){
33677         if(this.shim){
33678             this.shim.remove();
33679         }
33680         this.dd.unreg();
33681         this.proxy.parentNode.removeChild(this.proxy);
33682         if(removeEl){
33683             this.el.remove();
33684         }
33685     }
33686 });
33687
33688 /**
33689  * @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.
33690  */
33691 Roo.bootstrap.SplitBar.createProxy = function(dir){
33692     var proxy = new Roo.Element(document.createElement("div"));
33693     proxy.unselectable();
33694     var cls = 'roo-splitbar-proxy';
33695     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33696     document.body.appendChild(proxy.dom);
33697     return proxy.dom;
33698 };
33699
33700 /** 
33701  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33702  * Default Adapter. It assumes the splitter and resizing element are not positioned
33703  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33704  */
33705 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33706 };
33707
33708 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33709     // do nothing for now
33710     init : function(s){
33711     
33712     },
33713     /**
33714      * Called before drag operations to get the current size of the resizing element. 
33715      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33716      */
33717      getElementSize : function(s){
33718         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33719             return s.resizingEl.getWidth();
33720         }else{
33721             return s.resizingEl.getHeight();
33722         }
33723     },
33724     
33725     /**
33726      * Called after drag operations to set the size of the resizing element.
33727      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33728      * @param {Number} newSize The new size to set
33729      * @param {Function} onComplete A function to be invoked when resizing is complete
33730      */
33731     setElementSize : function(s, newSize, onComplete){
33732         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33733             if(!s.animate){
33734                 s.resizingEl.setWidth(newSize);
33735                 if(onComplete){
33736                     onComplete(s, newSize);
33737                 }
33738             }else{
33739                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33740             }
33741         }else{
33742             
33743             if(!s.animate){
33744                 s.resizingEl.setHeight(newSize);
33745                 if(onComplete){
33746                     onComplete(s, newSize);
33747                 }
33748             }else{
33749                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33750             }
33751         }
33752     }
33753 };
33754
33755 /** 
33756  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33757  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33758  * Adapter that  moves the splitter element to align with the resized sizing element. 
33759  * Used with an absolute positioned SplitBar.
33760  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33761  * document.body, make sure you assign an id to the body element.
33762  */
33763 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33764     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33765     this.container = Roo.get(container);
33766 };
33767
33768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33769     init : function(s){
33770         this.basic.init(s);
33771     },
33772     
33773     getElementSize : function(s){
33774         return this.basic.getElementSize(s);
33775     },
33776     
33777     setElementSize : function(s, newSize, onComplete){
33778         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33779     },
33780     
33781     moveSplitter : function(s){
33782         var yes = Roo.bootstrap.SplitBar;
33783         switch(s.placement){
33784             case yes.LEFT:
33785                 s.el.setX(s.resizingEl.getRight());
33786                 break;
33787             case yes.RIGHT:
33788                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33789                 break;
33790             case yes.TOP:
33791                 s.el.setY(s.resizingEl.getBottom());
33792                 break;
33793             case yes.BOTTOM:
33794                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33795                 break;
33796         }
33797     }
33798 };
33799
33800 /**
33801  * Orientation constant - Create a vertical SplitBar
33802  * @static
33803  * @type Number
33804  */
33805 Roo.bootstrap.SplitBar.VERTICAL = 1;
33806
33807 /**
33808  * Orientation constant - Create a horizontal SplitBar
33809  * @static
33810  * @type Number
33811  */
33812 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33813
33814 /**
33815  * Placement constant - The resizing element is to the left of the splitter element
33816  * @static
33817  * @type Number
33818  */
33819 Roo.bootstrap.SplitBar.LEFT = 1;
33820
33821 /**
33822  * Placement constant - The resizing element is to the right of the splitter element
33823  * @static
33824  * @type Number
33825  */
33826 Roo.bootstrap.SplitBar.RIGHT = 2;
33827
33828 /**
33829  * Placement constant - The resizing element is positioned above the splitter element
33830  * @static
33831  * @type Number
33832  */
33833 Roo.bootstrap.SplitBar.TOP = 3;
33834
33835 /**
33836  * Placement constant - The resizing element is positioned under splitter element
33837  * @static
33838  * @type Number
33839  */
33840 Roo.bootstrap.SplitBar.BOTTOM = 4;
33841 Roo.namespace("Roo.bootstrap.layout");/*
33842  * Based on:
33843  * Ext JS Library 1.1.1
33844  * Copyright(c) 2006-2007, Ext JS, LLC.
33845  *
33846  * Originally Released Under LGPL - original licence link has changed is not relivant.
33847  *
33848  * Fork - LGPL
33849  * <script type="text/javascript">
33850  */
33851
33852 /**
33853  * @class Roo.bootstrap.layout.Manager
33854  * @extends Roo.bootstrap.Component
33855  * Base class for layout managers.
33856  */
33857 Roo.bootstrap.layout.Manager = function(config)
33858 {
33859     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33860
33861
33862
33863
33864
33865     /** false to disable window resize monitoring @type Boolean */
33866     this.monitorWindowResize = true;
33867     this.regions = {};
33868     this.addEvents({
33869         /**
33870          * @event layout
33871          * Fires when a layout is performed.
33872          * @param {Roo.LayoutManager} this
33873          */
33874         "layout" : true,
33875         /**
33876          * @event regionresized
33877          * Fires when the user resizes a region.
33878          * @param {Roo.LayoutRegion} region The resized region
33879          * @param {Number} newSize The new size (width for east/west, height for north/south)
33880          */
33881         "regionresized" : true,
33882         /**
33883          * @event regioncollapsed
33884          * Fires when a region is collapsed.
33885          * @param {Roo.LayoutRegion} region The collapsed region
33886          */
33887         "regioncollapsed" : true,
33888         /**
33889          * @event regionexpanded
33890          * Fires when a region is expanded.
33891          * @param {Roo.LayoutRegion} region The expanded region
33892          */
33893         "regionexpanded" : true
33894     });
33895     this.updating = false;
33896
33897     if (config.el) {
33898         this.el = Roo.get(config.el);
33899         this.initEvents();
33900     }
33901
33902 };
33903
33904 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33905
33906
33907     regions : null,
33908
33909     monitorWindowResize : true,
33910
33911
33912     updating : false,
33913
33914
33915     onRender : function(ct, position)
33916     {
33917         if(!this.el){
33918             this.el = Roo.get(ct);
33919             this.initEvents();
33920         }
33921         //this.fireEvent('render',this);
33922     },
33923
33924
33925     initEvents: function()
33926     {
33927
33928
33929         // ie scrollbar fix
33930         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33931             document.body.scroll = "no";
33932         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33933             this.el.position('relative');
33934         }
33935         this.id = this.el.id;
33936         this.el.addClass("roo-layout-container");
33937         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33938         if(this.el.dom != document.body ) {
33939             this.el.on('resize', this.layout,this);
33940             this.el.on('show', this.layout,this);
33941         }
33942
33943     },
33944
33945     /**
33946      * Returns true if this layout is currently being updated
33947      * @return {Boolean}
33948      */
33949     isUpdating : function(){
33950         return this.updating;
33951     },
33952
33953     /**
33954      * Suspend the LayoutManager from doing auto-layouts while
33955      * making multiple add or remove calls
33956      */
33957     beginUpdate : function(){
33958         this.updating = true;
33959     },
33960
33961     /**
33962      * Restore auto-layouts and optionally disable the manager from performing a layout
33963      * @param {Boolean} noLayout true to disable a layout update
33964      */
33965     endUpdate : function(noLayout){
33966         this.updating = false;
33967         if(!noLayout){
33968             this.layout();
33969         }
33970     },
33971
33972     layout: function(){
33973         // abstract...
33974     },
33975
33976     onRegionResized : function(region, newSize){
33977         this.fireEvent("regionresized", region, newSize);
33978         this.layout();
33979     },
33980
33981     onRegionCollapsed : function(region){
33982         this.fireEvent("regioncollapsed", region);
33983     },
33984
33985     onRegionExpanded : function(region){
33986         this.fireEvent("regionexpanded", region);
33987     },
33988
33989     /**
33990      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33991      * performs box-model adjustments.
33992      * @return {Object} The size as an object {width: (the width), height: (the height)}
33993      */
33994     getViewSize : function()
33995     {
33996         var size;
33997         if(this.el.dom != document.body){
33998             size = this.el.getSize();
33999         }else{
34000             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34001         }
34002         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34003         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34004         return size;
34005     },
34006
34007     /**
34008      * Returns the Element this layout is bound to.
34009      * @return {Roo.Element}
34010      */
34011     getEl : function(){
34012         return this.el;
34013     },
34014
34015     /**
34016      * Returns the specified region.
34017      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34018      * @return {Roo.LayoutRegion}
34019      */
34020     getRegion : function(target){
34021         return this.regions[target.toLowerCase()];
34022     },
34023
34024     onWindowResize : function(){
34025         if(this.monitorWindowResize){
34026             this.layout();
34027         }
34028     }
34029 });
34030 /*
34031  * Based on:
34032  * Ext JS Library 1.1.1
34033  * Copyright(c) 2006-2007, Ext JS, LLC.
34034  *
34035  * Originally Released Under LGPL - original licence link has changed is not relivant.
34036  *
34037  * Fork - LGPL
34038  * <script type="text/javascript">
34039  */
34040 /**
34041  * @class Roo.bootstrap.layout.Border
34042  * @extends Roo.bootstrap.layout.Manager
34043  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34044  * please see: examples/bootstrap/nested.html<br><br>
34045  
34046 <b>The container the layout is rendered into can be either the body element or any other element.
34047 If it is not the body element, the container needs to either be an absolute positioned element,
34048 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34049 the container size if it is not the body element.</b>
34050
34051 * @constructor
34052 * Create a new Border
34053 * @param {Object} config Configuration options
34054  */
34055 Roo.bootstrap.layout.Border = function(config){
34056     config = config || {};
34057     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34058     
34059     
34060     
34061     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34062         if(config[region]){
34063             config[region].region = region;
34064             this.addRegion(config[region]);
34065         }
34066     },this);
34067     
34068 };
34069
34070 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34071
34072 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34073     /**
34074      * Creates and adds a new region if it doesn't already exist.
34075      * @param {String} target The target region key (north, south, east, west or center).
34076      * @param {Object} config The regions config object
34077      * @return {BorderLayoutRegion} The new region
34078      */
34079     addRegion : function(config)
34080     {
34081         if(!this.regions[config.region]){
34082             var r = this.factory(config);
34083             this.bindRegion(r);
34084         }
34085         return this.regions[config.region];
34086     },
34087
34088     // private (kinda)
34089     bindRegion : function(r){
34090         this.regions[r.config.region] = r;
34091         
34092         r.on("visibilitychange",    this.layout, this);
34093         r.on("paneladded",          this.layout, this);
34094         r.on("panelremoved",        this.layout, this);
34095         r.on("invalidated",         this.layout, this);
34096         r.on("resized",             this.onRegionResized, this);
34097         r.on("collapsed",           this.onRegionCollapsed, this);
34098         r.on("expanded",            this.onRegionExpanded, this);
34099     },
34100
34101     /**
34102      * Performs a layout update.
34103      */
34104     layout : function()
34105     {
34106         if(this.updating) {
34107             return;
34108         }
34109         
34110         // render all the rebions if they have not been done alreayd?
34111         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34112             if(this.regions[region] && !this.regions[region].bodyEl){
34113                 this.regions[region].onRender(this.el)
34114             }
34115         },this);
34116         
34117         var size = this.getViewSize();
34118         var w = size.width;
34119         var h = size.height;
34120         var centerW = w;
34121         var centerH = h;
34122         var centerY = 0;
34123         var centerX = 0;
34124         //var x = 0, y = 0;
34125
34126         var rs = this.regions;
34127         var north = rs["north"];
34128         var south = rs["south"]; 
34129         var west = rs["west"];
34130         var east = rs["east"];
34131         var center = rs["center"];
34132         //if(this.hideOnLayout){ // not supported anymore
34133             //c.el.setStyle("display", "none");
34134         //}
34135         if(north && north.isVisible()){
34136             var b = north.getBox();
34137             var m = north.getMargins();
34138             b.width = w - (m.left+m.right);
34139             b.x = m.left;
34140             b.y = m.top;
34141             centerY = b.height + b.y + m.bottom;
34142             centerH -= centerY;
34143             north.updateBox(this.safeBox(b));
34144         }
34145         if(south && south.isVisible()){
34146             var b = south.getBox();
34147             var m = south.getMargins();
34148             b.width = w - (m.left+m.right);
34149             b.x = m.left;
34150             var totalHeight = (b.height + m.top + m.bottom);
34151             b.y = h - totalHeight + m.top;
34152             centerH -= totalHeight;
34153             south.updateBox(this.safeBox(b));
34154         }
34155         if(west && west.isVisible()){
34156             var b = west.getBox();
34157             var m = west.getMargins();
34158             b.height = centerH - (m.top+m.bottom);
34159             b.x = m.left;
34160             b.y = centerY + m.top;
34161             var totalWidth = (b.width + m.left + m.right);
34162             centerX += totalWidth;
34163             centerW -= totalWidth;
34164             west.updateBox(this.safeBox(b));
34165         }
34166         if(east && east.isVisible()){
34167             var b = east.getBox();
34168             var m = east.getMargins();
34169             b.height = centerH - (m.top+m.bottom);
34170             var totalWidth = (b.width + m.left + m.right);
34171             b.x = w - totalWidth + m.left;
34172             b.y = centerY + m.top;
34173             centerW -= totalWidth;
34174             east.updateBox(this.safeBox(b));
34175         }
34176         if(center){
34177             var m = center.getMargins();
34178             var centerBox = {
34179                 x: centerX + m.left,
34180                 y: centerY + m.top,
34181                 width: centerW - (m.left+m.right),
34182                 height: centerH - (m.top+m.bottom)
34183             };
34184             //if(this.hideOnLayout){
34185                 //center.el.setStyle("display", "block");
34186             //}
34187             center.updateBox(this.safeBox(centerBox));
34188         }
34189         this.el.repaint();
34190         this.fireEvent("layout", this);
34191     },
34192
34193     // private
34194     safeBox : function(box){
34195         box.width = Math.max(0, box.width);
34196         box.height = Math.max(0, box.height);
34197         return box;
34198     },
34199
34200     /**
34201      * Adds a ContentPanel (or subclass) to this layout.
34202      * @param {String} target The target region key (north, south, east, west or center).
34203      * @param {Roo.ContentPanel} panel The panel to add
34204      * @return {Roo.ContentPanel} The added panel
34205      */
34206     add : function(target, panel){
34207          
34208         target = target.toLowerCase();
34209         return this.regions[target].add(panel);
34210     },
34211
34212     /**
34213      * Remove a ContentPanel (or subclass) to this layout.
34214      * @param {String} target The target region key (north, south, east, west or center).
34215      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34216      * @return {Roo.ContentPanel} The removed panel
34217      */
34218     remove : function(target, panel){
34219         target = target.toLowerCase();
34220         return this.regions[target].remove(panel);
34221     },
34222
34223     /**
34224      * Searches all regions for a panel with the specified id
34225      * @param {String} panelId
34226      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34227      */
34228     findPanel : function(panelId){
34229         var rs = this.regions;
34230         for(var target in rs){
34231             if(typeof rs[target] != "function"){
34232                 var p = rs[target].getPanel(panelId);
34233                 if(p){
34234                     return p;
34235                 }
34236             }
34237         }
34238         return null;
34239     },
34240
34241     /**
34242      * Searches all regions for a panel with the specified id and activates (shows) it.
34243      * @param {String/ContentPanel} panelId The panels id or the panel itself
34244      * @return {Roo.ContentPanel} The shown panel or null
34245      */
34246     showPanel : function(panelId) {
34247       var rs = this.regions;
34248       for(var target in rs){
34249          var r = rs[target];
34250          if(typeof r != "function"){
34251             if(r.hasPanel(panelId)){
34252                return r.showPanel(panelId);
34253             }
34254          }
34255       }
34256       return null;
34257    },
34258
34259    /**
34260      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34261      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34262      */
34263    /*
34264     restoreState : function(provider){
34265         if(!provider){
34266             provider = Roo.state.Manager;
34267         }
34268         var sm = new Roo.LayoutStateManager();
34269         sm.init(this, provider);
34270     },
34271 */
34272  
34273  
34274     /**
34275      * Adds a xtype elements to the layout.
34276      * <pre><code>
34277
34278 layout.addxtype({
34279        xtype : 'ContentPanel',
34280        region: 'west',
34281        items: [ .... ]
34282    }
34283 );
34284
34285 layout.addxtype({
34286         xtype : 'NestedLayoutPanel',
34287         region: 'west',
34288         layout: {
34289            center: { },
34290            west: { }   
34291         },
34292         items : [ ... list of content panels or nested layout panels.. ]
34293    }
34294 );
34295 </code></pre>
34296      * @param {Object} cfg Xtype definition of item to add.
34297      */
34298     addxtype : function(cfg)
34299     {
34300         // basically accepts a pannel...
34301         // can accept a layout region..!?!?
34302         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34303         
34304         
34305         // theory?  children can only be panels??
34306         
34307         //if (!cfg.xtype.match(/Panel$/)) {
34308         //    return false;
34309         //}
34310         var ret = false;
34311         
34312         if (typeof(cfg.region) == 'undefined') {
34313             Roo.log("Failed to add Panel, region was not set");
34314             Roo.log(cfg);
34315             return false;
34316         }
34317         var region = cfg.region;
34318         delete cfg.region;
34319         
34320           
34321         var xitems = [];
34322         if (cfg.items) {
34323             xitems = cfg.items;
34324             delete cfg.items;
34325         }
34326         var nb = false;
34327         
34328         switch(cfg.xtype) 
34329         {
34330             case 'Content':  // ContentPanel (el, cfg)
34331             case 'Scroll':  // ContentPanel (el, cfg)
34332             case 'View': 
34333                 cfg.autoCreate = true;
34334                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34335                 //} else {
34336                 //    var el = this.el.createChild();
34337                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34338                 //}
34339                 
34340                 this.add(region, ret);
34341                 break;
34342             
34343             /*
34344             case 'TreePanel': // our new panel!
34345                 cfg.el = this.el.createChild();
34346                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34347                 this.add(region, ret);
34348                 break;
34349             */
34350             
34351             case 'Nest': 
34352                 // create a new Layout (which is  a Border Layout...
34353                 
34354                 var clayout = cfg.layout;
34355                 clayout.el  = this.el.createChild();
34356                 clayout.items   = clayout.items  || [];
34357                 
34358                 delete cfg.layout;
34359                 
34360                 // replace this exitems with the clayout ones..
34361                 xitems = clayout.items;
34362                  
34363                 // force background off if it's in center...
34364                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34365                     cfg.background = false;
34366                 }
34367                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34368                 
34369                 
34370                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34371                 //console.log('adding nested layout panel '  + cfg.toSource());
34372                 this.add(region, ret);
34373                 nb = {}; /// find first...
34374                 break;
34375             
34376             case 'Grid':
34377                 
34378                 // needs grid and region
34379                 
34380                 //var el = this.getRegion(region).el.createChild();
34381                 /*
34382                  *var el = this.el.createChild();
34383                 // create the grid first...
34384                 cfg.grid.container = el;
34385                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34386                 */
34387                 
34388                 if (region == 'center' && this.active ) {
34389                     cfg.background = false;
34390                 }
34391                 
34392                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34393                 
34394                 this.add(region, ret);
34395                 /*
34396                 if (cfg.background) {
34397                     // render grid on panel activation (if panel background)
34398                     ret.on('activate', function(gp) {
34399                         if (!gp.grid.rendered) {
34400                     //        gp.grid.render(el);
34401                         }
34402                     });
34403                 } else {
34404                   //  cfg.grid.render(el);
34405                 }
34406                 */
34407                 break;
34408            
34409            
34410             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34411                 // it was the old xcomponent building that caused this before.
34412                 // espeically if border is the top element in the tree.
34413                 ret = this;
34414                 break; 
34415                 
34416                     
34417                 
34418                 
34419                 
34420             default:
34421                 /*
34422                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34423                     
34424                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34425                     this.add(region, ret);
34426                 } else {
34427                 */
34428                     Roo.log(cfg);
34429                     throw "Can not add '" + cfg.xtype + "' to Border";
34430                     return null;
34431              
34432                                 
34433              
34434         }
34435         this.beginUpdate();
34436         // add children..
34437         var region = '';
34438         var abn = {};
34439         Roo.each(xitems, function(i)  {
34440             region = nb && i.region ? i.region : false;
34441             
34442             var add = ret.addxtype(i);
34443            
34444             if (region) {
34445                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34446                 if (!i.background) {
34447                     abn[region] = nb[region] ;
34448                 }
34449             }
34450             
34451         });
34452         this.endUpdate();
34453
34454         // make the last non-background panel active..
34455         //if (nb) { Roo.log(abn); }
34456         if (nb) {
34457             
34458             for(var r in abn) {
34459                 region = this.getRegion(r);
34460                 if (region) {
34461                     // tried using nb[r], but it does not work..
34462                      
34463                     region.showPanel(abn[r]);
34464                    
34465                 }
34466             }
34467         }
34468         return ret;
34469         
34470     },
34471     
34472     
34473 // private
34474     factory : function(cfg)
34475     {
34476         
34477         var validRegions = Roo.bootstrap.layout.Border.regions;
34478
34479         var target = cfg.region;
34480         cfg.mgr = this;
34481         
34482         var r = Roo.bootstrap.layout;
34483         Roo.log(target);
34484         switch(target){
34485             case "north":
34486                 return new r.North(cfg);
34487             case "south":
34488                 return new r.South(cfg);
34489             case "east":
34490                 return new r.East(cfg);
34491             case "west":
34492                 return new r.West(cfg);
34493             case "center":
34494                 return new r.Center(cfg);
34495         }
34496         throw 'Layout region "'+target+'" not supported.';
34497     }
34498     
34499     
34500 });
34501  /*
34502  * Based on:
34503  * Ext JS Library 1.1.1
34504  * Copyright(c) 2006-2007, Ext JS, LLC.
34505  *
34506  * Originally Released Under LGPL - original licence link has changed is not relivant.
34507  *
34508  * Fork - LGPL
34509  * <script type="text/javascript">
34510  */
34511  
34512 /**
34513  * @class Roo.bootstrap.layout.Basic
34514  * @extends Roo.util.Observable
34515  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34516  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34517  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34518  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34519  * @cfg {string}   region  the region that it inhabits..
34520  * @cfg {bool}   skipConfig skip config?
34521  * 
34522
34523  */
34524 Roo.bootstrap.layout.Basic = function(config){
34525     
34526     this.mgr = config.mgr;
34527     
34528     this.position = config.region;
34529     
34530     var skipConfig = config.skipConfig;
34531     
34532     this.events = {
34533         /**
34534          * @scope Roo.BasicLayoutRegion
34535          */
34536         
34537         /**
34538          * @event beforeremove
34539          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34540          * @param {Roo.LayoutRegion} this
34541          * @param {Roo.ContentPanel} panel The panel
34542          * @param {Object} e The cancel event object
34543          */
34544         "beforeremove" : true,
34545         /**
34546          * @event invalidated
34547          * Fires when the layout for this region is changed.
34548          * @param {Roo.LayoutRegion} this
34549          */
34550         "invalidated" : true,
34551         /**
34552          * @event visibilitychange
34553          * Fires when this region is shown or hidden 
34554          * @param {Roo.LayoutRegion} this
34555          * @param {Boolean} visibility true or false
34556          */
34557         "visibilitychange" : true,
34558         /**
34559          * @event paneladded
34560          * Fires when a panel is added. 
34561          * @param {Roo.LayoutRegion} this
34562          * @param {Roo.ContentPanel} panel The panel
34563          */
34564         "paneladded" : true,
34565         /**
34566          * @event panelremoved
34567          * Fires when a panel is removed. 
34568          * @param {Roo.LayoutRegion} this
34569          * @param {Roo.ContentPanel} panel The panel
34570          */
34571         "panelremoved" : true,
34572         /**
34573          * @event beforecollapse
34574          * Fires when this region before collapse.
34575          * @param {Roo.LayoutRegion} this
34576          */
34577         "beforecollapse" : true,
34578         /**
34579          * @event collapsed
34580          * Fires when this region is collapsed.
34581          * @param {Roo.LayoutRegion} this
34582          */
34583         "collapsed" : true,
34584         /**
34585          * @event expanded
34586          * Fires when this region is expanded.
34587          * @param {Roo.LayoutRegion} this
34588          */
34589         "expanded" : true,
34590         /**
34591          * @event slideshow
34592          * Fires when this region is slid into view.
34593          * @param {Roo.LayoutRegion} this
34594          */
34595         "slideshow" : true,
34596         /**
34597          * @event slidehide
34598          * Fires when this region slides out of view. 
34599          * @param {Roo.LayoutRegion} this
34600          */
34601         "slidehide" : true,
34602         /**
34603          * @event panelactivated
34604          * Fires when a panel is activated. 
34605          * @param {Roo.LayoutRegion} this
34606          * @param {Roo.ContentPanel} panel The activated panel
34607          */
34608         "panelactivated" : true,
34609         /**
34610          * @event resized
34611          * Fires when the user resizes this region. 
34612          * @param {Roo.LayoutRegion} this
34613          * @param {Number} newSize The new size (width for east/west, height for north/south)
34614          */
34615         "resized" : true
34616     };
34617     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34618     this.panels = new Roo.util.MixedCollection();
34619     this.panels.getKey = this.getPanelId.createDelegate(this);
34620     this.box = null;
34621     this.activePanel = null;
34622     // ensure listeners are added...
34623     
34624     if (config.listeners || config.events) {
34625         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34626             listeners : config.listeners || {},
34627             events : config.events || {}
34628         });
34629     }
34630     
34631     if(skipConfig !== true){
34632         this.applyConfig(config);
34633     }
34634 };
34635
34636 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34637 {
34638     getPanelId : function(p){
34639         return p.getId();
34640     },
34641     
34642     applyConfig : function(config){
34643         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34644         this.config = config;
34645         
34646     },
34647     
34648     /**
34649      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34650      * the width, for horizontal (north, south) the height.
34651      * @param {Number} newSize The new width or height
34652      */
34653     resizeTo : function(newSize){
34654         var el = this.el ? this.el :
34655                  (this.activePanel ? this.activePanel.getEl() : null);
34656         if(el){
34657             switch(this.position){
34658                 case "east":
34659                 case "west":
34660                     el.setWidth(newSize);
34661                     this.fireEvent("resized", this, newSize);
34662                 break;
34663                 case "north":
34664                 case "south":
34665                     el.setHeight(newSize);
34666                     this.fireEvent("resized", this, newSize);
34667                 break;                
34668             }
34669         }
34670     },
34671     
34672     getBox : function(){
34673         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34674     },
34675     
34676     getMargins : function(){
34677         return this.margins;
34678     },
34679     
34680     updateBox : function(box){
34681         this.box = box;
34682         var el = this.activePanel.getEl();
34683         el.dom.style.left = box.x + "px";
34684         el.dom.style.top = box.y + "px";
34685         this.activePanel.setSize(box.width, box.height);
34686     },
34687     
34688     /**
34689      * Returns the container element for this region.
34690      * @return {Roo.Element}
34691      */
34692     getEl : function(){
34693         return this.activePanel;
34694     },
34695     
34696     /**
34697      * Returns true if this region is currently visible.
34698      * @return {Boolean}
34699      */
34700     isVisible : function(){
34701         return this.activePanel ? true : false;
34702     },
34703     
34704     setActivePanel : function(panel){
34705         panel = this.getPanel(panel);
34706         if(this.activePanel && this.activePanel != panel){
34707             this.activePanel.setActiveState(false);
34708             this.activePanel.getEl().setLeftTop(-10000,-10000);
34709         }
34710         this.activePanel = panel;
34711         panel.setActiveState(true);
34712         if(this.box){
34713             panel.setSize(this.box.width, this.box.height);
34714         }
34715         this.fireEvent("panelactivated", this, panel);
34716         this.fireEvent("invalidated");
34717     },
34718     
34719     /**
34720      * Show the specified panel.
34721      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34722      * @return {Roo.ContentPanel} The shown panel or null
34723      */
34724     showPanel : function(panel){
34725         panel = this.getPanel(panel);
34726         if(panel){
34727             this.setActivePanel(panel);
34728         }
34729         return panel;
34730     },
34731     
34732     /**
34733      * Get the active panel for this region.
34734      * @return {Roo.ContentPanel} The active panel or null
34735      */
34736     getActivePanel : function(){
34737         return this.activePanel;
34738     },
34739     
34740     /**
34741      * Add the passed ContentPanel(s)
34742      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34743      * @return {Roo.ContentPanel} The panel added (if only one was added)
34744      */
34745     add : function(panel){
34746         if(arguments.length > 1){
34747             for(var i = 0, len = arguments.length; i < len; i++) {
34748                 this.add(arguments[i]);
34749             }
34750             return null;
34751         }
34752         if(this.hasPanel(panel)){
34753             this.showPanel(panel);
34754             return panel;
34755         }
34756         var el = panel.getEl();
34757         if(el.dom.parentNode != this.mgr.el.dom){
34758             this.mgr.el.dom.appendChild(el.dom);
34759         }
34760         if(panel.setRegion){
34761             panel.setRegion(this);
34762         }
34763         this.panels.add(panel);
34764         el.setStyle("position", "absolute");
34765         if(!panel.background){
34766             this.setActivePanel(panel);
34767             if(this.config.initialSize && this.panels.getCount()==1){
34768                 this.resizeTo(this.config.initialSize);
34769             }
34770         }
34771         this.fireEvent("paneladded", this, panel);
34772         return panel;
34773     },
34774     
34775     /**
34776      * Returns true if the panel is in this region.
34777      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34778      * @return {Boolean}
34779      */
34780     hasPanel : function(panel){
34781         if(typeof panel == "object"){ // must be panel obj
34782             panel = panel.getId();
34783         }
34784         return this.getPanel(panel) ? true : false;
34785     },
34786     
34787     /**
34788      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34789      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34790      * @param {Boolean} preservePanel Overrides the config preservePanel option
34791      * @return {Roo.ContentPanel} The panel that was removed
34792      */
34793     remove : function(panel, preservePanel){
34794         panel = this.getPanel(panel);
34795         if(!panel){
34796             return null;
34797         }
34798         var e = {};
34799         this.fireEvent("beforeremove", this, panel, e);
34800         if(e.cancel === true){
34801             return null;
34802         }
34803         var panelId = panel.getId();
34804         this.panels.removeKey(panelId);
34805         return panel;
34806     },
34807     
34808     /**
34809      * Returns the panel specified or null if it's not in this region.
34810      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34811      * @return {Roo.ContentPanel}
34812      */
34813     getPanel : function(id){
34814         if(typeof id == "object"){ // must be panel obj
34815             return id;
34816         }
34817         return this.panels.get(id);
34818     },
34819     
34820     /**
34821      * Returns this regions position (north/south/east/west/center).
34822      * @return {String} 
34823      */
34824     getPosition: function(){
34825         return this.position;    
34826     }
34827 });/*
34828  * Based on:
34829  * Ext JS Library 1.1.1
34830  * Copyright(c) 2006-2007, Ext JS, LLC.
34831  *
34832  * Originally Released Under LGPL - original licence link has changed is not relivant.
34833  *
34834  * Fork - LGPL
34835  * <script type="text/javascript">
34836  */
34837  
34838 /**
34839  * @class Roo.bootstrap.layout.Region
34840  * @extends Roo.bootstrap.layout.Basic
34841  * This class represents a region in a layout manager.
34842  
34843  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34844  * @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})
34845  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34846  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34847  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34848  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34849  * @cfg {String}    title           The title for the region (overrides panel titles)
34850  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34851  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34852  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34853  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34854  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34855  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34856  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34857  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34858  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34859  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34860
34861  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34862  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34863  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34864  * @cfg {Number}    width           For East/West panels
34865  * @cfg {Number}    height          For North/South panels
34866  * @cfg {Boolean}   split           To show the splitter
34867  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34868  * 
34869  * @cfg {string}   cls             Extra CSS classes to add to region
34870  * 
34871  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34872  * @cfg {string}   region  the region that it inhabits..
34873  *
34874
34875  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34876  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34877
34878  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34879  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34880  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34881  */
34882 Roo.bootstrap.layout.Region = function(config)
34883 {
34884     this.applyConfig(config);
34885
34886     var mgr = config.mgr;
34887     var pos = config.region;
34888     config.skipConfig = true;
34889     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34890     
34891     if (mgr.el) {
34892         this.onRender(mgr.el);   
34893     }
34894      
34895     this.visible = true;
34896     this.collapsed = false;
34897     this.unrendered_panels = [];
34898 };
34899
34900 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34901
34902     position: '', // set by wrapper (eg. north/south etc..)
34903     unrendered_panels : null,  // unrendered panels.
34904     createBody : function(){
34905         /** This region's body element 
34906         * @type Roo.Element */
34907         this.bodyEl = this.el.createChild({
34908                 tag: "div",
34909                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34910         });
34911     },
34912
34913     onRender: function(ctr, pos)
34914     {
34915         var dh = Roo.DomHelper;
34916         /** This region's container element 
34917         * @type Roo.Element */
34918         this.el = dh.append(ctr.dom, {
34919                 tag: "div",
34920                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34921             }, true);
34922         /** This region's title element 
34923         * @type Roo.Element */
34924     
34925         this.titleEl = dh.append(this.el.dom,
34926             {
34927                     tag: "div",
34928                     unselectable: "on",
34929                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34930                     children:[
34931                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34932                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34933                     ]}, true);
34934         
34935         this.titleEl.enableDisplayMode();
34936         /** This region's title text element 
34937         * @type HTMLElement */
34938         this.titleTextEl = this.titleEl.dom.firstChild;
34939         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34940         /*
34941         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34942         this.closeBtn.enableDisplayMode();
34943         this.closeBtn.on("click", this.closeClicked, this);
34944         this.closeBtn.hide();
34945     */
34946         this.createBody(this.config);
34947         if(this.config.hideWhenEmpty){
34948             this.hide();
34949             this.on("paneladded", this.validateVisibility, this);
34950             this.on("panelremoved", this.validateVisibility, this);
34951         }
34952         if(this.autoScroll){
34953             this.bodyEl.setStyle("overflow", "auto");
34954         }else{
34955             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34956         }
34957         //if(c.titlebar !== false){
34958             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34959                 this.titleEl.hide();
34960             }else{
34961                 this.titleEl.show();
34962                 if(this.config.title){
34963                     this.titleTextEl.innerHTML = this.config.title;
34964                 }
34965             }
34966         //}
34967         if(this.config.collapsed){
34968             this.collapse(true);
34969         }
34970         if(this.config.hidden){
34971             this.hide();
34972         }
34973         
34974         if (this.unrendered_panels && this.unrendered_panels.length) {
34975             for (var i =0;i< this.unrendered_panels.length; i++) {
34976                 this.add(this.unrendered_panels[i]);
34977             }
34978             this.unrendered_panels = null;
34979             
34980         }
34981         
34982     },
34983     
34984     applyConfig : function(c)
34985     {
34986         /*
34987          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34988             var dh = Roo.DomHelper;
34989             if(c.titlebar !== false){
34990                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34991                 this.collapseBtn.on("click", this.collapse, this);
34992                 this.collapseBtn.enableDisplayMode();
34993                 /*
34994                 if(c.showPin === true || this.showPin){
34995                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34996                     this.stickBtn.enableDisplayMode();
34997                     this.stickBtn.on("click", this.expand, this);
34998                     this.stickBtn.hide();
34999                 }
35000                 
35001             }
35002             */
35003             /** This region's collapsed element
35004             * @type Roo.Element */
35005             /*
35006              *
35007             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35008                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35009             ]}, true);
35010             
35011             if(c.floatable !== false){
35012                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35013                this.collapsedEl.on("click", this.collapseClick, this);
35014             }
35015
35016             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35017                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35018                    id: "message", unselectable: "on", style:{"float":"left"}});
35019                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35020              }
35021             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35022             this.expandBtn.on("click", this.expand, this);
35023             
35024         }
35025         
35026         if(this.collapseBtn){
35027             this.collapseBtn.setVisible(c.collapsible == true);
35028         }
35029         
35030         this.cmargins = c.cmargins || this.cmargins ||
35031                          (this.position == "west" || this.position == "east" ?
35032                              {top: 0, left: 2, right:2, bottom: 0} :
35033                              {top: 2, left: 0, right:0, bottom: 2});
35034         */
35035         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35036         
35037         
35038         this.bottomTabs = c.tabPosition != "top";
35039         
35040         this.autoScroll = c.autoScroll || false;
35041         
35042         
35043        
35044         
35045         this.duration = c.duration || .30;
35046         this.slideDuration = c.slideDuration || .45;
35047         this.config = c;
35048        
35049     },
35050     /**
35051      * Returns true if this region is currently visible.
35052      * @return {Boolean}
35053      */
35054     isVisible : function(){
35055         return this.visible;
35056     },
35057
35058     /**
35059      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35060      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35061      */
35062     //setCollapsedTitle : function(title){
35063     //    title = title || "&#160;";
35064      //   if(this.collapsedTitleTextEl){
35065       //      this.collapsedTitleTextEl.innerHTML = title;
35066        // }
35067     //},
35068
35069     getBox : function(){
35070         var b;
35071       //  if(!this.collapsed){
35072             b = this.el.getBox(false, true);
35073        // }else{
35074           //  b = this.collapsedEl.getBox(false, true);
35075         //}
35076         return b;
35077     },
35078
35079     getMargins : function(){
35080         return this.margins;
35081         //return this.collapsed ? this.cmargins : this.margins;
35082     },
35083 /*
35084     highlight : function(){
35085         this.el.addClass("x-layout-panel-dragover");
35086     },
35087
35088     unhighlight : function(){
35089         this.el.removeClass("x-layout-panel-dragover");
35090     },
35091 */
35092     updateBox : function(box)
35093     {
35094         if (!this.bodyEl) {
35095             return; // not rendered yet..
35096         }
35097         
35098         this.box = box;
35099         if(!this.collapsed){
35100             this.el.dom.style.left = box.x + "px";
35101             this.el.dom.style.top = box.y + "px";
35102             this.updateBody(box.width, box.height);
35103         }else{
35104             this.collapsedEl.dom.style.left = box.x + "px";
35105             this.collapsedEl.dom.style.top = box.y + "px";
35106             this.collapsedEl.setSize(box.width, box.height);
35107         }
35108         if(this.tabs){
35109             this.tabs.autoSizeTabs();
35110         }
35111     },
35112
35113     updateBody : function(w, h)
35114     {
35115         if(w !== null){
35116             this.el.setWidth(w);
35117             w -= this.el.getBorderWidth("rl");
35118             if(this.config.adjustments){
35119                 w += this.config.adjustments[0];
35120             }
35121         }
35122         if(h !== null && h > 0){
35123             this.el.setHeight(h);
35124             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35125             h -= this.el.getBorderWidth("tb");
35126             if(this.config.adjustments){
35127                 h += this.config.adjustments[1];
35128             }
35129             this.bodyEl.setHeight(h);
35130             if(this.tabs){
35131                 h = this.tabs.syncHeight(h);
35132             }
35133         }
35134         if(this.panelSize){
35135             w = w !== null ? w : this.panelSize.width;
35136             h = h !== null ? h : this.panelSize.height;
35137         }
35138         if(this.activePanel){
35139             var el = this.activePanel.getEl();
35140             w = w !== null ? w : el.getWidth();
35141             h = h !== null ? h : el.getHeight();
35142             this.panelSize = {width: w, height: h};
35143             this.activePanel.setSize(w, h);
35144         }
35145         if(Roo.isIE && this.tabs){
35146             this.tabs.el.repaint();
35147         }
35148     },
35149
35150     /**
35151      * Returns the container element for this region.
35152      * @return {Roo.Element}
35153      */
35154     getEl : function(){
35155         return this.el;
35156     },
35157
35158     /**
35159      * Hides this region.
35160      */
35161     hide : function(){
35162         //if(!this.collapsed){
35163             this.el.dom.style.left = "-2000px";
35164             this.el.hide();
35165         //}else{
35166          //   this.collapsedEl.dom.style.left = "-2000px";
35167          //   this.collapsedEl.hide();
35168        // }
35169         this.visible = false;
35170         this.fireEvent("visibilitychange", this, false);
35171     },
35172
35173     /**
35174      * Shows this region if it was previously hidden.
35175      */
35176     show : function(){
35177         //if(!this.collapsed){
35178             this.el.show();
35179         //}else{
35180         //    this.collapsedEl.show();
35181        // }
35182         this.visible = true;
35183         this.fireEvent("visibilitychange", this, true);
35184     },
35185 /*
35186     closeClicked : function(){
35187         if(this.activePanel){
35188             this.remove(this.activePanel);
35189         }
35190     },
35191
35192     collapseClick : function(e){
35193         if(this.isSlid){
35194            e.stopPropagation();
35195            this.slideIn();
35196         }else{
35197            e.stopPropagation();
35198            this.slideOut();
35199         }
35200     },
35201 */
35202     /**
35203      * Collapses this region.
35204      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35205      */
35206     /*
35207     collapse : function(skipAnim, skipCheck = false){
35208         if(this.collapsed) {
35209             return;
35210         }
35211         
35212         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35213             
35214             this.collapsed = true;
35215             if(this.split){
35216                 this.split.el.hide();
35217             }
35218             if(this.config.animate && skipAnim !== true){
35219                 this.fireEvent("invalidated", this);
35220                 this.animateCollapse();
35221             }else{
35222                 this.el.setLocation(-20000,-20000);
35223                 this.el.hide();
35224                 this.collapsedEl.show();
35225                 this.fireEvent("collapsed", this);
35226                 this.fireEvent("invalidated", this);
35227             }
35228         }
35229         
35230     },
35231 */
35232     animateCollapse : function(){
35233         // overridden
35234     },
35235
35236     /**
35237      * Expands this region if it was previously collapsed.
35238      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35239      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35240      */
35241     /*
35242     expand : function(e, skipAnim){
35243         if(e) {
35244             e.stopPropagation();
35245         }
35246         if(!this.collapsed || this.el.hasActiveFx()) {
35247             return;
35248         }
35249         if(this.isSlid){
35250             this.afterSlideIn();
35251             skipAnim = true;
35252         }
35253         this.collapsed = false;
35254         if(this.config.animate && skipAnim !== true){
35255             this.animateExpand();
35256         }else{
35257             this.el.show();
35258             if(this.split){
35259                 this.split.el.show();
35260             }
35261             this.collapsedEl.setLocation(-2000,-2000);
35262             this.collapsedEl.hide();
35263             this.fireEvent("invalidated", this);
35264             this.fireEvent("expanded", this);
35265         }
35266     },
35267 */
35268     animateExpand : function(){
35269         // overridden
35270     },
35271
35272     initTabs : function()
35273     {
35274         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35275         
35276         var ts = new Roo.bootstrap.panel.Tabs({
35277                 el: this.bodyEl.dom,
35278                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35279                 disableTooltips: this.config.disableTabTips,
35280                 toolbar : this.config.toolbar
35281             });
35282         
35283         if(this.config.hideTabs){
35284             ts.stripWrap.setDisplayed(false);
35285         }
35286         this.tabs = ts;
35287         ts.resizeTabs = this.config.resizeTabs === true;
35288         ts.minTabWidth = this.config.minTabWidth || 40;
35289         ts.maxTabWidth = this.config.maxTabWidth || 250;
35290         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35291         ts.monitorResize = false;
35292         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35293         ts.bodyEl.addClass('roo-layout-tabs-body');
35294         this.panels.each(this.initPanelAsTab, this);
35295     },
35296
35297     initPanelAsTab : function(panel){
35298         var ti = this.tabs.addTab(
35299             panel.getEl().id,
35300             panel.getTitle(),
35301             null,
35302             this.config.closeOnTab && panel.isClosable(),
35303             panel.tpl
35304         );
35305         if(panel.tabTip !== undefined){
35306             ti.setTooltip(panel.tabTip);
35307         }
35308         ti.on("activate", function(){
35309               this.setActivePanel(panel);
35310         }, this);
35311         
35312         if(this.config.closeOnTab){
35313             ti.on("beforeclose", function(t, e){
35314                 e.cancel = true;
35315                 this.remove(panel);
35316             }, this);
35317         }
35318         
35319         panel.tabItem = ti;
35320         
35321         return ti;
35322     },
35323
35324     updatePanelTitle : function(panel, title)
35325     {
35326         if(this.activePanel == panel){
35327             this.updateTitle(title);
35328         }
35329         if(this.tabs){
35330             var ti = this.tabs.getTab(panel.getEl().id);
35331             ti.setText(title);
35332             if(panel.tabTip !== undefined){
35333                 ti.setTooltip(panel.tabTip);
35334             }
35335         }
35336     },
35337
35338     updateTitle : function(title){
35339         if(this.titleTextEl && !this.config.title){
35340             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35341         }
35342     },
35343
35344     setActivePanel : function(panel)
35345     {
35346         panel = this.getPanel(panel);
35347         if(this.activePanel && this.activePanel != panel){
35348             this.activePanel.setActiveState(false);
35349         }
35350         this.activePanel = panel;
35351         panel.setActiveState(true);
35352         if(this.panelSize){
35353             panel.setSize(this.panelSize.width, this.panelSize.height);
35354         }
35355         if(this.closeBtn){
35356             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35357         }
35358         this.updateTitle(panel.getTitle());
35359         if(this.tabs){
35360             this.fireEvent("invalidated", this);
35361         }
35362         this.fireEvent("panelactivated", this, panel);
35363     },
35364
35365     /**
35366      * Shows the specified panel.
35367      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35368      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35369      */
35370     showPanel : function(panel)
35371     {
35372         panel = this.getPanel(panel);
35373         if(panel){
35374             if(this.tabs){
35375                 var tab = this.tabs.getTab(panel.getEl().id);
35376                 if(tab.isHidden()){
35377                     this.tabs.unhideTab(tab.id);
35378                 }
35379                 tab.activate();
35380             }else{
35381                 this.setActivePanel(panel);
35382             }
35383         }
35384         return panel;
35385     },
35386
35387     /**
35388      * Get the active panel for this region.
35389      * @return {Roo.ContentPanel} The active panel or null
35390      */
35391     getActivePanel : function(){
35392         return this.activePanel;
35393     },
35394
35395     validateVisibility : function(){
35396         if(this.panels.getCount() < 1){
35397             this.updateTitle("&#160;");
35398             this.closeBtn.hide();
35399             this.hide();
35400         }else{
35401             if(!this.isVisible()){
35402                 this.show();
35403             }
35404         }
35405     },
35406
35407     /**
35408      * Adds the passed ContentPanel(s) to this region.
35409      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35410      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35411      */
35412     add : function(panel)
35413     {
35414         if(arguments.length > 1){
35415             for(var i = 0, len = arguments.length; i < len; i++) {
35416                 this.add(arguments[i]);
35417             }
35418             return null;
35419         }
35420         
35421         // if we have not been rendered yet, then we can not really do much of this..
35422         if (!this.bodyEl) {
35423             this.unrendered_panels.push(panel);
35424             return panel;
35425         }
35426         
35427         
35428         
35429         
35430         if(this.hasPanel(panel)){
35431             this.showPanel(panel);
35432             return panel;
35433         }
35434         panel.setRegion(this);
35435         this.panels.add(panel);
35436        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35437             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35438             // and hide them... ???
35439             this.bodyEl.dom.appendChild(panel.getEl().dom);
35440             if(panel.background !== true){
35441                 this.setActivePanel(panel);
35442             }
35443             this.fireEvent("paneladded", this, panel);
35444             return panel;
35445         }
35446         */
35447         if(!this.tabs){
35448             this.initTabs();
35449         }else{
35450             this.initPanelAsTab(panel);
35451         }
35452         
35453         
35454         if(panel.background !== true){
35455             this.tabs.activate(panel.getEl().id);
35456         }
35457         this.fireEvent("paneladded", this, panel);
35458         return panel;
35459     },
35460
35461     /**
35462      * Hides the tab for the specified panel.
35463      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35464      */
35465     hidePanel : function(panel){
35466         if(this.tabs && (panel = this.getPanel(panel))){
35467             this.tabs.hideTab(panel.getEl().id);
35468         }
35469     },
35470
35471     /**
35472      * Unhides the tab for a previously hidden panel.
35473      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35474      */
35475     unhidePanel : function(panel){
35476         if(this.tabs && (panel = this.getPanel(panel))){
35477             this.tabs.unhideTab(panel.getEl().id);
35478         }
35479     },
35480
35481     clearPanels : function(){
35482         while(this.panels.getCount() > 0){
35483              this.remove(this.panels.first());
35484         }
35485     },
35486
35487     /**
35488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35489      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35490      * @param {Boolean} preservePanel Overrides the config preservePanel option
35491      * @return {Roo.ContentPanel} The panel that was removed
35492      */
35493     remove : function(panel, preservePanel)
35494     {
35495         panel = this.getPanel(panel);
35496         if(!panel){
35497             return null;
35498         }
35499         var e = {};
35500         this.fireEvent("beforeremove", this, panel, e);
35501         if(e.cancel === true){
35502             return null;
35503         }
35504         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35505         var panelId = panel.getId();
35506         this.panels.removeKey(panelId);
35507         if(preservePanel){
35508             document.body.appendChild(panel.getEl().dom);
35509         }
35510         if(this.tabs){
35511             this.tabs.removeTab(panel.getEl().id);
35512         }else if (!preservePanel){
35513             this.bodyEl.dom.removeChild(panel.getEl().dom);
35514         }
35515         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35516             var p = this.panels.first();
35517             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35518             tempEl.appendChild(p.getEl().dom);
35519             this.bodyEl.update("");
35520             this.bodyEl.dom.appendChild(p.getEl().dom);
35521             tempEl = null;
35522             this.updateTitle(p.getTitle());
35523             this.tabs = null;
35524             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35525             this.setActivePanel(p);
35526         }
35527         panel.setRegion(null);
35528         if(this.activePanel == panel){
35529             this.activePanel = null;
35530         }
35531         if(this.config.autoDestroy !== false && preservePanel !== true){
35532             try{panel.destroy();}catch(e){}
35533         }
35534         this.fireEvent("panelremoved", this, panel);
35535         return panel;
35536     },
35537
35538     /**
35539      * Returns the TabPanel component used by this region
35540      * @return {Roo.TabPanel}
35541      */
35542     getTabs : function(){
35543         return this.tabs;
35544     },
35545
35546     createTool : function(parentEl, className){
35547         var btn = Roo.DomHelper.append(parentEl, {
35548             tag: "div",
35549             cls: "x-layout-tools-button",
35550             children: [ {
35551                 tag: "div",
35552                 cls: "roo-layout-tools-button-inner " + className,
35553                 html: "&#160;"
35554             }]
35555         }, true);
35556         btn.addClassOnOver("roo-layout-tools-button-over");
35557         return btn;
35558     }
35559 });/*
35560  * Based on:
35561  * Ext JS Library 1.1.1
35562  * Copyright(c) 2006-2007, Ext JS, LLC.
35563  *
35564  * Originally Released Under LGPL - original licence link has changed is not relivant.
35565  *
35566  * Fork - LGPL
35567  * <script type="text/javascript">
35568  */
35569  
35570
35571
35572 /**
35573  * @class Roo.SplitLayoutRegion
35574  * @extends Roo.LayoutRegion
35575  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35576  */
35577 Roo.bootstrap.layout.Split = function(config){
35578     this.cursor = config.cursor;
35579     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35580 };
35581
35582 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35583 {
35584     splitTip : "Drag to resize.",
35585     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35586     useSplitTips : false,
35587
35588     applyConfig : function(config){
35589         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35590     },
35591     
35592     onRender : function(ctr,pos) {
35593         
35594         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35595         if(!this.config.split){
35596             return;
35597         }
35598         if(!this.split){
35599             
35600             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35601                             tag: "div",
35602                             id: this.el.id + "-split",
35603                             cls: "roo-layout-split roo-layout-split-"+this.position,
35604                             html: "&#160;"
35605             });
35606             /** The SplitBar for this region 
35607             * @type Roo.SplitBar */
35608             // does not exist yet...
35609             Roo.log([this.position, this.orientation]);
35610             
35611             this.split = new Roo.bootstrap.SplitBar({
35612                 dragElement : splitEl,
35613                 resizingElement: this.el,
35614                 orientation : this.orientation
35615             });
35616             
35617             this.split.on("moved", this.onSplitMove, this);
35618             this.split.useShim = this.config.useShim === true;
35619             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35620             if(this.useSplitTips){
35621                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35622             }
35623             //if(config.collapsible){
35624             //    this.split.el.on("dblclick", this.collapse,  this);
35625             //}
35626         }
35627         if(typeof this.config.minSize != "undefined"){
35628             this.split.minSize = this.config.minSize;
35629         }
35630         if(typeof this.config.maxSize != "undefined"){
35631             this.split.maxSize = this.config.maxSize;
35632         }
35633         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35634             this.hideSplitter();
35635         }
35636         
35637     },
35638
35639     getHMaxSize : function(){
35640          var cmax = this.config.maxSize || 10000;
35641          var center = this.mgr.getRegion("center");
35642          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35643     },
35644
35645     getVMaxSize : function(){
35646          var cmax = this.config.maxSize || 10000;
35647          var center = this.mgr.getRegion("center");
35648          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35649     },
35650
35651     onSplitMove : function(split, newSize){
35652         this.fireEvent("resized", this, newSize);
35653     },
35654     
35655     /** 
35656      * Returns the {@link Roo.SplitBar} for this region.
35657      * @return {Roo.SplitBar}
35658      */
35659     getSplitBar : function(){
35660         return this.split;
35661     },
35662     
35663     hide : function(){
35664         this.hideSplitter();
35665         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35666     },
35667
35668     hideSplitter : function(){
35669         if(this.split){
35670             this.split.el.setLocation(-2000,-2000);
35671             this.split.el.hide();
35672         }
35673     },
35674
35675     show : function(){
35676         if(this.split){
35677             this.split.el.show();
35678         }
35679         Roo.bootstrap.layout.Split.superclass.show.call(this);
35680     },
35681     
35682     beforeSlide: function(){
35683         if(Roo.isGecko){// firefox overflow auto bug workaround
35684             this.bodyEl.clip();
35685             if(this.tabs) {
35686                 this.tabs.bodyEl.clip();
35687             }
35688             if(this.activePanel){
35689                 this.activePanel.getEl().clip();
35690                 
35691                 if(this.activePanel.beforeSlide){
35692                     this.activePanel.beforeSlide();
35693                 }
35694             }
35695         }
35696     },
35697     
35698     afterSlide : function(){
35699         if(Roo.isGecko){// firefox overflow auto bug workaround
35700             this.bodyEl.unclip();
35701             if(this.tabs) {
35702                 this.tabs.bodyEl.unclip();
35703             }
35704             if(this.activePanel){
35705                 this.activePanel.getEl().unclip();
35706                 if(this.activePanel.afterSlide){
35707                     this.activePanel.afterSlide();
35708                 }
35709             }
35710         }
35711     },
35712
35713     initAutoHide : function(){
35714         if(this.autoHide !== false){
35715             if(!this.autoHideHd){
35716                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35717                 this.autoHideHd = {
35718                     "mouseout": function(e){
35719                         if(!e.within(this.el, true)){
35720                             st.delay(500);
35721                         }
35722                     },
35723                     "mouseover" : function(e){
35724                         st.cancel();
35725                     },
35726                     scope : this
35727                 };
35728             }
35729             this.el.on(this.autoHideHd);
35730         }
35731     },
35732
35733     clearAutoHide : function(){
35734         if(this.autoHide !== false){
35735             this.el.un("mouseout", this.autoHideHd.mouseout);
35736             this.el.un("mouseover", this.autoHideHd.mouseover);
35737         }
35738     },
35739
35740     clearMonitor : function(){
35741         Roo.get(document).un("click", this.slideInIf, this);
35742     },
35743
35744     // these names are backwards but not changed for compat
35745     slideOut : function(){
35746         if(this.isSlid || this.el.hasActiveFx()){
35747             return;
35748         }
35749         this.isSlid = true;
35750         if(this.collapseBtn){
35751             this.collapseBtn.hide();
35752         }
35753         this.closeBtnState = this.closeBtn.getStyle('display');
35754         this.closeBtn.hide();
35755         if(this.stickBtn){
35756             this.stickBtn.show();
35757         }
35758         this.el.show();
35759         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35760         this.beforeSlide();
35761         this.el.setStyle("z-index", 10001);
35762         this.el.slideIn(this.getSlideAnchor(), {
35763             callback: function(){
35764                 this.afterSlide();
35765                 this.initAutoHide();
35766                 Roo.get(document).on("click", this.slideInIf, this);
35767                 this.fireEvent("slideshow", this);
35768             },
35769             scope: this,
35770             block: true
35771         });
35772     },
35773
35774     afterSlideIn : function(){
35775         this.clearAutoHide();
35776         this.isSlid = false;
35777         this.clearMonitor();
35778         this.el.setStyle("z-index", "");
35779         if(this.collapseBtn){
35780             this.collapseBtn.show();
35781         }
35782         this.closeBtn.setStyle('display', this.closeBtnState);
35783         if(this.stickBtn){
35784             this.stickBtn.hide();
35785         }
35786         this.fireEvent("slidehide", this);
35787     },
35788
35789     slideIn : function(cb){
35790         if(!this.isSlid || this.el.hasActiveFx()){
35791             Roo.callback(cb);
35792             return;
35793         }
35794         this.isSlid = false;
35795         this.beforeSlide();
35796         this.el.slideOut(this.getSlideAnchor(), {
35797             callback: function(){
35798                 this.el.setLeftTop(-10000, -10000);
35799                 this.afterSlide();
35800                 this.afterSlideIn();
35801                 Roo.callback(cb);
35802             },
35803             scope: this,
35804             block: true
35805         });
35806     },
35807     
35808     slideInIf : function(e){
35809         if(!e.within(this.el)){
35810             this.slideIn();
35811         }
35812     },
35813
35814     animateCollapse : function(){
35815         this.beforeSlide();
35816         this.el.setStyle("z-index", 20000);
35817         var anchor = this.getSlideAnchor();
35818         this.el.slideOut(anchor, {
35819             callback : function(){
35820                 this.el.setStyle("z-index", "");
35821                 this.collapsedEl.slideIn(anchor, {duration:.3});
35822                 this.afterSlide();
35823                 this.el.setLocation(-10000,-10000);
35824                 this.el.hide();
35825                 this.fireEvent("collapsed", this);
35826             },
35827             scope: this,
35828             block: true
35829         });
35830     },
35831
35832     animateExpand : function(){
35833         this.beforeSlide();
35834         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35835         this.el.setStyle("z-index", 20000);
35836         this.collapsedEl.hide({
35837             duration:.1
35838         });
35839         this.el.slideIn(this.getSlideAnchor(), {
35840             callback : function(){
35841                 this.el.setStyle("z-index", "");
35842                 this.afterSlide();
35843                 if(this.split){
35844                     this.split.el.show();
35845                 }
35846                 this.fireEvent("invalidated", this);
35847                 this.fireEvent("expanded", this);
35848             },
35849             scope: this,
35850             block: true
35851         });
35852     },
35853
35854     anchors : {
35855         "west" : "left",
35856         "east" : "right",
35857         "north" : "top",
35858         "south" : "bottom"
35859     },
35860
35861     sanchors : {
35862         "west" : "l",
35863         "east" : "r",
35864         "north" : "t",
35865         "south" : "b"
35866     },
35867
35868     canchors : {
35869         "west" : "tl-tr",
35870         "east" : "tr-tl",
35871         "north" : "tl-bl",
35872         "south" : "bl-tl"
35873     },
35874
35875     getAnchor : function(){
35876         return this.anchors[this.position];
35877     },
35878
35879     getCollapseAnchor : function(){
35880         return this.canchors[this.position];
35881     },
35882
35883     getSlideAnchor : function(){
35884         return this.sanchors[this.position];
35885     },
35886
35887     getAlignAdj : function(){
35888         var cm = this.cmargins;
35889         switch(this.position){
35890             case "west":
35891                 return [0, 0];
35892             break;
35893             case "east":
35894                 return [0, 0];
35895             break;
35896             case "north":
35897                 return [0, 0];
35898             break;
35899             case "south":
35900                 return [0, 0];
35901             break;
35902         }
35903     },
35904
35905     getExpandAdj : function(){
35906         var c = this.collapsedEl, cm = this.cmargins;
35907         switch(this.position){
35908             case "west":
35909                 return [-(cm.right+c.getWidth()+cm.left), 0];
35910             break;
35911             case "east":
35912                 return [cm.right+c.getWidth()+cm.left, 0];
35913             break;
35914             case "north":
35915                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35916             break;
35917             case "south":
35918                 return [0, cm.top+cm.bottom+c.getHeight()];
35919             break;
35920         }
35921     }
35922 });/*
35923  * Based on:
35924  * Ext JS Library 1.1.1
35925  * Copyright(c) 2006-2007, Ext JS, LLC.
35926  *
35927  * Originally Released Under LGPL - original licence link has changed is not relivant.
35928  *
35929  * Fork - LGPL
35930  * <script type="text/javascript">
35931  */
35932 /*
35933  * These classes are private internal classes
35934  */
35935 Roo.bootstrap.layout.Center = function(config){
35936     config.region = "center";
35937     Roo.bootstrap.layout.Region.call(this, config);
35938     this.visible = true;
35939     this.minWidth = config.minWidth || 20;
35940     this.minHeight = config.minHeight || 20;
35941 };
35942
35943 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35944     hide : function(){
35945         // center panel can't be hidden
35946     },
35947     
35948     show : function(){
35949         // center panel can't be hidden
35950     },
35951     
35952     getMinWidth: function(){
35953         return this.minWidth;
35954     },
35955     
35956     getMinHeight: function(){
35957         return this.minHeight;
35958     }
35959 });
35960
35961
35962
35963
35964  
35965
35966
35967
35968
35969
35970 Roo.bootstrap.layout.North = function(config)
35971 {
35972     config.region = 'north';
35973     config.cursor = 'n-resize';
35974     
35975     Roo.bootstrap.layout.Split.call(this, config);
35976     
35977     
35978     if(this.split){
35979         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35980         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35981         this.split.el.addClass("roo-layout-split-v");
35982     }
35983     var size = config.initialSize || config.height;
35984     if(typeof size != "undefined"){
35985         this.el.setHeight(size);
35986     }
35987 };
35988 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35989 {
35990     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35991     
35992     
35993     
35994     getBox : function(){
35995         if(this.collapsed){
35996             return this.collapsedEl.getBox();
35997         }
35998         var box = this.el.getBox();
35999         if(this.split){
36000             box.height += this.split.el.getHeight();
36001         }
36002         return box;
36003     },
36004     
36005     updateBox : function(box){
36006         if(this.split && !this.collapsed){
36007             box.height -= this.split.el.getHeight();
36008             this.split.el.setLeft(box.x);
36009             this.split.el.setTop(box.y+box.height);
36010             this.split.el.setWidth(box.width);
36011         }
36012         if(this.collapsed){
36013             this.updateBody(box.width, null);
36014         }
36015         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36016     }
36017 });
36018
36019
36020
36021
36022
36023 Roo.bootstrap.layout.South = function(config){
36024     config.region = 'south';
36025     config.cursor = 's-resize';
36026     Roo.bootstrap.layout.Split.call(this, config);
36027     if(this.split){
36028         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36029         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36030         this.split.el.addClass("roo-layout-split-v");
36031     }
36032     var size = config.initialSize || config.height;
36033     if(typeof size != "undefined"){
36034         this.el.setHeight(size);
36035     }
36036 };
36037
36038 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36039     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36040     getBox : function(){
36041         if(this.collapsed){
36042             return this.collapsedEl.getBox();
36043         }
36044         var box = this.el.getBox();
36045         if(this.split){
36046             var sh = this.split.el.getHeight();
36047             box.height += sh;
36048             box.y -= sh;
36049         }
36050         return box;
36051     },
36052     
36053     updateBox : function(box){
36054         if(this.split && !this.collapsed){
36055             var sh = this.split.el.getHeight();
36056             box.height -= sh;
36057             box.y += sh;
36058             this.split.el.setLeft(box.x);
36059             this.split.el.setTop(box.y-sh);
36060             this.split.el.setWidth(box.width);
36061         }
36062         if(this.collapsed){
36063             this.updateBody(box.width, null);
36064         }
36065         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36066     }
36067 });
36068
36069 Roo.bootstrap.layout.East = function(config){
36070     config.region = "east";
36071     config.cursor = "e-resize";
36072     Roo.bootstrap.layout.Split.call(this, config);
36073     if(this.split){
36074         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36075         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36076         this.split.el.addClass("roo-layout-split-h");
36077     }
36078     var size = config.initialSize || config.width;
36079     if(typeof size != "undefined"){
36080         this.el.setWidth(size);
36081     }
36082 };
36083 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36084     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36085     getBox : function(){
36086         if(this.collapsed){
36087             return this.collapsedEl.getBox();
36088         }
36089         var box = this.el.getBox();
36090         if(this.split){
36091             var sw = this.split.el.getWidth();
36092             box.width += sw;
36093             box.x -= sw;
36094         }
36095         return box;
36096     },
36097
36098     updateBox : function(box){
36099         if(this.split && !this.collapsed){
36100             var sw = this.split.el.getWidth();
36101             box.width -= sw;
36102             this.split.el.setLeft(box.x);
36103             this.split.el.setTop(box.y);
36104             this.split.el.setHeight(box.height);
36105             box.x += sw;
36106         }
36107         if(this.collapsed){
36108             this.updateBody(null, box.height);
36109         }
36110         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36111     }
36112 });
36113
36114 Roo.bootstrap.layout.West = function(config){
36115     config.region = "west";
36116     config.cursor = "w-resize";
36117     
36118     Roo.bootstrap.layout.Split.call(this, config);
36119     if(this.split){
36120         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36121         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36122         this.split.el.addClass("roo-layout-split-h");
36123     }
36124     
36125 };
36126 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36127     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36128     
36129     onRender: function(ctr, pos)
36130     {
36131         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36132         var size = this.config.initialSize || this.config.width;
36133         if(typeof size != "undefined"){
36134             this.el.setWidth(size);
36135         }
36136     },
36137     
36138     getBox : function(){
36139         if(this.collapsed){
36140             return this.collapsedEl.getBox();
36141         }
36142         var box = this.el.getBox();
36143         if(this.split){
36144             box.width += this.split.el.getWidth();
36145         }
36146         return box;
36147     },
36148     
36149     updateBox : function(box){
36150         if(this.split && !this.collapsed){
36151             var sw = this.split.el.getWidth();
36152             box.width -= sw;
36153             this.split.el.setLeft(box.x+box.width);
36154             this.split.el.setTop(box.y);
36155             this.split.el.setHeight(box.height);
36156         }
36157         if(this.collapsed){
36158             this.updateBody(null, box.height);
36159         }
36160         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36161     }
36162 });
36163 Roo.namespace("Roo.bootstrap.panel");/*
36164  * Based on:
36165  * Ext JS Library 1.1.1
36166  * Copyright(c) 2006-2007, Ext JS, LLC.
36167  *
36168  * Originally Released Under LGPL - original licence link has changed is not relivant.
36169  *
36170  * Fork - LGPL
36171  * <script type="text/javascript">
36172  */
36173 /**
36174  * @class Roo.ContentPanel
36175  * @extends Roo.util.Observable
36176  * A basic ContentPanel element.
36177  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36178  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36179  * @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
36180  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36181  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36182  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36183  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36184  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36185  * @cfg {String} title          The title for this panel
36186  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36187  * @cfg {String} url            Calls {@link #setUrl} with this value
36188  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36189  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36190  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36191  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36192  * @cfg {Boolean} badges render the badges
36193
36194  * @constructor
36195  * Create a new ContentPanel.
36196  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36197  * @param {String/Object} config A string to set only the title or a config object
36198  * @param {String} content (optional) Set the HTML content for this panel
36199  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36200  */
36201 Roo.bootstrap.panel.Content = function( config){
36202     
36203     this.tpl = config.tpl || false;
36204     
36205     var el = config.el;
36206     var content = config.content;
36207
36208     if(config.autoCreate){ // xtype is available if this is called from factory
36209         el = Roo.id();
36210     }
36211     this.el = Roo.get(el);
36212     if(!this.el && config && config.autoCreate){
36213         if(typeof config.autoCreate == "object"){
36214             if(!config.autoCreate.id){
36215                 config.autoCreate.id = config.id||el;
36216             }
36217             this.el = Roo.DomHelper.append(document.body,
36218                         config.autoCreate, true);
36219         }else{
36220             var elcfg =  {   tag: "div",
36221                             cls: "roo-layout-inactive-content",
36222                             id: config.id||el
36223                             };
36224             if (config.html) {
36225                 elcfg.html = config.html;
36226                 
36227             }
36228                         
36229             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36230         }
36231     } 
36232     this.closable = false;
36233     this.loaded = false;
36234     this.active = false;
36235    
36236       
36237     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36238         
36239         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36240         
36241         this.wrapEl = this.el; //this.el.wrap();
36242         var ti = [];
36243         if (config.toolbar.items) {
36244             ti = config.toolbar.items ;
36245             delete config.toolbar.items ;
36246         }
36247         
36248         var nitems = [];
36249         this.toolbar.render(this.wrapEl, 'before');
36250         for(var i =0;i < ti.length;i++) {
36251           //  Roo.log(['add child', items[i]]);
36252             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36253         }
36254         this.toolbar.items = nitems;
36255         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36256         delete config.toolbar;
36257         
36258     }
36259     /*
36260     // xtype created footer. - not sure if will work as we normally have to render first..
36261     if (this.footer && !this.footer.el && this.footer.xtype) {
36262         if (!this.wrapEl) {
36263             this.wrapEl = this.el.wrap();
36264         }
36265     
36266         this.footer.container = this.wrapEl.createChild();
36267          
36268         this.footer = Roo.factory(this.footer, Roo);
36269         
36270     }
36271     */
36272     
36273      if(typeof config == "string"){
36274         this.title = config;
36275     }else{
36276         Roo.apply(this, config);
36277     }
36278     
36279     if(this.resizeEl){
36280         this.resizeEl = Roo.get(this.resizeEl, true);
36281     }else{
36282         this.resizeEl = this.el;
36283     }
36284     // handle view.xtype
36285     
36286  
36287     
36288     
36289     this.addEvents({
36290         /**
36291          * @event activate
36292          * Fires when this panel is activated. 
36293          * @param {Roo.ContentPanel} this
36294          */
36295         "activate" : true,
36296         /**
36297          * @event deactivate
36298          * Fires when this panel is activated. 
36299          * @param {Roo.ContentPanel} this
36300          */
36301         "deactivate" : true,
36302
36303         /**
36304          * @event resize
36305          * Fires when this panel is resized if fitToFrame is true.
36306          * @param {Roo.ContentPanel} this
36307          * @param {Number} width The width after any component adjustments
36308          * @param {Number} height The height after any component adjustments
36309          */
36310         "resize" : true,
36311         
36312          /**
36313          * @event render
36314          * Fires when this tab is created
36315          * @param {Roo.ContentPanel} this
36316          */
36317         "render" : true
36318         
36319         
36320         
36321     });
36322     
36323
36324     
36325     
36326     if(this.autoScroll){
36327         this.resizeEl.setStyle("overflow", "auto");
36328     } else {
36329         // fix randome scrolling
36330         //this.el.on('scroll', function() {
36331         //    Roo.log('fix random scolling');
36332         //    this.scrollTo('top',0); 
36333         //});
36334     }
36335     content = content || this.content;
36336     if(content){
36337         this.setContent(content);
36338     }
36339     if(config && config.url){
36340         this.setUrl(this.url, this.params, this.loadOnce);
36341     }
36342     
36343     
36344     
36345     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36346     
36347     if (this.view && typeof(this.view.xtype) != 'undefined') {
36348         this.view.el = this.el.appendChild(document.createElement("div"));
36349         this.view = Roo.factory(this.view); 
36350         this.view.render  &&  this.view.render(false, '');  
36351     }
36352     
36353     
36354     this.fireEvent('render', this);
36355 };
36356
36357 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36358     
36359     tabTip : '',
36360     
36361     setRegion : function(region){
36362         this.region = region;
36363         this.setActiveClass(region && !this.background);
36364     },
36365     
36366     
36367     setActiveClass: function(state)
36368     {
36369         if(state){
36370            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36371            this.el.setStyle('position','relative');
36372         }else{
36373            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36374            this.el.setStyle('position', 'absolute');
36375         } 
36376     },
36377     
36378     /**
36379      * Returns the toolbar for this Panel if one was configured. 
36380      * @return {Roo.Toolbar} 
36381      */
36382     getToolbar : function(){
36383         return this.toolbar;
36384     },
36385     
36386     setActiveState : function(active)
36387     {
36388         this.active = active;
36389         this.setActiveClass(active);
36390         if(!active){
36391             this.fireEvent("deactivate", this);
36392         }else{
36393             this.fireEvent("activate", this);
36394         }
36395     },
36396     /**
36397      * Updates this panel's element
36398      * @param {String} content The new content
36399      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36400     */
36401     setContent : function(content, loadScripts){
36402         this.el.update(content, loadScripts);
36403     },
36404
36405     ignoreResize : function(w, h){
36406         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36407             return true;
36408         }else{
36409             this.lastSize = {width: w, height: h};
36410             return false;
36411         }
36412     },
36413     /**
36414      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36415      * @return {Roo.UpdateManager} The UpdateManager
36416      */
36417     getUpdateManager : function(){
36418         return this.el.getUpdateManager();
36419     },
36420      /**
36421      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36422      * @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:
36423 <pre><code>
36424 panel.load({
36425     url: "your-url.php",
36426     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36427     callback: yourFunction,
36428     scope: yourObject, //(optional scope)
36429     discardUrl: false,
36430     nocache: false,
36431     text: "Loading...",
36432     timeout: 30,
36433     scripts: false
36434 });
36435 </code></pre>
36436      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36437      * 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.
36438      * @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}
36439      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36440      * @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.
36441      * @return {Roo.ContentPanel} this
36442      */
36443     load : function(){
36444         var um = this.el.getUpdateManager();
36445         um.update.apply(um, arguments);
36446         return this;
36447     },
36448
36449
36450     /**
36451      * 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.
36452      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36453      * @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)
36454      * @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)
36455      * @return {Roo.UpdateManager} The UpdateManager
36456      */
36457     setUrl : function(url, params, loadOnce){
36458         if(this.refreshDelegate){
36459             this.removeListener("activate", this.refreshDelegate);
36460         }
36461         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36462         this.on("activate", this.refreshDelegate);
36463         return this.el.getUpdateManager();
36464     },
36465     
36466     _handleRefresh : function(url, params, loadOnce){
36467         if(!loadOnce || !this.loaded){
36468             var updater = this.el.getUpdateManager();
36469             updater.update(url, params, this._setLoaded.createDelegate(this));
36470         }
36471     },
36472     
36473     _setLoaded : function(){
36474         this.loaded = true;
36475     }, 
36476     
36477     /**
36478      * Returns this panel's id
36479      * @return {String} 
36480      */
36481     getId : function(){
36482         return this.el.id;
36483     },
36484     
36485     /** 
36486      * Returns this panel's element - used by regiosn to add.
36487      * @return {Roo.Element} 
36488      */
36489     getEl : function(){
36490         return this.wrapEl || this.el;
36491     },
36492     
36493    
36494     
36495     adjustForComponents : function(width, height)
36496     {
36497         //Roo.log('adjustForComponents ');
36498         if(this.resizeEl != this.el){
36499             width -= this.el.getFrameWidth('lr');
36500             height -= this.el.getFrameWidth('tb');
36501         }
36502         if(this.toolbar){
36503             var te = this.toolbar.getEl();
36504             te.setWidth(width);
36505             height -= te.getHeight();
36506         }
36507         if(this.footer){
36508             var te = this.footer.getEl();
36509             te.setWidth(width);
36510             height -= te.getHeight();
36511         }
36512         
36513         
36514         if(this.adjustments){
36515             width += this.adjustments[0];
36516             height += this.adjustments[1];
36517         }
36518         return {"width": width, "height": height};
36519     },
36520     
36521     setSize : function(width, height){
36522         if(this.fitToFrame && !this.ignoreResize(width, height)){
36523             if(this.fitContainer && this.resizeEl != this.el){
36524                 this.el.setSize(width, height);
36525             }
36526             var size = this.adjustForComponents(width, height);
36527             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36528             this.fireEvent('resize', this, size.width, size.height);
36529         }
36530     },
36531     
36532     /**
36533      * Returns this panel's title
36534      * @return {String} 
36535      */
36536     getTitle : function(){
36537         
36538         if (typeof(this.title) != 'object') {
36539             return this.title;
36540         }
36541         
36542         var t = '';
36543         for (var k in this.title) {
36544             if (!this.title.hasOwnProperty(k)) {
36545                 continue;
36546             }
36547             
36548             if (k.indexOf('-') >= 0) {
36549                 var s = k.split('-');
36550                 for (var i = 0; i<s.length; i++) {
36551                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36552                 }
36553             } else {
36554                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36555             }
36556         }
36557         return t;
36558     },
36559     
36560     /**
36561      * Set this panel's title
36562      * @param {String} title
36563      */
36564     setTitle : function(title){
36565         this.title = title;
36566         if(this.region){
36567             this.region.updatePanelTitle(this, title);
36568         }
36569     },
36570     
36571     /**
36572      * Returns true is this panel was configured to be closable
36573      * @return {Boolean} 
36574      */
36575     isClosable : function(){
36576         return this.closable;
36577     },
36578     
36579     beforeSlide : function(){
36580         this.el.clip();
36581         this.resizeEl.clip();
36582     },
36583     
36584     afterSlide : function(){
36585         this.el.unclip();
36586         this.resizeEl.unclip();
36587     },
36588     
36589     /**
36590      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36591      *   Will fail silently if the {@link #setUrl} method has not been called.
36592      *   This does not activate the panel, just updates its content.
36593      */
36594     refresh : function(){
36595         if(this.refreshDelegate){
36596            this.loaded = false;
36597            this.refreshDelegate();
36598         }
36599     },
36600     
36601     /**
36602      * Destroys this panel
36603      */
36604     destroy : function(){
36605         this.el.removeAllListeners();
36606         var tempEl = document.createElement("span");
36607         tempEl.appendChild(this.el.dom);
36608         tempEl.innerHTML = "";
36609         this.el.remove();
36610         this.el = null;
36611     },
36612     
36613     /**
36614      * form - if the content panel contains a form - this is a reference to it.
36615      * @type {Roo.form.Form}
36616      */
36617     form : false,
36618     /**
36619      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36620      *    This contains a reference to it.
36621      * @type {Roo.View}
36622      */
36623     view : false,
36624     
36625       /**
36626      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36627      * <pre><code>
36628
36629 layout.addxtype({
36630        xtype : 'Form',
36631        items: [ .... ]
36632    }
36633 );
36634
36635 </code></pre>
36636      * @param {Object} cfg Xtype definition of item to add.
36637      */
36638     
36639     
36640     getChildContainer: function () {
36641         return this.getEl();
36642     }
36643     
36644     
36645     /*
36646         var  ret = new Roo.factory(cfg);
36647         return ret;
36648         
36649         
36650         // add form..
36651         if (cfg.xtype.match(/^Form$/)) {
36652             
36653             var el;
36654             //if (this.footer) {
36655             //    el = this.footer.container.insertSibling(false, 'before');
36656             //} else {
36657                 el = this.el.createChild();
36658             //}
36659
36660             this.form = new  Roo.form.Form(cfg);
36661             
36662             
36663             if ( this.form.allItems.length) {
36664                 this.form.render(el.dom);
36665             }
36666             return this.form;
36667         }
36668         // should only have one of theses..
36669         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36670             // views.. should not be just added - used named prop 'view''
36671             
36672             cfg.el = this.el.appendChild(document.createElement("div"));
36673             // factory?
36674             
36675             var ret = new Roo.factory(cfg);
36676              
36677              ret.render && ret.render(false, ''); // render blank..
36678             this.view = ret;
36679             return ret;
36680         }
36681         return false;
36682     }
36683     \*/
36684 });
36685  
36686 /**
36687  * @class Roo.bootstrap.panel.Grid
36688  * @extends Roo.bootstrap.panel.Content
36689  * @constructor
36690  * Create a new GridPanel.
36691  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36692  * @param {Object} config A the config object
36693   
36694  */
36695
36696
36697
36698 Roo.bootstrap.panel.Grid = function(config)
36699 {
36700     
36701       
36702     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36703         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36704
36705     config.el = this.wrapper;
36706     //this.el = this.wrapper;
36707     
36708       if (config.container) {
36709         // ctor'ed from a Border/panel.grid
36710         
36711         
36712         this.wrapper.setStyle("overflow", "hidden");
36713         this.wrapper.addClass('roo-grid-container');
36714
36715     }
36716     
36717     
36718     if(config.toolbar){
36719         var tool_el = this.wrapper.createChild();    
36720         this.toolbar = Roo.factory(config.toolbar);
36721         var ti = [];
36722         if (config.toolbar.items) {
36723             ti = config.toolbar.items ;
36724             delete config.toolbar.items ;
36725         }
36726         
36727         var nitems = [];
36728         this.toolbar.render(tool_el);
36729         for(var i =0;i < ti.length;i++) {
36730           //  Roo.log(['add child', items[i]]);
36731             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36732         }
36733         this.toolbar.items = nitems;
36734         
36735         delete config.toolbar;
36736     }
36737     
36738     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36739     config.grid.scrollBody = true;;
36740     config.grid.monitorWindowResize = false; // turn off autosizing
36741     config.grid.autoHeight = false;
36742     config.grid.autoWidth = false;
36743     
36744     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36745     
36746     if (config.background) {
36747         // render grid on panel activation (if panel background)
36748         this.on('activate', function(gp) {
36749             if (!gp.grid.rendered) {
36750                 gp.grid.render(this.wrapper);
36751                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36752             }
36753         });
36754             
36755     } else {
36756         this.grid.render(this.wrapper);
36757         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36758
36759     }
36760     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36761     // ??? needed ??? config.el = this.wrapper;
36762     
36763     
36764     
36765   
36766     // xtype created footer. - not sure if will work as we normally have to render first..
36767     if (this.footer && !this.footer.el && this.footer.xtype) {
36768         
36769         var ctr = this.grid.getView().getFooterPanel(true);
36770         this.footer.dataSource = this.grid.dataSource;
36771         this.footer = Roo.factory(this.footer, Roo);
36772         this.footer.render(ctr);
36773         
36774     }
36775     
36776     
36777     
36778     
36779      
36780 };
36781
36782 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36783     getId : function(){
36784         return this.grid.id;
36785     },
36786     
36787     /**
36788      * Returns the grid for this panel
36789      * @return {Roo.bootstrap.Table} 
36790      */
36791     getGrid : function(){
36792         return this.grid;    
36793     },
36794     
36795     setSize : function(width, height){
36796         if(!this.ignoreResize(width, height)){
36797             var grid = this.grid;
36798             var size = this.adjustForComponents(width, height);
36799             var gridel = grid.getGridEl();
36800             gridel.setSize(size.width, size.height);
36801             /*
36802             var thd = grid.getGridEl().select('thead',true).first();
36803             var tbd = grid.getGridEl().select('tbody', true).first();
36804             if (tbd) {
36805                 tbd.setSize(width, height - thd.getHeight());
36806             }
36807             */
36808             grid.autoSize();
36809         }
36810     },
36811      
36812     
36813     
36814     beforeSlide : function(){
36815         this.grid.getView().scroller.clip();
36816     },
36817     
36818     afterSlide : function(){
36819         this.grid.getView().scroller.unclip();
36820     },
36821     
36822     destroy : function(){
36823         this.grid.destroy();
36824         delete this.grid;
36825         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36826     }
36827 });
36828
36829 /**
36830  * @class Roo.bootstrap.panel.Nest
36831  * @extends Roo.bootstrap.panel.Content
36832  * @constructor
36833  * Create a new Panel, that can contain a layout.Border.
36834  * 
36835  * 
36836  * @param {Roo.BorderLayout} layout The layout for this panel
36837  * @param {String/Object} config A string to set only the title or a config object
36838  */
36839 Roo.bootstrap.panel.Nest = function(config)
36840 {
36841     // construct with only one argument..
36842     /* FIXME - implement nicer consturctors
36843     if (layout.layout) {
36844         config = layout;
36845         layout = config.layout;
36846         delete config.layout;
36847     }
36848     if (layout.xtype && !layout.getEl) {
36849         // then layout needs constructing..
36850         layout = Roo.factory(layout, Roo);
36851     }
36852     */
36853     
36854     config.el =  config.layout.getEl();
36855     
36856     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36857     
36858     config.layout.monitorWindowResize = false; // turn off autosizing
36859     this.layout = config.layout;
36860     this.layout.getEl().addClass("roo-layout-nested-layout");
36861     
36862     
36863     
36864     
36865 };
36866
36867 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36868
36869     setSize : function(width, height){
36870         if(!this.ignoreResize(width, height)){
36871             var size = this.adjustForComponents(width, height);
36872             var el = this.layout.getEl();
36873             if (size.height < 1) {
36874                 el.setWidth(size.width);   
36875             } else {
36876                 el.setSize(size.width, size.height);
36877             }
36878             var touch = el.dom.offsetWidth;
36879             this.layout.layout();
36880             // ie requires a double layout on the first pass
36881             if(Roo.isIE && !this.initialized){
36882                 this.initialized = true;
36883                 this.layout.layout();
36884             }
36885         }
36886     },
36887     
36888     // activate all subpanels if not currently active..
36889     
36890     setActiveState : function(active){
36891         this.active = active;
36892         this.setActiveClass(active);
36893         
36894         if(!active){
36895             this.fireEvent("deactivate", this);
36896             return;
36897         }
36898         
36899         this.fireEvent("activate", this);
36900         // not sure if this should happen before or after..
36901         if (!this.layout) {
36902             return; // should not happen..
36903         }
36904         var reg = false;
36905         for (var r in this.layout.regions) {
36906             reg = this.layout.getRegion(r);
36907             if (reg.getActivePanel()) {
36908                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36909                 reg.setActivePanel(reg.getActivePanel());
36910                 continue;
36911             }
36912             if (!reg.panels.length) {
36913                 continue;
36914             }
36915             reg.showPanel(reg.getPanel(0));
36916         }
36917         
36918         
36919         
36920         
36921     },
36922     
36923     /**
36924      * Returns the nested BorderLayout for this panel
36925      * @return {Roo.BorderLayout} 
36926      */
36927     getLayout : function(){
36928         return this.layout;
36929     },
36930     
36931      /**
36932      * Adds a xtype elements to the layout of the nested panel
36933      * <pre><code>
36934
36935 panel.addxtype({
36936        xtype : 'ContentPanel',
36937        region: 'west',
36938        items: [ .... ]
36939    }
36940 );
36941
36942 panel.addxtype({
36943         xtype : 'NestedLayoutPanel',
36944         region: 'west',
36945         layout: {
36946            center: { },
36947            west: { }   
36948         },
36949         items : [ ... list of content panels or nested layout panels.. ]
36950    }
36951 );
36952 </code></pre>
36953      * @param {Object} cfg Xtype definition of item to add.
36954      */
36955     addxtype : function(cfg) {
36956         return this.layout.addxtype(cfg);
36957     
36958     }
36959 });        /*
36960  * Based on:
36961  * Ext JS Library 1.1.1
36962  * Copyright(c) 2006-2007, Ext JS, LLC.
36963  *
36964  * Originally Released Under LGPL - original licence link has changed is not relivant.
36965  *
36966  * Fork - LGPL
36967  * <script type="text/javascript">
36968  */
36969 /**
36970  * @class Roo.TabPanel
36971  * @extends Roo.util.Observable
36972  * A lightweight tab container.
36973  * <br><br>
36974  * Usage:
36975  * <pre><code>
36976 // basic tabs 1, built from existing content
36977 var tabs = new Roo.TabPanel("tabs1");
36978 tabs.addTab("script", "View Script");
36979 tabs.addTab("markup", "View Markup");
36980 tabs.activate("script");
36981
36982 // more advanced tabs, built from javascript
36983 var jtabs = new Roo.TabPanel("jtabs");
36984 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36985
36986 // set up the UpdateManager
36987 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36988 var updater = tab2.getUpdateManager();
36989 updater.setDefaultUrl("ajax1.htm");
36990 tab2.on('activate', updater.refresh, updater, true);
36991
36992 // Use setUrl for Ajax loading
36993 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36994 tab3.setUrl("ajax2.htm", null, true);
36995
36996 // Disabled tab
36997 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36998 tab4.disable();
36999
37000 jtabs.activate("jtabs-1");
37001  * </code></pre>
37002  * @constructor
37003  * Create a new TabPanel.
37004  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37005  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37006  */
37007 Roo.bootstrap.panel.Tabs = function(config){
37008     /**
37009     * The container element for this TabPanel.
37010     * @type Roo.Element
37011     */
37012     this.el = Roo.get(config.el);
37013     delete config.el;
37014     if(config){
37015         if(typeof config == "boolean"){
37016             this.tabPosition = config ? "bottom" : "top";
37017         }else{
37018             Roo.apply(this, config);
37019         }
37020     }
37021     
37022     if(this.tabPosition == "bottom"){
37023         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37024         this.el.addClass("roo-tabs-bottom");
37025     }
37026     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37027     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37028     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37029     if(Roo.isIE){
37030         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37031     }
37032     if(this.tabPosition != "bottom"){
37033         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37034          * @type Roo.Element
37035          */
37036         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37037         this.el.addClass("roo-tabs-top");
37038     }
37039     this.items = [];
37040
37041     this.bodyEl.setStyle("position", "relative");
37042
37043     this.active = null;
37044     this.activateDelegate = this.activate.createDelegate(this);
37045
37046     this.addEvents({
37047         /**
37048          * @event tabchange
37049          * Fires when the active tab changes
37050          * @param {Roo.TabPanel} this
37051          * @param {Roo.TabPanelItem} activePanel The new active tab
37052          */
37053         "tabchange": true,
37054         /**
37055          * @event beforetabchange
37056          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37057          * @param {Roo.TabPanel} this
37058          * @param {Object} e Set cancel to true on this object to cancel the tab change
37059          * @param {Roo.TabPanelItem} tab The tab being changed to
37060          */
37061         "beforetabchange" : true
37062     });
37063
37064     Roo.EventManager.onWindowResize(this.onResize, this);
37065     this.cpad = this.el.getPadding("lr");
37066     this.hiddenCount = 0;
37067
37068
37069     // toolbar on the tabbar support...
37070     if (this.toolbar) {
37071         alert("no toolbar support yet");
37072         this.toolbar  = false;
37073         /*
37074         var tcfg = this.toolbar;
37075         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37076         this.toolbar = new Roo.Toolbar(tcfg);
37077         if (Roo.isSafari) {
37078             var tbl = tcfg.container.child('table', true);
37079             tbl.setAttribute('width', '100%');
37080         }
37081         */
37082         
37083     }
37084    
37085
37086
37087     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37088 };
37089
37090 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37091     /*
37092      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37093      */
37094     tabPosition : "top",
37095     /*
37096      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37097      */
37098     currentTabWidth : 0,
37099     /*
37100      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37101      */
37102     minTabWidth : 40,
37103     /*
37104      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37105      */
37106     maxTabWidth : 250,
37107     /*
37108      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37109      */
37110     preferredTabWidth : 175,
37111     /*
37112      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37113      */
37114     resizeTabs : false,
37115     /*
37116      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37117      */
37118     monitorResize : true,
37119     /*
37120      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37121      */
37122     toolbar : false,
37123
37124     /**
37125      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37126      * @param {String} id The id of the div to use <b>or create</b>
37127      * @param {String} text The text for the tab
37128      * @param {String} content (optional) Content to put in the TabPanelItem body
37129      * @param {Boolean} closable (optional) True to create a close icon on the tab
37130      * @return {Roo.TabPanelItem} The created TabPanelItem
37131      */
37132     addTab : function(id, text, content, closable, tpl)
37133     {
37134         var item = new Roo.bootstrap.panel.TabItem({
37135             panel: this,
37136             id : id,
37137             text : text,
37138             closable : closable,
37139             tpl : tpl
37140         });
37141         this.addTabItem(item);
37142         if(content){
37143             item.setContent(content);
37144         }
37145         return item;
37146     },
37147
37148     /**
37149      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37150      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37151      * @return {Roo.TabPanelItem}
37152      */
37153     getTab : function(id){
37154         return this.items[id];
37155     },
37156
37157     /**
37158      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37159      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37160      */
37161     hideTab : function(id){
37162         var t = this.items[id];
37163         if(!t.isHidden()){
37164            t.setHidden(true);
37165            this.hiddenCount++;
37166            this.autoSizeTabs();
37167         }
37168     },
37169
37170     /**
37171      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37172      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37173      */
37174     unhideTab : function(id){
37175         var t = this.items[id];
37176         if(t.isHidden()){
37177            t.setHidden(false);
37178            this.hiddenCount--;
37179            this.autoSizeTabs();
37180         }
37181     },
37182
37183     /**
37184      * Adds an existing {@link Roo.TabPanelItem}.
37185      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37186      */
37187     addTabItem : function(item){
37188         this.items[item.id] = item;
37189         this.items.push(item);
37190       //  if(this.resizeTabs){
37191     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37192   //         this.autoSizeTabs();
37193 //        }else{
37194 //            item.autoSize();
37195        // }
37196     },
37197
37198     /**
37199      * Removes a {@link Roo.TabPanelItem}.
37200      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37201      */
37202     removeTab : function(id){
37203         var items = this.items;
37204         var tab = items[id];
37205         if(!tab) { return; }
37206         var index = items.indexOf(tab);
37207         if(this.active == tab && items.length > 1){
37208             var newTab = this.getNextAvailable(index);
37209             if(newTab) {
37210                 newTab.activate();
37211             }
37212         }
37213         this.stripEl.dom.removeChild(tab.pnode.dom);
37214         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37215             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37216         }
37217         items.splice(index, 1);
37218         delete this.items[tab.id];
37219         tab.fireEvent("close", tab);
37220         tab.purgeListeners();
37221         this.autoSizeTabs();
37222     },
37223
37224     getNextAvailable : function(start){
37225         var items = this.items;
37226         var index = start;
37227         // look for a next tab that will slide over to
37228         // replace the one being removed
37229         while(index < items.length){
37230             var item = items[++index];
37231             if(item && !item.isHidden()){
37232                 return item;
37233             }
37234         }
37235         // if one isn't found select the previous tab (on the left)
37236         index = start;
37237         while(index >= 0){
37238             var item = items[--index];
37239             if(item && !item.isHidden()){
37240                 return item;
37241             }
37242         }
37243         return null;
37244     },
37245
37246     /**
37247      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37248      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37249      */
37250     disableTab : function(id){
37251         var tab = this.items[id];
37252         if(tab && this.active != tab){
37253             tab.disable();
37254         }
37255     },
37256
37257     /**
37258      * Enables a {@link Roo.TabPanelItem} that is disabled.
37259      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37260      */
37261     enableTab : function(id){
37262         var tab = this.items[id];
37263         tab.enable();
37264     },
37265
37266     /**
37267      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37268      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37269      * @return {Roo.TabPanelItem} The TabPanelItem.
37270      */
37271     activate : function(id){
37272         var tab = this.items[id];
37273         if(!tab){
37274             return null;
37275         }
37276         if(tab == this.active || tab.disabled){
37277             return tab;
37278         }
37279         var e = {};
37280         this.fireEvent("beforetabchange", this, e, tab);
37281         if(e.cancel !== true && !tab.disabled){
37282             if(this.active){
37283                 this.active.hide();
37284             }
37285             this.active = this.items[id];
37286             this.active.show();
37287             this.fireEvent("tabchange", this, this.active);
37288         }
37289         return tab;
37290     },
37291
37292     /**
37293      * Gets the active {@link Roo.TabPanelItem}.
37294      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37295      */
37296     getActiveTab : function(){
37297         return this.active;
37298     },
37299
37300     /**
37301      * Updates the tab body element to fit the height of the container element
37302      * for overflow scrolling
37303      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37304      */
37305     syncHeight : function(targetHeight){
37306         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37307         var bm = this.bodyEl.getMargins();
37308         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37309         this.bodyEl.setHeight(newHeight);
37310         return newHeight;
37311     },
37312
37313     onResize : function(){
37314         if(this.monitorResize){
37315             this.autoSizeTabs();
37316         }
37317     },
37318
37319     /**
37320      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37321      */
37322     beginUpdate : function(){
37323         this.updating = true;
37324     },
37325
37326     /**
37327      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37328      */
37329     endUpdate : function(){
37330         this.updating = false;
37331         this.autoSizeTabs();
37332     },
37333
37334     /**
37335      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37336      */
37337     autoSizeTabs : function(){
37338         var count = this.items.length;
37339         var vcount = count - this.hiddenCount;
37340         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37341             return;
37342         }
37343         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37344         var availWidth = Math.floor(w / vcount);
37345         var b = this.stripBody;
37346         if(b.getWidth() > w){
37347             var tabs = this.items;
37348             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37349             if(availWidth < this.minTabWidth){
37350                 /*if(!this.sleft){    // incomplete scrolling code
37351                     this.createScrollButtons();
37352                 }
37353                 this.showScroll();
37354                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37355             }
37356         }else{
37357             if(this.currentTabWidth < this.preferredTabWidth){
37358                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37359             }
37360         }
37361     },
37362
37363     /**
37364      * Returns the number of tabs in this TabPanel.
37365      * @return {Number}
37366      */
37367      getCount : function(){
37368          return this.items.length;
37369      },
37370
37371     /**
37372      * Resizes all the tabs to the passed width
37373      * @param {Number} The new width
37374      */
37375     setTabWidth : function(width){
37376         this.currentTabWidth = width;
37377         for(var i = 0, len = this.items.length; i < len; i++) {
37378                 if(!this.items[i].isHidden()) {
37379                 this.items[i].setWidth(width);
37380             }
37381         }
37382     },
37383
37384     /**
37385      * Destroys this TabPanel
37386      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37387      */
37388     destroy : function(removeEl){
37389         Roo.EventManager.removeResizeListener(this.onResize, this);
37390         for(var i = 0, len = this.items.length; i < len; i++){
37391             this.items[i].purgeListeners();
37392         }
37393         if(removeEl === true){
37394             this.el.update("");
37395             this.el.remove();
37396         }
37397     },
37398     
37399     createStrip : function(container)
37400     {
37401         var strip = document.createElement("nav");
37402         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37403         container.appendChild(strip);
37404         return strip;
37405     },
37406     
37407     createStripList : function(strip)
37408     {
37409         // div wrapper for retard IE
37410         // returns the "tr" element.
37411         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37412         //'<div class="x-tabs-strip-wrap">'+
37413           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37414           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37415         return strip.firstChild; //.firstChild.firstChild.firstChild;
37416     },
37417     createBody : function(container)
37418     {
37419         var body = document.createElement("div");
37420         Roo.id(body, "tab-body");
37421         //Roo.fly(body).addClass("x-tabs-body");
37422         Roo.fly(body).addClass("tab-content");
37423         container.appendChild(body);
37424         return body;
37425     },
37426     createItemBody :function(bodyEl, id){
37427         var body = Roo.getDom(id);
37428         if(!body){
37429             body = document.createElement("div");
37430             body.id = id;
37431         }
37432         //Roo.fly(body).addClass("x-tabs-item-body");
37433         Roo.fly(body).addClass("tab-pane");
37434          bodyEl.insertBefore(body, bodyEl.firstChild);
37435         return body;
37436     },
37437     /** @private */
37438     createStripElements :  function(stripEl, text, closable, tpl)
37439     {
37440         var td = document.createElement("li"); // was td..
37441         
37442         
37443         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37444         
37445         
37446         stripEl.appendChild(td);
37447         /*if(closable){
37448             td.className = "x-tabs-closable";
37449             if(!this.closeTpl){
37450                 this.closeTpl = new Roo.Template(
37451                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37452                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37453                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37454                 );
37455             }
37456             var el = this.closeTpl.overwrite(td, {"text": text});
37457             var close = el.getElementsByTagName("div")[0];
37458             var inner = el.getElementsByTagName("em")[0];
37459             return {"el": el, "close": close, "inner": inner};
37460         } else {
37461         */
37462         // not sure what this is..
37463 //            if(!this.tabTpl){
37464                 //this.tabTpl = new Roo.Template(
37465                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37466                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37467                 //);
37468 //                this.tabTpl = new Roo.Template(
37469 //                   '<a href="#">' +
37470 //                   '<span unselectable="on"' +
37471 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37472 //                            ' >{text}</span></a>'
37473 //                );
37474 //                
37475 //            }
37476
37477
37478             var template = tpl || this.tabTpl || false;
37479             
37480             if(!template){
37481                 
37482                 template = new Roo.Template(
37483                    '<a href="#">' +
37484                    '<span unselectable="on"' +
37485                             (this.disableTooltips ? '' : ' title="{text}"') +
37486                             ' >{text}</span></a>'
37487                 );
37488             }
37489             
37490             switch (typeof(template)) {
37491                 case 'object' :
37492                     break;
37493                 case 'string' :
37494                     template = new Roo.Template(template);
37495                     break;
37496                 default :
37497                     break;
37498             }
37499             
37500             var el = template.overwrite(td, {"text": text});
37501             
37502             var inner = el.getElementsByTagName("span")[0];
37503             
37504             return {"el": el, "inner": inner};
37505             
37506     }
37507         
37508     
37509 });
37510
37511 /**
37512  * @class Roo.TabPanelItem
37513  * @extends Roo.util.Observable
37514  * Represents an individual item (tab plus body) in a TabPanel.
37515  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37516  * @param {String} id The id of this TabPanelItem
37517  * @param {String} text The text for the tab of this TabPanelItem
37518  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37519  */
37520 Roo.bootstrap.panel.TabItem = function(config){
37521     /**
37522      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37523      * @type Roo.TabPanel
37524      */
37525     this.tabPanel = config.panel;
37526     /**
37527      * The id for this TabPanelItem
37528      * @type String
37529      */
37530     this.id = config.id;
37531     /** @private */
37532     this.disabled = false;
37533     /** @private */
37534     this.text = config.text;
37535     /** @private */
37536     this.loaded = false;
37537     this.closable = config.closable;
37538
37539     /**
37540      * The body element for this TabPanelItem.
37541      * @type Roo.Element
37542      */
37543     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37544     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37545     this.bodyEl.setStyle("display", "block");
37546     this.bodyEl.setStyle("zoom", "1");
37547     //this.hideAction();
37548
37549     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37550     /** @private */
37551     this.el = Roo.get(els.el);
37552     this.inner = Roo.get(els.inner, true);
37553     this.textEl = Roo.get(this.el.dom.firstChild, true);
37554     this.pnode = Roo.get(els.el.parentNode, true);
37555     this.el.on("mousedown", this.onTabMouseDown, this);
37556     this.el.on("click", this.onTabClick, this);
37557     /** @private */
37558     if(config.closable){
37559         var c = Roo.get(els.close, true);
37560         c.dom.title = this.closeText;
37561         c.addClassOnOver("close-over");
37562         c.on("click", this.closeClick, this);
37563      }
37564
37565     this.addEvents({
37566          /**
37567          * @event activate
37568          * Fires when this tab becomes the active tab.
37569          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37570          * @param {Roo.TabPanelItem} this
37571          */
37572         "activate": true,
37573         /**
37574          * @event beforeclose
37575          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37576          * @param {Roo.TabPanelItem} this
37577          * @param {Object} e Set cancel to true on this object to cancel the close.
37578          */
37579         "beforeclose": true,
37580         /**
37581          * @event close
37582          * Fires when this tab is closed.
37583          * @param {Roo.TabPanelItem} this
37584          */
37585          "close": true,
37586         /**
37587          * @event deactivate
37588          * Fires when this tab is no longer the active tab.
37589          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37590          * @param {Roo.TabPanelItem} this
37591          */
37592          "deactivate" : true
37593     });
37594     this.hidden = false;
37595
37596     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37597 };
37598
37599 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37600            {
37601     purgeListeners : function(){
37602        Roo.util.Observable.prototype.purgeListeners.call(this);
37603        this.el.removeAllListeners();
37604     },
37605     /**
37606      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37607      */
37608     show : function(){
37609         this.pnode.addClass("active");
37610         this.showAction();
37611         if(Roo.isOpera){
37612             this.tabPanel.stripWrap.repaint();
37613         }
37614         this.fireEvent("activate", this.tabPanel, this);
37615     },
37616
37617     /**
37618      * Returns true if this tab is the active tab.
37619      * @return {Boolean}
37620      */
37621     isActive : function(){
37622         return this.tabPanel.getActiveTab() == this;
37623     },
37624
37625     /**
37626      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37627      */
37628     hide : function(){
37629         this.pnode.removeClass("active");
37630         this.hideAction();
37631         this.fireEvent("deactivate", this.tabPanel, this);
37632     },
37633
37634     hideAction : function(){
37635         this.bodyEl.hide();
37636         this.bodyEl.setStyle("position", "absolute");
37637         this.bodyEl.setLeft("-20000px");
37638         this.bodyEl.setTop("-20000px");
37639     },
37640
37641     showAction : function(){
37642         this.bodyEl.setStyle("position", "relative");
37643         this.bodyEl.setTop("");
37644         this.bodyEl.setLeft("");
37645         this.bodyEl.show();
37646     },
37647
37648     /**
37649      * Set the tooltip for the tab.
37650      * @param {String} tooltip The tab's tooltip
37651      */
37652     setTooltip : function(text){
37653         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37654             this.textEl.dom.qtip = text;
37655             this.textEl.dom.removeAttribute('title');
37656         }else{
37657             this.textEl.dom.title = text;
37658         }
37659     },
37660
37661     onTabClick : function(e){
37662         e.preventDefault();
37663         this.tabPanel.activate(this.id);
37664     },
37665
37666     onTabMouseDown : function(e){
37667         e.preventDefault();
37668         this.tabPanel.activate(this.id);
37669     },
37670 /*
37671     getWidth : function(){
37672         return this.inner.getWidth();
37673     },
37674
37675     setWidth : function(width){
37676         var iwidth = width - this.pnode.getPadding("lr");
37677         this.inner.setWidth(iwidth);
37678         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37679         this.pnode.setWidth(width);
37680     },
37681 */
37682     /**
37683      * Show or hide the tab
37684      * @param {Boolean} hidden True to hide or false to show.
37685      */
37686     setHidden : function(hidden){
37687         this.hidden = hidden;
37688         this.pnode.setStyle("display", hidden ? "none" : "");
37689     },
37690
37691     /**
37692      * Returns true if this tab is "hidden"
37693      * @return {Boolean}
37694      */
37695     isHidden : function(){
37696         return this.hidden;
37697     },
37698
37699     /**
37700      * Returns the text for this tab
37701      * @return {String}
37702      */
37703     getText : function(){
37704         return this.text;
37705     },
37706     /*
37707     autoSize : function(){
37708         //this.el.beginMeasure();
37709         this.textEl.setWidth(1);
37710         /*
37711          *  #2804 [new] Tabs in Roojs
37712          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37713          */
37714         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37715         //this.el.endMeasure();
37716     //},
37717
37718     /**
37719      * Sets the text for the tab (Note: this also sets the tooltip text)
37720      * @param {String} text The tab's text and tooltip
37721      */
37722     setText : function(text){
37723         this.text = text;
37724         this.textEl.update(text);
37725         this.setTooltip(text);
37726         //if(!this.tabPanel.resizeTabs){
37727         //    this.autoSize();
37728         //}
37729     },
37730     /**
37731      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37732      */
37733     activate : function(){
37734         this.tabPanel.activate(this.id);
37735     },
37736
37737     /**
37738      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37739      */
37740     disable : function(){
37741         if(this.tabPanel.active != this){
37742             this.disabled = true;
37743             this.pnode.addClass("disabled");
37744         }
37745     },
37746
37747     /**
37748      * Enables this TabPanelItem if it was previously disabled.
37749      */
37750     enable : function(){
37751         this.disabled = false;
37752         this.pnode.removeClass("disabled");
37753     },
37754
37755     /**
37756      * Sets the content for this TabPanelItem.
37757      * @param {String} content The content
37758      * @param {Boolean} loadScripts true to look for and load scripts
37759      */
37760     setContent : function(content, loadScripts){
37761         this.bodyEl.update(content, loadScripts);
37762     },
37763
37764     /**
37765      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37766      * @return {Roo.UpdateManager} The UpdateManager
37767      */
37768     getUpdateManager : function(){
37769         return this.bodyEl.getUpdateManager();
37770     },
37771
37772     /**
37773      * Set a URL to be used to load the content for this TabPanelItem.
37774      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37775      * @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)
37776      * @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)
37777      * @return {Roo.UpdateManager} The UpdateManager
37778      */
37779     setUrl : function(url, params, loadOnce){
37780         if(this.refreshDelegate){
37781             this.un('activate', this.refreshDelegate);
37782         }
37783         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37784         this.on("activate", this.refreshDelegate);
37785         return this.bodyEl.getUpdateManager();
37786     },
37787
37788     /** @private */
37789     _handleRefresh : function(url, params, loadOnce){
37790         if(!loadOnce || !this.loaded){
37791             var updater = this.bodyEl.getUpdateManager();
37792             updater.update(url, params, this._setLoaded.createDelegate(this));
37793         }
37794     },
37795
37796     /**
37797      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37798      *   Will fail silently if the setUrl method has not been called.
37799      *   This does not activate the panel, just updates its content.
37800      */
37801     refresh : function(){
37802         if(this.refreshDelegate){
37803            this.loaded = false;
37804            this.refreshDelegate();
37805         }
37806     },
37807
37808     /** @private */
37809     _setLoaded : function(){
37810         this.loaded = true;
37811     },
37812
37813     /** @private */
37814     closeClick : function(e){
37815         var o = {};
37816         e.stopEvent();
37817         this.fireEvent("beforeclose", this, o);
37818         if(o.cancel !== true){
37819             this.tabPanel.removeTab(this.id);
37820         }
37821     },
37822     /**
37823      * The text displayed in the tooltip for the close icon.
37824      * @type String
37825      */
37826     closeText : "Close this tab"
37827 });