roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.weightClass = ["btn-default", 
538                        "btn-primary", 
539                        "btn-success", 
540                        "btn-info", 
541                        "btn-warning",
542                        "btn-danger",
543                        "btn-link"
544                       ],  
545     this.addEvents({
546         // raw events
547         /**
548          * @event click
549          * When a butotn is pressed
550          * @param {Roo.bootstrap.Button} this
551          * @param {Roo.EventObject} e
552          */
553         "click" : true,
554          /**
555          * @event toggle
556          * After the button has been toggles
557          * @param {Roo.EventObject} e
558          * @param {boolean} pressed (also available as button.pressed)
559          */
560         "toggle" : true
561     });
562 };
563
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
565     html: false,
566     active: false,
567     weight: '',
568     size: '',
569     tag: 'button',
570     href: '',
571     disabled: false,
572     isClose: false,
573     glyphicon: '',
574     badge: '',
575     theme: 'default',
576     inverse: false,
577     
578     toggle: false,
579     ontext: 'ON',
580     offtext: 'OFF',
581     defaulton: true,
582     preventDefault: true,
583     removeClass: false,
584     name: false,
585     target: false,
586     
587     
588     pressed : null,
589      
590     
591     getAutoCreate : function(){
592         
593         var cfg = {
594             tag : 'button',
595             cls : 'roo-button',
596             html: ''
597         };
598         
599         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
601             this.tag = 'button';
602         } else {
603             cfg.tag = this.tag;
604         }
605         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606         
607         if (this.toggle == true) {
608             cfg={
609                 tag: 'div',
610                 cls: 'slider-frame roo-button',
611                 cn: [
612                     {
613                         tag: 'span',
614                         'data-on-text':'ON',
615                         'data-off-text':'OFF',
616                         cls: 'slider-button',
617                         html: this.offtext
618                     }
619                 ]
620             };
621             
622             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623                 cfg.cls += ' '+this.weight;
624             }
625             
626             return cfg;
627         }
628         
629         if (this.isClose) {
630             cfg.cls += ' close';
631             
632             cfg["aria-hidden"] = true;
633             
634             cfg.html = "&times;";
635             
636             return cfg;
637         }
638         
639          
640         if (this.theme==='default') {
641             cfg.cls = 'btn roo-button';
642             
643             //if (this.parentType != 'Navbar') {
644             this.weight = this.weight.length ?  this.weight : 'default';
645             //}
646             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647                 
648                 cfg.cls += ' btn-' + this.weight;
649             }
650         } else if (this.theme==='glow') {
651             
652             cfg.tag = 'a';
653             cfg.cls = 'btn-glow roo-button';
654             
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' ' + this.weight;
658             }
659         }
660    
661         
662         if (this.inverse) {
663             this.cls += ' inverse';
664         }
665         
666         
667         if (this.active) {
668             cfg.cls += ' active';
669         }
670         
671         if (this.disabled) {
672             cfg.disabled = 'disabled';
673         }
674         
675         if (this.items) {
676             Roo.log('changing to ul' );
677             cfg.tag = 'ul';
678             this.glyphicon = 'caret';
679         }
680         
681         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682          
683         //gsRoo.log(this.parentType);
684         if (this.parentType === 'Navbar' && !this.parent().bar) {
685             Roo.log('changing to li?');
686             
687             cfg.tag = 'li';
688             
689             cfg.cls = '';
690             cfg.cn =  [{
691                 tag : 'a',
692                 cls : 'roo-button',
693                 html : this.html,
694                 href : this.href || '#'
695             }];
696             if (this.menu) {
697                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
698                 cfg.cls += ' dropdown';
699             }   
700             
701             delete cfg.html;
702             
703         }
704         
705        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
706         
707         if (this.glyphicon) {
708             cfg.html = ' ' + cfg.html;
709             
710             cfg.cn = [
711                 {
712                     tag: 'span',
713                     cls: 'glyphicon glyphicon-' + this.glyphicon
714                 }
715             ];
716         }
717         
718         if (this.badge) {
719             cfg.html += ' ';
720             
721             cfg.tag = 'a';
722             
723 //            cfg.cls='btn roo-button';
724             
725             cfg.href=this.href;
726             
727             var value = cfg.html;
728             
729             if(this.glyphicon){
730                 value = {
731                             tag: 'span',
732                             cls: 'glyphicon glyphicon-' + this.glyphicon,
733                             html: this.html
734                         };
735                 
736             }
737             
738             cfg.cn = [
739                 value,
740                 {
741                     tag: 'span',
742                     cls: 'badge',
743                     html: this.badge
744                 }
745             ];
746             
747             cfg.html='';
748         }
749         
750         if (this.menu) {
751             cfg.cls += ' dropdown';
752             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
753         }
754         
755         if (cfg.tag !== 'a' && this.href !== '') {
756             throw "Tag must be a to set href.";
757         } else if (this.href.length > 0) {
758             cfg.href = this.href;
759         }
760         
761         if(this.removeClass){
762             cfg.cls = '';
763         }
764         
765         if(this.target){
766             cfg.target = this.target;
767         }
768         
769         return cfg;
770     },
771     initEvents: function() {
772        // Roo.log('init events?');
773 //        Roo.log(this.el.dom);
774         // add the menu...
775         
776         if (typeof (this.menu) != 'undefined') {
777             this.menu.parentType = this.xtype;
778             this.menu.triggerEl = this.el;
779             this.addxtype(Roo.apply({}, this.menu));
780         }
781
782
783        if (this.el.hasClass('roo-button')) {
784             this.el.on('click', this.onClick, this);
785        } else {
786             this.el.select('.roo-button').on('click', this.onClick, this);
787        }
788        
789        if(this.removeClass){
790            this.el.on('click', this.onClick, this);
791        }
792        
793        this.el.enableDisplayMode();
794         
795     },
796     onClick : function(e)
797     {
798         if (this.disabled) {
799             return;
800         }
801         
802         
803         Roo.log('button on click ');
804         if(this.preventDefault){
805             e.preventDefault();
806         }
807         if (this.pressed === true || this.pressed === false) {
808             this.pressed = !this.pressed;
809             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810             this.fireEvent('toggle', this, e, this.pressed);
811         }
812         
813         
814         this.fireEvent('click', this, e);
815     },
816     
817     /**
818      * Enables this button
819      */
820     enable : function()
821     {
822         this.disabled = false;
823         this.el.removeClass('disabled');
824     },
825     
826     /**
827      * Disable this button
828      */
829     disable : function()
830     {
831         this.disabled = true;
832         this.el.addClass('disabled');
833     },
834      /**
835      * sets the active state on/off, 
836      * @param {Boolean} state (optional) Force a particular state
837      */
838     setActive : function(v) {
839         
840         this.el[v ? 'addClass' : 'removeClass']('active');
841     },
842      /**
843      * toggles the current active state 
844      */
845     toggleActive : function()
846     {
847        var active = this.el.hasClass('active');
848        this.setActive(!active);
849        
850         
851     },
852     setText : function(str)
853     {
854         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
855     },
856     getText : function()
857     {
858         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859     },
860     hide: function() {
861        
862      
863         this.el.hide();   
864     },
865     show: function() {
866        
867         this.el.show();   
868     },
869     setWeight : function(str)
870     {
871           this.el.removeClass(this.weightClass);
872         this.el.addClass('btn-' + str);        
873     }
874     
875     
876 });
877
878  /*
879  * - LGPL
880  *
881  * column
882  * 
883  */
884
885 /**
886  * @class Roo.bootstrap.Column
887  * @extends Roo.bootstrap.Component
888  * Bootstrap Column class
889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
897  *
898  * 
899  * @cfg {Boolean} hidden (true|false) hide the element
900  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901  * @cfg {String} fa (ban|check|...) font awesome icon
902  * @cfg {Number} fasize (1|2|....) font awsome size
903
904  * @cfg {String} icon (info-sign|check|...) glyphicon name
905
906  * @cfg {String} html content of column.
907  * 
908  * @constructor
909  * Create a new Column
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.Column = function(config){
914     Roo.bootstrap.Column.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
918     
919     xs: false,
920     sm: false,
921     md: false,
922     lg: false,
923     xsoff: false,
924     smoff: false,
925     mdoff: false,
926     lgoff: false,
927     html: '',
928     offset: 0,
929     alert: false,
930     fa: false,
931     icon : false,
932     hidden : false,
933     fasize : 1,
934     
935     getAutoCreate : function(){
936         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
937         
938         cfg = {
939             tag: 'div',
940             cls: 'column'
941         };
942         
943         var settings=this;
944         ['xs','sm','md','lg'].map(function(size){
945             //Roo.log( size + ':' + settings[size]);
946             
947             if (settings[size+'off'] !== false) {
948                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
949             }
950             
951             if (settings[size] === false) {
952                 return;
953             }
954             
955             if (!settings[size]) { // 0 = hidden
956                 cfg.cls += ' hidden-' + size;
957                 return;
958             }
959             cfg.cls += ' col-' + size + '-' + settings[size];
960             
961         });
962         
963         if (this.hidden) {
964             cfg.cls += ' hidden';
965         }
966         
967         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968             cfg.cls +=' alert alert-' + this.alert;
969         }
970         
971         
972         if (this.html.length) {
973             cfg.html = this.html;
974         }
975         if (this.fa) {
976             var fasize = '';
977             if (this.fasize > 1) {
978                 fasize = ' fa-' + this.fasize + 'x';
979             }
980             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
981             
982             
983         }
984         if (this.icon) {
985             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
986         }
987         
988         return cfg;
989     }
990    
991 });
992
993  
994
995  /*
996  * - LGPL
997  *
998  * page container.
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Container
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Container class
1007  * @cfg {Boolean} jumbotron is it a jumbotron element
1008  * @cfg {String} html content of element
1009  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1011  * @cfg {String} header content of header (for panel)
1012  * @cfg {String} footer content of footer (for panel)
1013  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014  * @cfg {String} tag (header|aside|section) type of HTML tag.
1015  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016  * @cfg {String} fa font awesome icon
1017  * @cfg {String} icon (info-sign|check|...) glyphicon name
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {Boolean} expandable (true|false) default false
1020  * @cfg {Boolean} expanded (true|false) default true
1021  * @cfg {String} rheader contet on the right of header
1022  * @cfg {Boolean} clickable (true|false) default false
1023
1024  *     
1025  * @constructor
1026  * Create a new Container
1027  * @param {Object} config The config object
1028  */
1029
1030 Roo.bootstrap.Container = function(config){
1031     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035          /**
1036          * @event expand
1037          * After the panel has been expand
1038          * 
1039          * @param {Roo.bootstrap.Container} this
1040          */
1041         "expand" : true,
1042         /**
1043          * @event collapse
1044          * After the panel has been collapsed
1045          * 
1046          * @param {Roo.bootstrap.Container} this
1047          */
1048         "collapse" : true,
1049         /**
1050          * @event click
1051          * When a element is chick
1052          * @param {Roo.bootstrap.Container} this
1053          * @param {Roo.EventObject} e
1054          */
1055         "click" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1060     
1061     jumbotron : false,
1062     well: '',
1063     panel : '',
1064     header: '',
1065     footer : '',
1066     sticky: '',
1067     tag : false,
1068     alert : false,
1069     fa: false,
1070     icon : false,
1071     expandable : false,
1072     rheader : '',
1073     expanded : true,
1074     clickable: false,
1075   
1076      
1077     getChildContainer : function() {
1078         
1079         if(!this.el){
1080             return false;
1081         }
1082         
1083         if (this.panel.length) {
1084             return this.el.select('.panel-body',true).first();
1085         }
1086         
1087         return this.el;
1088     },
1089     
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : this.tag || 'div',
1095             html : '',
1096             cls : ''
1097         };
1098         if (this.jumbotron) {
1099             cfg.cls = 'jumbotron';
1100         }
1101         
1102         
1103         
1104         // - this is applied by the parent..
1105         //if (this.cls) {
1106         //    cfg.cls = this.cls + '';
1107         //}
1108         
1109         if (this.sticky.length) {
1110             
1111             var bd = Roo.get(document.body);
1112             if (!bd.hasClass('bootstrap-sticky')) {
1113                 bd.addClass('bootstrap-sticky');
1114                 Roo.select('html',true).setStyle('height', '100%');
1115             }
1116              
1117             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1118         }
1119         
1120         
1121         if (this.well.length) {
1122             switch (this.well) {
1123                 case 'lg':
1124                 case 'sm':
1125                     cfg.cls +=' well well-' +this.well;
1126                     break;
1127                 default:
1128                     cfg.cls +=' well';
1129                     break;
1130             }
1131         }
1132         
1133         if (this.hidden) {
1134             cfg.cls += ' hidden';
1135         }
1136         
1137         
1138         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139             cfg.cls +=' alert alert-' + this.alert;
1140         }
1141         
1142         var body = cfg;
1143         
1144         if (this.panel.length) {
1145             cfg.cls += ' panel panel-' + this.panel;
1146             cfg.cn = [];
1147             if (this.header.length) {
1148                 
1149                 var h = [];
1150                 
1151                 if(this.expandable){
1152                     
1153                     cfg.cls = cfg.cls + ' expandable';
1154                     
1155                     h.push({
1156                         tag: 'i',
1157                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1158                     });
1159                     
1160                 }
1161                 
1162                 h.push(
1163                     {
1164                         tag: 'span',
1165                         cls : 'panel-title',
1166                         html : (this.expandable ? '&nbsp;' : '') + this.header
1167                     },
1168                     {
1169                         tag: 'span',
1170                         cls: 'panel-header-right',
1171                         html: this.rheader
1172                     }
1173                 );
1174                 
1175                 cfg.cn.push({
1176                     cls : 'panel-heading',
1177                     style : this.expandable ? 'cursor: pointer' : '',
1178                     cn : h
1179                 });
1180                 
1181             }
1182             
1183             body = false;
1184             cfg.cn.push({
1185                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1186                 html : this.html
1187             });
1188             
1189             
1190             if (this.footer.length) {
1191                 cfg.cn.push({
1192                     cls : 'panel-footer',
1193                     html : this.footer
1194                     
1195                 });
1196             }
1197             
1198         }
1199         
1200         if (body) {
1201             body.html = this.html || cfg.html;
1202             // prefix with the icons..
1203             if (this.fa) {
1204                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1205             }
1206             if (this.icon) {
1207                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1208             }
1209             
1210             
1211         }
1212         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213             cfg.cls =  'container';
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() 
1220     {
1221         if(this.expandable){
1222             var headerEl = this.headerEl();
1223         
1224             if(headerEl){
1225                 headerEl.on('click', this.onToggleClick, this);
1226             }
1227         }
1228         
1229         if(this.clickable){
1230             this.el.on('click', this.onClick, this);
1231         }
1232         
1233     },
1234     
1235     onToggleClick : function()
1236     {
1237         var headerEl = this.headerEl();
1238         
1239         if(!headerEl){
1240             return;
1241         }
1242         
1243         if(this.expanded){
1244             this.collapse();
1245             return;
1246         }
1247         
1248         this.expand();
1249     },
1250     
1251     expand : function()
1252     {
1253         if(this.fireEvent('expand', this)) {
1254             
1255             this.expanded = true;
1256             
1257             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258             
1259             this.el.select('.panel-body',true).first().removeClass('hide');
1260             
1261             var toggleEl = this.toggleEl();
1262
1263             if(!toggleEl){
1264                 return;
1265             }
1266
1267             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1268         }
1269         
1270     },
1271     
1272     collapse : function()
1273     {
1274         if(this.fireEvent('collapse', this)) {
1275             
1276             this.expanded = false;
1277             
1278             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279             this.el.select('.panel-body',true).first().addClass('hide');
1280         
1281             var toggleEl = this.toggleEl();
1282
1283             if(!toggleEl){
1284                 return;
1285             }
1286
1287             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1288         }
1289     },
1290     
1291     toggleEl : function()
1292     {
1293         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1294             return;
1295         }
1296         
1297         return this.el.select('.panel-heading .fa',true).first();
1298     },
1299     
1300     headerEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading',true).first()
1307     },
1308     
1309     bodyEl : function()
1310     {
1311         if(!this.el || !this.panel.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-body',true).first()
1316     },
1317     
1318     titleEl : function()
1319     {
1320         if(!this.el || !this.panel.length || !this.header.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-title',true).first();
1325     },
1326     
1327     setTitle : function(v)
1328     {
1329         var titleEl = this.titleEl();
1330         
1331         if(!titleEl){
1332             return;
1333         }
1334         
1335         titleEl.dom.innerHTML = v;
1336     },
1337     
1338     getTitle : function()
1339     {
1340         
1341         var titleEl = this.titleEl();
1342         
1343         if(!titleEl){
1344             return '';
1345         }
1346         
1347         return titleEl.dom.innerHTML;
1348     },
1349     
1350     setRightTitle : function(v)
1351     {
1352         var t = this.el.select('.panel-header-right',true).first();
1353         
1354         if(!t){
1355             return;
1356         }
1357         
1358         t.dom.innerHTML = v;
1359     },
1360     
1361     onClick : function(e)
1362     {
1363         e.preventDefault();
1364         
1365         this.fireEvent('click', this, e);
1366     }
1367    
1368 });
1369
1370  /*
1371  * - LGPL
1372  *
1373  * image
1374  * 
1375  */
1376
1377
1378 /**
1379  * @class Roo.bootstrap.Img
1380  * @extends Roo.bootstrap.Component
1381  * Bootstrap Img class
1382  * @cfg {Boolean} imgResponsive false | true
1383  * @cfg {String} border rounded | circle | thumbnail
1384  * @cfg {String} src image source
1385  * @cfg {String} alt image alternative text
1386  * @cfg {String} href a tag href
1387  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388  * @cfg {String} xsUrl xs image source
1389  * @cfg {String} smUrl sm image source
1390  * @cfg {String} mdUrl md image source
1391  * @cfg {String} lgUrl lg image source
1392  * 
1393  * @constructor
1394  * Create a new Input
1395  * @param {Object} config The config object
1396  */
1397
1398 Roo.bootstrap.Img = function(config){
1399     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1400     
1401     this.addEvents({
1402         // img events
1403         /**
1404          * @event click
1405          * The img click event for the img.
1406          * @param {Roo.EventObject} e
1407          */
1408         "click" : true
1409     });
1410 };
1411
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1413     
1414     imgResponsive: true,
1415     border: '',
1416     src: 'about:blank',
1417     href: false,
1418     target: false,
1419     xsUrl: '',
1420     smUrl: '',
1421     mdUrl: '',
1422     lgUrl: '',
1423
1424     getAutoCreate : function()
1425     {   
1426         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427             return this.createSingleImg();
1428         }
1429         
1430         var cfg = {
1431             tag: 'div',
1432             cls: 'roo-image-responsive-group',
1433             cn: []
1434         };
1435         var _this = this;
1436         
1437         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438             
1439             if(!_this[size + 'Url']){
1440                 return;
1441             }
1442             
1443             var img = {
1444                 tag: 'img',
1445                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446                 html: _this.html || cfg.html,
1447                 src: _this[size + 'Url']
1448             };
1449             
1450             img.cls += ' roo-image-responsive-' + size;
1451             
1452             var s = ['xs', 'sm', 'md', 'lg'];
1453             
1454             s.splice(s.indexOf(size), 1);
1455             
1456             Roo.each(s, function(ss){
1457                 img.cls += ' hidden-' + ss;
1458             });
1459             
1460             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461                 cfg.cls += ' img-' + _this.border;
1462             }
1463             
1464             if(_this.alt){
1465                 cfg.alt = _this.alt;
1466             }
1467             
1468             if(_this.href){
1469                 var a = {
1470                     tag: 'a',
1471                     href: _this.href,
1472                     cn: [
1473                         img
1474                     ]
1475                 };
1476
1477                 if(this.target){
1478                     a.target = _this.target;
1479                 }
1480             }
1481             
1482             cfg.cn.push((_this.href) ? a : img);
1483             
1484         });
1485         
1486         return cfg;
1487     },
1488     
1489     createSingleImg : function()
1490     {
1491         var cfg = {
1492             tag: 'img',
1493             cls: (this.imgResponsive) ? 'img-responsive' : '',
1494             html : null,
1495             src : 'about:blank'  // just incase src get's set to undefined?!?
1496         };
1497         
1498         cfg.html = this.html || cfg.html;
1499         
1500         cfg.src = this.src || cfg.src;
1501         
1502         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503             cfg.cls += ' img-' + this.border;
1504         }
1505         
1506         if(this.alt){
1507             cfg.alt = this.alt;
1508         }
1509         
1510         if(this.href){
1511             var a = {
1512                 tag: 'a',
1513                 href: this.href,
1514                 cn: [
1515                     cfg
1516                 ]
1517             };
1518             
1519             if(this.target){
1520                 a.target = this.target;
1521             }
1522             
1523         }
1524         
1525         return (this.href) ? a : cfg;
1526     },
1527     
1528     initEvents: function() 
1529     {
1530         if(!this.href){
1531             this.el.on('click', this.onClick, this);
1532         }
1533         
1534     },
1535     
1536     onClick : function(e)
1537     {
1538         Roo.log('img onclick');
1539         this.fireEvent('click', this, e);
1540     },
1541     /**
1542      * Sets the url of the image - used to update it
1543      * @param {String} url the url of the image
1544      */
1545     
1546     setSrc : function(url)
1547     {
1548         this.src =  url;
1549         
1550         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551             this.el.dom.src =  url;
1552             return;
1553         }
1554         
1555         this.el.select('img', true).first().dom.src =  url;
1556     }
1557     
1558     
1559    
1560 });
1561
1562  /*
1563  * - LGPL
1564  *
1565  * image
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Link
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Link Class
1574  * @cfg {String} alt image alternative text
1575  * @cfg {String} href a tag href
1576  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577  * @cfg {String} html the content of the link.
1578  * @cfg {String} anchor name for the anchor link
1579  * @cfg {String} fa - favicon
1580
1581  * @cfg {Boolean} preventDefault (true | false) default false
1582
1583  * 
1584  * @constructor
1585  * Create a new Input
1586  * @param {Object} config The config object
1587  */
1588
1589 Roo.bootstrap.Link = function(config){
1590     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1591     
1592     this.addEvents({
1593         // img events
1594         /**
1595          * @event click
1596          * The img click event for the img.
1597          * @param {Roo.EventObject} e
1598          */
1599         "click" : true
1600     });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1604     
1605     href: false,
1606     target: false,
1607     preventDefault: false,
1608     anchor : false,
1609     alt : false,
1610     fa: false,
1611
1612
1613     getAutoCreate : function()
1614     {
1615         var html = this.html || '';
1616         
1617         if (this.fa !== false) {
1618             html = '<i class="fa fa-' + this.fa + '"></i>';
1619         }
1620         var cfg = {
1621             tag: 'a'
1622         };
1623         // anchor's do not require html/href...
1624         if (this.anchor === false) {
1625             cfg.html = html;
1626             cfg.href = this.href || '#';
1627         } else {
1628             cfg.name = this.anchor;
1629             if (this.html !== false || this.fa !== false) {
1630                 cfg.html = html;
1631             }
1632             if (this.href !== false) {
1633                 cfg.href = this.href;
1634             }
1635         }
1636         
1637         if(this.alt !== false){
1638             cfg.alt = this.alt;
1639         }
1640         
1641         
1642         if(this.target !== false) {
1643             cfg.target = this.target;
1644         }
1645         
1646         return cfg;
1647     },
1648     
1649     initEvents: function() {
1650         
1651         if(!this.href || this.preventDefault){
1652             this.el.on('click', this.onClick, this);
1653         }
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         if(this.preventDefault){
1659             e.preventDefault();
1660         }
1661         //Roo.log('img onclick');
1662         this.fireEvent('click', this, e);
1663     }
1664    
1665 });
1666
1667  /*
1668  * - LGPL
1669  *
1670  * header
1671  * 
1672  */
1673
1674 /**
1675  * @class Roo.bootstrap.Header
1676  * @extends Roo.bootstrap.Component
1677  * Bootstrap Header class
1678  * @cfg {String} html content of header
1679  * @cfg {Number} level (1|2|3|4|5|6) default 1
1680  * 
1681  * @constructor
1682  * Create a new Header
1683  * @param {Object} config The config object
1684  */
1685
1686
1687 Roo.bootstrap.Header  = function(config){
1688     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1689 };
1690
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1692     
1693     //href : false,
1694     html : false,
1695     level : 1,
1696     
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         
1702         
1703         var cfg = {
1704             tag: 'h' + (1 *this.level),
1705             html: this.html || ''
1706         } ;
1707         
1708         return cfg;
1709     }
1710    
1711 });
1712
1713  
1714
1715  /*
1716  * Based on:
1717  * Ext JS Library 1.1.1
1718  * Copyright(c) 2006-2007, Ext JS, LLC.
1719  *
1720  * Originally Released Under LGPL - original licence link has changed is not relivant.
1721  *
1722  * Fork - LGPL
1723  * <script type="text/javascript">
1724  */
1725  
1726 /**
1727  * @class Roo.bootstrap.MenuMgr
1728  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1729  * @singleton
1730  */
1731 Roo.bootstrap.MenuMgr = function(){
1732    var menus, active, groups = {}, attached = false, lastShow = new Date();
1733
1734    // private - called when first menu is created
1735    function init(){
1736        menus = {};
1737        active = new Roo.util.MixedCollection();
1738        Roo.get(document).addKeyListener(27, function(){
1739            if(active.length > 0){
1740                hideAll();
1741            }
1742        });
1743    }
1744
1745    // private
1746    function hideAll(){
1747        if(active && active.length > 0){
1748            var c = active.clone();
1749            c.each(function(m){
1750                m.hide();
1751            });
1752        }
1753    }
1754
1755    // private
1756    function onHide(m){
1757        active.remove(m);
1758        if(active.length < 1){
1759            Roo.get(document).un("mouseup", onMouseDown);
1760             
1761            attached = false;
1762        }
1763    }
1764
1765    // private
1766    function onShow(m){
1767        var last = active.last();
1768        lastShow = new Date();
1769        active.add(m);
1770        if(!attached){
1771           Roo.get(document).on("mouseup", onMouseDown);
1772            
1773            attached = true;
1774        }
1775        if(m.parentMenu){
1776           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777           m.parentMenu.activeChild = m;
1778        }else if(last && last.isVisible()){
1779           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1780        }
1781    }
1782
1783    // private
1784    function onBeforeHide(m){
1785        if(m.activeChild){
1786            m.activeChild.hide();
1787        }
1788        if(m.autoHideTimer){
1789            clearTimeout(m.autoHideTimer);
1790            delete m.autoHideTimer;
1791        }
1792    }
1793
1794    // private
1795    function onBeforeShow(m){
1796        var pm = m.parentMenu;
1797        if(!pm && !m.allowOtherMenus){
1798            hideAll();
1799        }else if(pm && pm.activeChild && active != m){
1800            pm.activeChild.hide();
1801        }
1802    }
1803
1804    // private this should really trigger on mouseup..
1805    function onMouseDown(e){
1806         Roo.log("on Mouse Up");
1807         
1808         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809             Roo.log("MenuManager hideAll");
1810             hideAll();
1811             e.stopEvent();
1812         }
1813         
1814         
1815    }
1816
1817    // private
1818    function onBeforeCheck(mi, state){
1819        if(state){
1820            var g = groups[mi.group];
1821            for(var i = 0, l = g.length; i < l; i++){
1822                if(g[i] != mi){
1823                    g[i].setChecked(false);
1824                }
1825            }
1826        }
1827    }
1828
1829    return {
1830
1831        /**
1832         * Hides all menus that are currently visible
1833         */
1834        hideAll : function(){
1835             hideAll();  
1836        },
1837
1838        // private
1839        register : function(menu){
1840            if(!menus){
1841                init();
1842            }
1843            menus[menu.id] = menu;
1844            menu.on("beforehide", onBeforeHide);
1845            menu.on("hide", onHide);
1846            menu.on("beforeshow", onBeforeShow);
1847            menu.on("show", onShow);
1848            var g = menu.group;
1849            if(g && menu.events["checkchange"]){
1850                if(!groups[g]){
1851                    groups[g] = [];
1852                }
1853                groups[g].push(menu);
1854                menu.on("checkchange", onCheck);
1855            }
1856        },
1857
1858         /**
1859          * Returns a {@link Roo.menu.Menu} object
1860          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861          * be used to generate and return a new Menu instance.
1862          */
1863        get : function(menu){
1864            if(typeof menu == "string"){ // menu id
1865                return menus[menu];
1866            }else if(menu.events){  // menu instance
1867                return menu;
1868            }
1869            /*else if(typeof menu.length == 'number'){ // array of menu items?
1870                return new Roo.bootstrap.Menu({items:menu});
1871            }else{ // otherwise, must be a config
1872                return new Roo.bootstrap.Menu(menu);
1873            }
1874            */
1875            return false;
1876        },
1877
1878        // private
1879        unregister : function(menu){
1880            delete menus[menu.id];
1881            menu.un("beforehide", onBeforeHide);
1882            menu.un("hide", onHide);
1883            menu.un("beforeshow", onBeforeShow);
1884            menu.un("show", onShow);
1885            var g = menu.group;
1886            if(g && menu.events["checkchange"]){
1887                groups[g].remove(menu);
1888                menu.un("checkchange", onCheck);
1889            }
1890        },
1891
1892        // private
1893        registerCheckable : function(menuItem){
1894            var g = menuItem.group;
1895            if(g){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menuItem);
1900                menuItem.on("beforecheckchange", onBeforeCheck);
1901            }
1902        },
1903
1904        // private
1905        unregisterCheckable : function(menuItem){
1906            var g = menuItem.group;
1907            if(g){
1908                groups[g].remove(menuItem);
1909                menuItem.un("beforecheckchange", onBeforeCheck);
1910            }
1911        }
1912    };
1913 }();/*
1914  * - LGPL
1915  *
1916  * menu
1917  * 
1918  */
1919
1920 /**
1921  * @class Roo.bootstrap.Menu
1922  * @extends Roo.bootstrap.Component
1923  * Bootstrap Menu class - container for MenuItems
1924  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1926  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1927  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1928  * 
1929  * @constructor
1930  * Create a new Menu
1931  * @param {Object} config The config object
1932  */
1933
1934
1935 Roo.bootstrap.Menu = function(config){
1936     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937     if (this.registerMenu && this.type != 'treeview')  {
1938         Roo.bootstrap.MenuMgr.register(this);
1939     }
1940     this.addEvents({
1941         /**
1942          * @event beforeshow
1943          * Fires before this menu is displayed
1944          * @param {Roo.menu.Menu} this
1945          */
1946         beforeshow : true,
1947         /**
1948          * @event beforehide
1949          * Fires before this menu is hidden
1950          * @param {Roo.menu.Menu} this
1951          */
1952         beforehide : true,
1953         /**
1954          * @event show
1955          * Fires after this menu is displayed
1956          * @param {Roo.menu.Menu} this
1957          */
1958         show : true,
1959         /**
1960          * @event hide
1961          * Fires after this menu is hidden
1962          * @param {Roo.menu.Menu} this
1963          */
1964         hide : true,
1965         /**
1966          * @event click
1967          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968          * @param {Roo.menu.Menu} this
1969          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970          * @param {Roo.EventObject} e
1971          */
1972         click : true,
1973         /**
1974          * @event mouseover
1975          * Fires when the mouse is hovering over this menu
1976          * @param {Roo.menu.Menu} this
1977          * @param {Roo.EventObject} e
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          */
1980         mouseover : true,
1981         /**
1982          * @event mouseout
1983          * Fires when the mouse exits this menu
1984          * @param {Roo.menu.Menu} this
1985          * @param {Roo.EventObject} e
1986          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1987          */
1988         mouseout : true,
1989         /**
1990          * @event itemclick
1991          * Fires when a menu item contained in this menu is clicked
1992          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993          * @param {Roo.EventObject} e
1994          */
1995         itemclick: true
1996     });
1997     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1998 };
1999
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2001     
2002    /// html : false,
2003     //align : '',
2004     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2005     type: false,
2006     /**
2007      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008      */
2009     registerMenu : true,
2010     
2011     menuItems :false, // stores the menu items..
2012     
2013     hidden:true,
2014         
2015     parentMenu : false,
2016     
2017     stopEvent : true,
2018     
2019     isLink : false,
2020     
2021     getChildContainer : function() {
2022         return this.el;  
2023     },
2024     
2025     getAutoCreate : function(){
2026          
2027         //if (['right'].indexOf(this.align)!==-1) {
2028         //    cfg.cn[1].cls += ' pull-right'
2029         //}
2030         
2031         
2032         var cfg = {
2033             tag : 'ul',
2034             cls : 'dropdown-menu' ,
2035             style : 'z-index:1000'
2036             
2037         };
2038         
2039         if (this.type === 'submenu') {
2040             cfg.cls = 'submenu active';
2041         }
2042         if (this.type === 'treeview') {
2043             cfg.cls = 'treeview-menu';
2044         }
2045         
2046         return cfg;
2047     },
2048     initEvents : function() {
2049         
2050        // Roo.log("ADD event");
2051        // Roo.log(this.triggerEl.dom);
2052         
2053         this.triggerEl.on('click', this.onTriggerClick, this);
2054         
2055         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056         
2057         this.triggerEl.addClass('dropdown-toggle');
2058         
2059         if (Roo.isTouch) {
2060             this.el.on('touchstart'  , this.onTouch, this);
2061         }
2062         this.el.on('click' , this.onClick, this);
2063
2064         this.el.on("mouseover", this.onMouseOver, this);
2065         this.el.on("mouseout", this.onMouseOut, this);
2066         
2067     },
2068     
2069     findTargetItem : function(e)
2070     {
2071         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2072         if(!t){
2073             return false;
2074         }
2075         //Roo.log(t);         Roo.log(t.id);
2076         if(t && t.id){
2077             //Roo.log(this.menuitems);
2078             return this.menuitems.get(t.id);
2079             
2080             //return this.items.get(t.menuItemId);
2081         }
2082         
2083         return false;
2084     },
2085     
2086     onTouch : function(e) 
2087     {
2088         Roo.log("menu.onTouch");
2089         //e.stopEvent(); this make the user popdown broken
2090         this.onClick(e);
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log("menu.onClick");
2096         
2097         var t = this.findTargetItem(e);
2098         if(!t || t.isContainer){
2099             return;
2100         }
2101         Roo.log(e);
2102         /*
2103         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2104             if(t == this.activeItem && t.shouldDeactivate(e)){
2105                 this.activeItem.deactivate();
2106                 delete this.activeItem;
2107                 return;
2108             }
2109             if(t.canActivate){
2110                 this.setActiveItem(t, true);
2111             }
2112             return;
2113             
2114             
2115         }
2116         */
2117        
2118         Roo.log('pass click event');
2119         
2120         t.onClick(e);
2121         
2122         this.fireEvent("click", this, t, e);
2123         
2124         var _this = this;
2125         
2126         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                     combobox
12928                 ];
12929                 
12930                 if(this.indicatorpos == 'right'){
12931                     Roo.log('hidden name:'+this.hiddenName);
12932                     cfg.cn = [
12933                         {
12934                            tag: 'label',
12935                            cn : [
12936                                {
12937                                    tag : 'span',
12938                                    html : this.fieldLabel
12939                                },
12940                                {
12941                                   tag : 'i',
12942                                   cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12943                                   tooltip : 'This field is required'
12944                                }
12945                            ]
12946                         },
12947                         combobox
12948                     ];
12949                     
12950                 }
12951
12952         } else {
12953             
12954 //                Roo.log(" no label && no align");
12955                 cfg = combobox
12956                      
12957                 
12958         }
12959          
12960         var settings=this;
12961         ['xs','sm','md','lg'].map(function(size){
12962             if (settings[size]) {
12963                 cfg.cls += ' col-' + size + '-' + settings[size];
12964             }
12965         });
12966         
12967         return cfg;
12968         
12969     },
12970     
12971     _initEventsCalled : false,
12972     
12973     // private
12974     initEvents: function()
12975     {   
12976         if (this._initEventsCalled) { // as we call render... prevent looping...
12977             return;
12978         }
12979         this._initEventsCalled = true;
12980         
12981         if (!this.store) {
12982             throw "can not find store for combo";
12983         }
12984         
12985         this.store = Roo.factory(this.store, Roo.data);
12986         this.store.parent = this;
12987         
12988         // if we are building from html. then this element is so complex, that we can not really
12989         // use the rendered HTML.
12990         // so we have to trash and replace the previous code.
12991         if (Roo.XComponent.build_from_html) {
12992             
12993             // remove this element....
12994             var e = this.el.dom, k=0;
12995             while (e ) { e = e.previousSibling;  ++k;}
12996
12997             this.el.remove();
12998             
12999             this.el=false;
13000             this.rendered = false;
13001             
13002             this.render(this.parent().getChildContainer(true), k);
13003             
13004             
13005             
13006         }
13007         
13008         if(Roo.isIOS && this.useNativeIOS){
13009             this.initIOSView();
13010             return;
13011         }
13012         
13013         /*
13014          * Touch Devices
13015          */
13016         
13017         if(Roo.isTouch && this.mobileTouchView){
13018             this.initTouchView();
13019             return;
13020         }
13021         
13022         if(this.tickable){
13023             this.initTickableEvents();
13024             return;
13025         }
13026         
13027         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13028         
13029         if(this.hiddenName){
13030             
13031             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13032             
13033             this.hiddenField.dom.value =
13034                 this.hiddenValue !== undefined ? this.hiddenValue :
13035                 this.value !== undefined ? this.value : '';
13036
13037             // prevent input submission
13038             this.el.dom.removeAttribute('name');
13039             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13040              
13041              
13042         }
13043         //if(Roo.isGecko){
13044         //    this.el.dom.setAttribute('autocomplete', 'off');
13045         //}
13046         
13047         var cls = 'x-combo-list';
13048         
13049         //this.list = new Roo.Layer({
13050         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13051         //});
13052         
13053         var _this = this;
13054         
13055         (function(){
13056             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13057             _this.list.setWidth(lw);
13058         }).defer(100);
13059         
13060         this.list.on('mouseover', this.onViewOver, this);
13061         this.list.on('mousemove', this.onViewMove, this);
13062         
13063         this.list.on('scroll', this.onViewScroll, this);
13064         
13065         /*
13066         this.list.swallowEvent('mousewheel');
13067         this.assetHeight = 0;
13068
13069         if(this.title){
13070             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13071             this.assetHeight += this.header.getHeight();
13072         }
13073
13074         this.innerList = this.list.createChild({cls:cls+'-inner'});
13075         this.innerList.on('mouseover', this.onViewOver, this);
13076         this.innerList.on('mousemove', this.onViewMove, this);
13077         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13078         
13079         if(this.allowBlank && !this.pageSize && !this.disableClear){
13080             this.footer = this.list.createChild({cls:cls+'-ft'});
13081             this.pageTb = new Roo.Toolbar(this.footer);
13082            
13083         }
13084         if(this.pageSize){
13085             this.footer = this.list.createChild({cls:cls+'-ft'});
13086             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13087                     {pageSize: this.pageSize});
13088             
13089         }
13090         
13091         if (this.pageTb && this.allowBlank && !this.disableClear) {
13092             var _this = this;
13093             this.pageTb.add(new Roo.Toolbar.Fill(), {
13094                 cls: 'x-btn-icon x-btn-clear',
13095                 text: '&#160;',
13096                 handler: function()
13097                 {
13098                     _this.collapse();
13099                     _this.clearValue();
13100                     _this.onSelect(false, -1);
13101                 }
13102             });
13103         }
13104         if (this.footer) {
13105             this.assetHeight += this.footer.getHeight();
13106         }
13107         */
13108             
13109         if(!this.tpl){
13110             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13111         }
13112
13113         this.view = new Roo.View(this.list, this.tpl, {
13114             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13115         });
13116         //this.view.wrapEl.setDisplayed(false);
13117         this.view.on('click', this.onViewClick, this);
13118         
13119         
13120         this.store.on('beforeload', this.onBeforeLoad, this);
13121         this.store.on('load', this.onLoad, this);
13122         this.store.on('loadexception', this.onLoadException, this);
13123         /*
13124         if(this.resizable){
13125             this.resizer = new Roo.Resizable(this.list,  {
13126                pinned:true, handles:'se'
13127             });
13128             this.resizer.on('resize', function(r, w, h){
13129                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13130                 this.listWidth = w;
13131                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13132                 this.restrictHeight();
13133             }, this);
13134             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13135         }
13136         */
13137         if(!this.editable){
13138             this.editable = true;
13139             this.setEditable(false);
13140         }
13141         
13142         /*
13143         
13144         if (typeof(this.events.add.listeners) != 'undefined') {
13145             
13146             this.addicon = this.wrap.createChild(
13147                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13148        
13149             this.addicon.on('click', function(e) {
13150                 this.fireEvent('add', this);
13151             }, this);
13152         }
13153         if (typeof(this.events.edit.listeners) != 'undefined') {
13154             
13155             this.editicon = this.wrap.createChild(
13156                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13157             if (this.addicon) {
13158                 this.editicon.setStyle('margin-left', '40px');
13159             }
13160             this.editicon.on('click', function(e) {
13161                 
13162                 // we fire even  if inothing is selected..
13163                 this.fireEvent('edit', this, this.lastData );
13164                 
13165             }, this);
13166         }
13167         */
13168         
13169         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13170             "up" : function(e){
13171                 this.inKeyMode = true;
13172                 this.selectPrev();
13173             },
13174
13175             "down" : function(e){
13176                 if(!this.isExpanded()){
13177                     this.onTriggerClick();
13178                 }else{
13179                     this.inKeyMode = true;
13180                     this.selectNext();
13181                 }
13182             },
13183
13184             "enter" : function(e){
13185 //                this.onViewClick();
13186                 //return true;
13187                 this.collapse();
13188                 
13189                 if(this.fireEvent("specialkey", this, e)){
13190                     this.onViewClick(false);
13191                 }
13192                 
13193                 return true;
13194             },
13195
13196             "esc" : function(e){
13197                 this.collapse();
13198             },
13199
13200             "tab" : function(e){
13201                 this.collapse();
13202                 
13203                 if(this.fireEvent("specialkey", this, e)){
13204                     this.onViewClick(false);
13205                 }
13206                 
13207                 return true;
13208             },
13209
13210             scope : this,
13211
13212             doRelay : function(foo, bar, hname){
13213                 if(hname == 'down' || this.scope.isExpanded()){
13214                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13215                 }
13216                 return true;
13217             },
13218
13219             forceKeyDown: true
13220         });
13221         
13222         
13223         this.queryDelay = Math.max(this.queryDelay || 10,
13224                 this.mode == 'local' ? 10 : 250);
13225         
13226         
13227         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13228         
13229         if(this.typeAhead){
13230             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13231         }
13232         if(this.editable !== false){
13233             this.inputEl().on("keyup", this.onKeyUp, this);
13234         }
13235         if(this.forceSelection){
13236             this.inputEl().on('blur', this.doForce, this);
13237         }
13238         
13239         if(this.multiple){
13240             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13241             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13242         }
13243     },
13244     
13245     initTickableEvents: function()
13246     {   
13247         this.createList();
13248         
13249         if(this.hiddenName){
13250             
13251             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13252             
13253             this.hiddenField.dom.value =
13254                 this.hiddenValue !== undefined ? this.hiddenValue :
13255                 this.value !== undefined ? this.value : '';
13256
13257             // prevent input submission
13258             this.el.dom.removeAttribute('name');
13259             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13260              
13261              
13262         }
13263         
13264 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13265         
13266         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13267         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13268         if(this.triggerList){
13269             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13270         }
13271          
13272         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13273         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13274         
13275         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13276         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13277         
13278         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13279         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13280         
13281         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13282         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13283         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13284         
13285         this.okBtn.hide();
13286         this.cancelBtn.hide();
13287         
13288         var _this = this;
13289         
13290         (function(){
13291             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13292             _this.list.setWidth(lw);
13293         }).defer(100);
13294         
13295         this.list.on('mouseover', this.onViewOver, this);
13296         this.list.on('mousemove', this.onViewMove, this);
13297         
13298         this.list.on('scroll', this.onViewScroll, this);
13299         
13300         if(!this.tpl){
13301             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>';
13302         }
13303
13304         this.view = new Roo.View(this.list, this.tpl, {
13305             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13306         });
13307         
13308         //this.view.wrapEl.setDisplayed(false);
13309         this.view.on('click', this.onViewClick, this);
13310         
13311         
13312         
13313         this.store.on('beforeload', this.onBeforeLoad, this);
13314         this.store.on('load', this.onLoad, this);
13315         this.store.on('loadexception', this.onLoadException, this);
13316         
13317         if(this.editable){
13318             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13319                 "up" : function(e){
13320                     this.inKeyMode = true;
13321                     this.selectPrev();
13322                 },
13323
13324                 "down" : function(e){
13325                     this.inKeyMode = true;
13326                     this.selectNext();
13327                 },
13328
13329                 "enter" : function(e){
13330                     if(this.fireEvent("specialkey", this, e)){
13331                         this.onViewClick(false);
13332                     }
13333                     
13334                     return true;
13335                 },
13336
13337                 "esc" : function(e){
13338                     this.onTickableFooterButtonClick(e, false, false);
13339                 },
13340
13341                 "tab" : function(e){
13342                     this.fireEvent("specialkey", this, e);
13343                     
13344                     this.onTickableFooterButtonClick(e, false, false);
13345                     
13346                     return true;
13347                 },
13348
13349                 scope : this,
13350
13351                 doRelay : function(e, fn, key){
13352                     if(this.scope.isExpanded()){
13353                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13354                     }
13355                     return true;
13356                 },
13357
13358                 forceKeyDown: true
13359             });
13360         }
13361         
13362         this.queryDelay = Math.max(this.queryDelay || 10,
13363                 this.mode == 'local' ? 10 : 250);
13364         
13365         
13366         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13367         
13368         if(this.typeAhead){
13369             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13370         }
13371         
13372         if(this.editable !== false){
13373             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13374         }
13375         
13376         this.indicator = this.indicatorEl();
13377         
13378         if(this.indicator){
13379             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13380             this.indicator.hide();
13381         }
13382         
13383     },
13384
13385     onDestroy : function(){
13386         if(this.view){
13387             this.view.setStore(null);
13388             this.view.el.removeAllListeners();
13389             this.view.el.remove();
13390             this.view.purgeListeners();
13391         }
13392         if(this.list){
13393             this.list.dom.innerHTML  = '';
13394         }
13395         
13396         if(this.store){
13397             this.store.un('beforeload', this.onBeforeLoad, this);
13398             this.store.un('load', this.onLoad, this);
13399             this.store.un('loadexception', this.onLoadException, this);
13400         }
13401         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13402     },
13403
13404     // private
13405     fireKey : function(e){
13406         if(e.isNavKeyPress() && !this.list.isVisible()){
13407             this.fireEvent("specialkey", this, e);
13408         }
13409     },
13410
13411     // private
13412     onResize: function(w, h){
13413 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13414 //        
13415 //        if(typeof w != 'number'){
13416 //            // we do not handle it!?!?
13417 //            return;
13418 //        }
13419 //        var tw = this.trigger.getWidth();
13420 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13421 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13422 //        var x = w - tw;
13423 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13424 //            
13425 //        //this.trigger.setStyle('left', x+'px');
13426 //        
13427 //        if(this.list && this.listWidth === undefined){
13428 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13429 //            this.list.setWidth(lw);
13430 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13431 //        }
13432         
13433     
13434         
13435     },
13436
13437     /**
13438      * Allow or prevent the user from directly editing the field text.  If false is passed,
13439      * the user will only be able to select from the items defined in the dropdown list.  This method
13440      * is the runtime equivalent of setting the 'editable' config option at config time.
13441      * @param {Boolean} value True to allow the user to directly edit the field text
13442      */
13443     setEditable : function(value){
13444         if(value == this.editable){
13445             return;
13446         }
13447         this.editable = value;
13448         if(!value){
13449             this.inputEl().dom.setAttribute('readOnly', true);
13450             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13451             this.inputEl().addClass('x-combo-noedit');
13452         }else{
13453             this.inputEl().dom.setAttribute('readOnly', false);
13454             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13455             this.inputEl().removeClass('x-combo-noedit');
13456         }
13457     },
13458
13459     // private
13460     
13461     onBeforeLoad : function(combo,opts){
13462         if(!this.hasFocus){
13463             return;
13464         }
13465          if (!opts.add) {
13466             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13467          }
13468         this.restrictHeight();
13469         this.selectedIndex = -1;
13470     },
13471
13472     // private
13473     onLoad : function(){
13474         
13475         this.hasQuery = false;
13476         
13477         if(!this.hasFocus){
13478             return;
13479         }
13480         
13481         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13482             this.loading.hide();
13483         }
13484         
13485         if(this.store.getCount() > 0){
13486             
13487             this.expand();
13488             this.restrictHeight();
13489             if(this.lastQuery == this.allQuery){
13490                 if(this.editable && !this.tickable){
13491                     this.inputEl().dom.select();
13492                 }
13493                 
13494                 if(
13495                     !this.selectByValue(this.value, true) &&
13496                     this.autoFocus && 
13497                     (
13498                         !this.store.lastOptions ||
13499                         typeof(this.store.lastOptions.add) == 'undefined' || 
13500                         this.store.lastOptions.add != true
13501                     )
13502                 ){
13503                     this.select(0, true);
13504                 }
13505             }else{
13506                 if(this.autoFocus){
13507                     this.selectNext();
13508                 }
13509                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13510                     this.taTask.delay(this.typeAheadDelay);
13511                 }
13512             }
13513         }else{
13514             this.onEmptyResults();
13515         }
13516         
13517         //this.el.focus();
13518     },
13519     // private
13520     onLoadException : function()
13521     {
13522         this.hasQuery = false;
13523         
13524         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13525             this.loading.hide();
13526         }
13527         
13528         if(this.tickable && this.editable){
13529             return;
13530         }
13531         
13532         this.collapse();
13533         // only causes errors at present
13534         //Roo.log(this.store.reader.jsonData);
13535         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13536             // fixme
13537             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13538         //}
13539         
13540         
13541     },
13542     // private
13543     onTypeAhead : function(){
13544         if(this.store.getCount() > 0){
13545             var r = this.store.getAt(0);
13546             var newValue = r.data[this.displayField];
13547             var len = newValue.length;
13548             var selStart = this.getRawValue().length;
13549             
13550             if(selStart != len){
13551                 this.setRawValue(newValue);
13552                 this.selectText(selStart, newValue.length);
13553             }
13554         }
13555     },
13556
13557     // private
13558     onSelect : function(record, index){
13559         
13560         if(this.fireEvent('beforeselect', this, record, index) !== false){
13561         
13562             this.setFromData(index > -1 ? record.data : false);
13563             
13564             this.collapse();
13565             this.fireEvent('select', this, record, index);
13566         }
13567     },
13568
13569     /**
13570      * Returns the currently selected field value or empty string if no value is set.
13571      * @return {String} value The selected value
13572      */
13573     getValue : function()
13574     {
13575         if(Roo.isIOS && this.useNativeIOS){
13576             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13577         }
13578         
13579         if(this.multiple){
13580             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13581         }
13582         
13583         if(this.valueField){
13584             return typeof this.value != 'undefined' ? this.value : '';
13585         }else{
13586             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13587         }
13588     },
13589     
13590     getRawValue : function()
13591     {
13592         if(Roo.isIOS && this.useNativeIOS){
13593             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13594         }
13595         
13596         var v = this.inputEl().getValue();
13597         
13598         return v;
13599     },
13600
13601     /**
13602      * Clears any text/value currently set in the field
13603      */
13604     clearValue : function(){
13605         
13606         if(this.hiddenField){
13607             this.hiddenField.dom.value = '';
13608         }
13609         this.value = '';
13610         this.setRawValue('');
13611         this.lastSelectionText = '';
13612         this.lastData = false;
13613         
13614         var close = this.closeTriggerEl();
13615         
13616         if(close){
13617             close.hide();
13618         }
13619         
13620         this.validate();
13621         
13622     },
13623
13624     /**
13625      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13626      * will be displayed in the field.  If the value does not match the data value of an existing item,
13627      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13628      * Otherwise the field will be blank (although the value will still be set).
13629      * @param {String} value The value to match
13630      */
13631     setValue : function(v)
13632     {
13633         if(Roo.isIOS && this.useNativeIOS){
13634             this.setIOSValue(v);
13635             return;
13636         }
13637         
13638         if(this.multiple){
13639             this.syncValue();
13640             return;
13641         }
13642         
13643         var text = v;
13644         if(this.valueField){
13645             var r = this.findRecord(this.valueField, v);
13646             if(r){
13647                 text = r.data[this.displayField];
13648             }else if(this.valueNotFoundText !== undefined){
13649                 text = this.valueNotFoundText;
13650             }
13651         }
13652         this.lastSelectionText = text;
13653         if(this.hiddenField){
13654             this.hiddenField.dom.value = v;
13655         }
13656         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13657         this.value = v;
13658         
13659         var close = this.closeTriggerEl();
13660         
13661         if(close){
13662             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13663         }
13664         
13665         this.validate();
13666     },
13667     /**
13668      * @property {Object} the last set data for the element
13669      */
13670     
13671     lastData : false,
13672     /**
13673      * Sets the value of the field based on a object which is related to the record format for the store.
13674      * @param {Object} value the value to set as. or false on reset?
13675      */
13676     setFromData : function(o){
13677         
13678         if(this.multiple){
13679             this.addItem(o);
13680             return;
13681         }
13682             
13683         var dv = ''; // display value
13684         var vv = ''; // value value..
13685         this.lastData = o;
13686         if (this.displayField) {
13687             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13688         } else {
13689             // this is an error condition!!!
13690             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13691         }
13692         
13693         if(this.valueField){
13694             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13695         }
13696         
13697         var close = this.closeTriggerEl();
13698         
13699         if(close){
13700             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13701         }
13702         
13703         if(this.hiddenField){
13704             this.hiddenField.dom.value = vv;
13705             
13706             this.lastSelectionText = dv;
13707             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13708             this.value = vv;
13709             return;
13710         }
13711         // no hidden field.. - we store the value in 'value', but still display
13712         // display field!!!!
13713         this.lastSelectionText = dv;
13714         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13715         this.value = vv;
13716         
13717         
13718         
13719     },
13720     // private
13721     reset : function(){
13722         // overridden so that last data is reset..
13723         
13724         if(this.multiple){
13725             this.clearItem();
13726             return;
13727         }
13728         
13729         this.setValue(this.originalValue);
13730         //this.clearInvalid();
13731         this.lastData = false;
13732         if (this.view) {
13733             this.view.clearSelections();
13734         }
13735         
13736         this.validate();
13737     },
13738     // private
13739     findRecord : function(prop, value){
13740         var record;
13741         if(this.store.getCount() > 0){
13742             this.store.each(function(r){
13743                 if(r.data[prop] == value){
13744                     record = r;
13745                     return false;
13746                 }
13747                 return true;
13748             });
13749         }
13750         return record;
13751     },
13752     
13753     getName: function()
13754     {
13755         // returns hidden if it's set..
13756         if (!this.rendered) {return ''};
13757         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13758         
13759     },
13760     // private
13761     onViewMove : function(e, t){
13762         this.inKeyMode = false;
13763     },
13764
13765     // private
13766     onViewOver : function(e, t){
13767         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13768             return;
13769         }
13770         var item = this.view.findItemFromChild(t);
13771         
13772         if(item){
13773             var index = this.view.indexOf(item);
13774             this.select(index, false);
13775         }
13776     },
13777
13778     // private
13779     onViewClick : function(view, doFocus, el, e)
13780     {
13781         var index = this.view.getSelectedIndexes()[0];
13782         
13783         var r = this.store.getAt(index);
13784         
13785         if(this.tickable){
13786             
13787             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13788                 return;
13789             }
13790             
13791             var rm = false;
13792             var _this = this;
13793             
13794             Roo.each(this.tickItems, function(v,k){
13795                 
13796                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13797                     Roo.log(v);
13798                     _this.tickItems.splice(k, 1);
13799                     
13800                     if(typeof(e) == 'undefined' && view == false){
13801                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13802                     }
13803                     
13804                     rm = true;
13805                     return;
13806                 }
13807             });
13808             
13809             if(rm){
13810                 return;
13811             }
13812             
13813             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13814                 this.tickItems.push(r.data);
13815             }
13816             
13817             if(typeof(e) == 'undefined' && view == false){
13818                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13819             }
13820                     
13821             return;
13822         }
13823         
13824         if(r){
13825             this.onSelect(r, index);
13826         }
13827         if(doFocus !== false && !this.blockFocus){
13828             this.inputEl().focus();
13829         }
13830     },
13831
13832     // private
13833     restrictHeight : function(){
13834         //this.innerList.dom.style.height = '';
13835         //var inner = this.innerList.dom;
13836         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13837         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13838         //this.list.beginUpdate();
13839         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13840         this.list.alignTo(this.inputEl(), this.listAlign);
13841         this.list.alignTo(this.inputEl(), this.listAlign);
13842         //this.list.endUpdate();
13843     },
13844
13845     // private
13846     onEmptyResults : function(){
13847         
13848         if(this.tickable && this.editable){
13849             this.restrictHeight();
13850             return;
13851         }
13852         
13853         this.collapse();
13854     },
13855
13856     /**
13857      * Returns true if the dropdown list is expanded, else false.
13858      */
13859     isExpanded : function(){
13860         return this.list.isVisible();
13861     },
13862
13863     /**
13864      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13865      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13866      * @param {String} value The data value of the item to select
13867      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13868      * selected item if it is not currently in view (defaults to true)
13869      * @return {Boolean} True if the value matched an item in the list, else false
13870      */
13871     selectByValue : function(v, scrollIntoView){
13872         if(v !== undefined && v !== null){
13873             var r = this.findRecord(this.valueField || this.displayField, v);
13874             if(r){
13875                 this.select(this.store.indexOf(r), scrollIntoView);
13876                 return true;
13877             }
13878         }
13879         return false;
13880     },
13881
13882     /**
13883      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13884      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13885      * @param {Number} index The zero-based index of the list item to select
13886      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13887      * selected item if it is not currently in view (defaults to true)
13888      */
13889     select : function(index, scrollIntoView){
13890         this.selectedIndex = index;
13891         this.view.select(index);
13892         if(scrollIntoView !== false){
13893             var el = this.view.getNode(index);
13894             /*
13895              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13896              */
13897             if(el){
13898                 this.list.scrollChildIntoView(el, false);
13899             }
13900         }
13901     },
13902
13903     // private
13904     selectNext : function(){
13905         var ct = this.store.getCount();
13906         if(ct > 0){
13907             if(this.selectedIndex == -1){
13908                 this.select(0);
13909             }else if(this.selectedIndex < ct-1){
13910                 this.select(this.selectedIndex+1);
13911             }
13912         }
13913     },
13914
13915     // private
13916     selectPrev : function(){
13917         var ct = this.store.getCount();
13918         if(ct > 0){
13919             if(this.selectedIndex == -1){
13920                 this.select(0);
13921             }else if(this.selectedIndex != 0){
13922                 this.select(this.selectedIndex-1);
13923             }
13924         }
13925     },
13926
13927     // private
13928     onKeyUp : function(e){
13929         if(this.editable !== false && !e.isSpecialKey()){
13930             this.lastKey = e.getKey();
13931             this.dqTask.delay(this.queryDelay);
13932         }
13933     },
13934
13935     // private
13936     validateBlur : function(){
13937         return !this.list || !this.list.isVisible();   
13938     },
13939
13940     // private
13941     initQuery : function(){
13942         
13943         var v = this.getRawValue();
13944         
13945         if(this.tickable && this.editable){
13946             v = this.tickableInputEl().getValue();
13947         }
13948         
13949         this.doQuery(v);
13950     },
13951
13952     // private
13953     doForce : function(){
13954         if(this.inputEl().dom.value.length > 0){
13955             this.inputEl().dom.value =
13956                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13957              
13958         }
13959     },
13960
13961     /**
13962      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13963      * query allowing the query action to be canceled if needed.
13964      * @param {String} query The SQL query to execute
13965      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13966      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13967      * saved in the current store (defaults to false)
13968      */
13969     doQuery : function(q, forceAll){
13970         
13971         if(q === undefined || q === null){
13972             q = '';
13973         }
13974         var qe = {
13975             query: q,
13976             forceAll: forceAll,
13977             combo: this,
13978             cancel:false
13979         };
13980         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13981             return false;
13982         }
13983         q = qe.query;
13984         
13985         forceAll = qe.forceAll;
13986         if(forceAll === true || (q.length >= this.minChars)){
13987             
13988             this.hasQuery = true;
13989             
13990             if(this.lastQuery != q || this.alwaysQuery){
13991                 this.lastQuery = q;
13992                 if(this.mode == 'local'){
13993                     this.selectedIndex = -1;
13994                     if(forceAll){
13995                         this.store.clearFilter();
13996                     }else{
13997                         
13998                         if(this.specialFilter){
13999                             this.fireEvent('specialfilter', this);
14000                             this.onLoad();
14001                             return;
14002                         }
14003                         
14004                         this.store.filter(this.displayField, q);
14005                     }
14006                     
14007                     this.store.fireEvent("datachanged", this.store);
14008                     
14009                     this.onLoad();
14010                     
14011                     
14012                 }else{
14013                     
14014                     this.store.baseParams[this.queryParam] = q;
14015                     
14016                     var options = {params : this.getParams(q)};
14017                     
14018                     if(this.loadNext){
14019                         options.add = true;
14020                         options.params.start = this.page * this.pageSize;
14021                     }
14022                     
14023                     this.store.load(options);
14024                     
14025                     /*
14026                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14027                      *  we should expand the list on onLoad
14028                      *  so command out it
14029                      */
14030 //                    this.expand();
14031                 }
14032             }else{
14033                 this.selectedIndex = -1;
14034                 this.onLoad();   
14035             }
14036         }
14037         
14038         this.loadNext = false;
14039     },
14040     
14041     // private
14042     getParams : function(q){
14043         var p = {};
14044         //p[this.queryParam] = q;
14045         
14046         if(this.pageSize){
14047             p.start = 0;
14048             p.limit = this.pageSize;
14049         }
14050         return p;
14051     },
14052
14053     /**
14054      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14055      */
14056     collapse : function(){
14057         if(!this.isExpanded()){
14058             return;
14059         }
14060         
14061         this.list.hide();
14062         
14063         this.hasFocus = false;
14064         
14065         if(this.tickable){
14066             this.okBtn.hide();
14067             this.cancelBtn.hide();
14068             this.trigger.show();
14069             
14070             if(this.editable){
14071                 this.tickableInputEl().dom.value = '';
14072                 this.tickableInputEl().blur();
14073             }
14074             
14075         }
14076         
14077         Roo.get(document).un('mousedown', this.collapseIf, this);
14078         Roo.get(document).un('mousewheel', this.collapseIf, this);
14079         if (!this.editable) {
14080             Roo.get(document).un('keydown', this.listKeyPress, this);
14081         }
14082         this.fireEvent('collapse', this);
14083         
14084         this.validate();
14085     },
14086
14087     // private
14088     collapseIf : function(e){
14089         var in_combo  = e.within(this.el);
14090         var in_list =  e.within(this.list);
14091         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14092         
14093         if (in_combo || in_list || is_list) {
14094             //e.stopPropagation();
14095             return;
14096         }
14097         
14098         if(this.tickable){
14099             this.onTickableFooterButtonClick(e, false, false);
14100         }
14101
14102         this.collapse();
14103         
14104     },
14105
14106     /**
14107      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14108      */
14109     expand : function(){
14110        
14111         if(this.isExpanded() || !this.hasFocus){
14112             return;
14113         }
14114         
14115         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14116         this.list.setWidth(lw);
14117         
14118         Roo.log('expand');
14119         
14120         this.list.show();
14121         
14122         this.restrictHeight();
14123         
14124         if(this.tickable){
14125             
14126             this.tickItems = Roo.apply([], this.item);
14127             
14128             this.okBtn.show();
14129             this.cancelBtn.show();
14130             this.trigger.hide();
14131             
14132             if(this.editable){
14133                 this.tickableInputEl().focus();
14134             }
14135             
14136         }
14137         
14138         Roo.get(document).on('mousedown', this.collapseIf, this);
14139         Roo.get(document).on('mousewheel', this.collapseIf, this);
14140         if (!this.editable) {
14141             Roo.get(document).on('keydown', this.listKeyPress, this);
14142         }
14143         
14144         this.fireEvent('expand', this);
14145     },
14146
14147     // private
14148     // Implements the default empty TriggerField.onTriggerClick function
14149     onTriggerClick : function(e)
14150     {
14151         Roo.log('trigger click');
14152         
14153         if(this.disabled || !this.triggerList){
14154             return;
14155         }
14156         
14157         this.page = 0;
14158         this.loadNext = false;
14159         
14160         if(this.isExpanded()){
14161             this.collapse();
14162             if (!this.blockFocus) {
14163                 this.inputEl().focus();
14164             }
14165             
14166         }else {
14167             this.hasFocus = true;
14168             if(this.triggerAction == 'all') {
14169                 this.doQuery(this.allQuery, true);
14170             } else {
14171                 this.doQuery(this.getRawValue());
14172             }
14173             if (!this.blockFocus) {
14174                 this.inputEl().focus();
14175             }
14176         }
14177     },
14178     
14179     onTickableTriggerClick : function(e)
14180     {
14181         if(this.disabled){
14182             return;
14183         }
14184         
14185         this.page = 0;
14186         this.loadNext = false;
14187         this.hasFocus = true;
14188         
14189         if(this.triggerAction == 'all') {
14190             this.doQuery(this.allQuery, true);
14191         } else {
14192             this.doQuery(this.getRawValue());
14193         }
14194     },
14195     
14196     onSearchFieldClick : function(e)
14197     {
14198         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14199             this.onTickableFooterButtonClick(e, false, false);
14200             return;
14201         }
14202         
14203         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14204             return;
14205         }
14206         
14207         this.page = 0;
14208         this.loadNext = false;
14209         this.hasFocus = true;
14210         
14211         if(this.triggerAction == 'all') {
14212             this.doQuery(this.allQuery, true);
14213         } else {
14214             this.doQuery(this.getRawValue());
14215         }
14216     },
14217     
14218     listKeyPress : function(e)
14219     {
14220         //Roo.log('listkeypress');
14221         // scroll to first matching element based on key pres..
14222         if (e.isSpecialKey()) {
14223             return false;
14224         }
14225         var k = String.fromCharCode(e.getKey()).toUpperCase();
14226         //Roo.log(k);
14227         var match  = false;
14228         var csel = this.view.getSelectedNodes();
14229         var cselitem = false;
14230         if (csel.length) {
14231             var ix = this.view.indexOf(csel[0]);
14232             cselitem  = this.store.getAt(ix);
14233             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14234                 cselitem = false;
14235             }
14236             
14237         }
14238         
14239         this.store.each(function(v) { 
14240             if (cselitem) {
14241                 // start at existing selection.
14242                 if (cselitem.id == v.id) {
14243                     cselitem = false;
14244                 }
14245                 return true;
14246             }
14247                 
14248             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14249                 match = this.store.indexOf(v);
14250                 return false;
14251             }
14252             return true;
14253         }, this);
14254         
14255         if (match === false) {
14256             return true; // no more action?
14257         }
14258         // scroll to?
14259         this.view.select(match);
14260         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14261         sn.scrollIntoView(sn.dom.parentNode, false);
14262     },
14263     
14264     onViewScroll : function(e, t){
14265         
14266         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){
14267             return;
14268         }
14269         
14270         this.hasQuery = true;
14271         
14272         this.loading = this.list.select('.loading', true).first();
14273         
14274         if(this.loading === null){
14275             this.list.createChild({
14276                 tag: 'div',
14277                 cls: 'loading roo-select2-more-results roo-select2-active',
14278                 html: 'Loading more results...'
14279             });
14280             
14281             this.loading = this.list.select('.loading', true).first();
14282             
14283             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14284             
14285             this.loading.hide();
14286         }
14287         
14288         this.loading.show();
14289         
14290         var _combo = this;
14291         
14292         this.page++;
14293         this.loadNext = true;
14294         
14295         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14296         
14297         return;
14298     },
14299     
14300     addItem : function(o)
14301     {   
14302         var dv = ''; // display value
14303         
14304         if (this.displayField) {
14305             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14306         } else {
14307             // this is an error condition!!!
14308             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14309         }
14310         
14311         if(!dv.length){
14312             return;
14313         }
14314         
14315         var choice = this.choices.createChild({
14316             tag: 'li',
14317             cls: 'roo-select2-search-choice',
14318             cn: [
14319                 {
14320                     tag: 'div',
14321                     html: dv
14322                 },
14323                 {
14324                     tag: 'a',
14325                     href: '#',
14326                     cls: 'roo-select2-search-choice-close fa fa-times',
14327                     tabindex: '-1'
14328                 }
14329             ]
14330             
14331         }, this.searchField);
14332         
14333         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14334         
14335         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14336         
14337         this.item.push(o);
14338         
14339         this.lastData = o;
14340         
14341         this.syncValue();
14342         
14343         this.inputEl().dom.value = '';
14344         
14345         this.validate();
14346     },
14347     
14348     onRemoveItem : function(e, _self, o)
14349     {
14350         e.preventDefault();
14351         
14352         this.lastItem = Roo.apply([], this.item);
14353         
14354         var index = this.item.indexOf(o.data) * 1;
14355         
14356         if( index < 0){
14357             Roo.log('not this item?!');
14358             return;
14359         }
14360         
14361         this.item.splice(index, 1);
14362         o.item.remove();
14363         
14364         this.syncValue();
14365         
14366         this.fireEvent('remove', this, e);
14367         
14368         this.validate();
14369         
14370     },
14371     
14372     syncValue : function()
14373     {
14374         if(!this.item.length){
14375             this.clearValue();
14376             return;
14377         }
14378             
14379         var value = [];
14380         var _this = this;
14381         Roo.each(this.item, function(i){
14382             if(_this.valueField){
14383                 value.push(i[_this.valueField]);
14384                 return;
14385             }
14386
14387             value.push(i);
14388         });
14389
14390         this.value = value.join(',');
14391
14392         if(this.hiddenField){
14393             this.hiddenField.dom.value = this.value;
14394         }
14395         
14396         this.store.fireEvent("datachanged", this.store);
14397         
14398         this.validate();
14399     },
14400     
14401     clearItem : function()
14402     {
14403         if(!this.multiple){
14404             return;
14405         }
14406         
14407         this.item = [];
14408         
14409         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14410            c.remove();
14411         });
14412         
14413         this.syncValue();
14414         
14415         this.validate();
14416         
14417         if(this.tickable && !Roo.isTouch){
14418             this.view.refresh();
14419         }
14420     },
14421     
14422     inputEl: function ()
14423     {
14424         if(Roo.isIOS && this.useNativeIOS){
14425             return this.el.select('select.roo-ios-select', true).first();
14426         }
14427         
14428         if(Roo.isTouch && this.mobileTouchView){
14429             return this.el.select('input.form-control',true).first();
14430         }
14431         
14432         if(this.tickable){
14433             return this.searchField;
14434         }
14435         
14436         return this.el.select('input.form-control',true).first();
14437     },
14438     
14439     onTickableFooterButtonClick : function(e, btn, el)
14440     {
14441         e.preventDefault();
14442         
14443         this.lastItem = Roo.apply([], this.item);
14444         
14445         if(btn && btn.name == 'cancel'){
14446             this.tickItems = Roo.apply([], this.item);
14447             this.collapse();
14448             return;
14449         }
14450         
14451         this.clearItem();
14452         
14453         var _this = this;
14454         
14455         Roo.each(this.tickItems, function(o){
14456             _this.addItem(o);
14457         });
14458         
14459         this.collapse();
14460         
14461     },
14462     
14463     validate : function()
14464     {
14465         var v = this.getRawValue();
14466         
14467         if(this.multiple){
14468             v = this.getValue();
14469         }
14470         
14471         if(this.disabled || this.allowBlank || v.length){
14472             this.markValid();
14473             return true;
14474         }
14475         
14476         this.markInvalid();
14477         return false;
14478     },
14479     
14480     tickableInputEl : function()
14481     {
14482         if(!this.tickable || !this.editable){
14483             return this.inputEl();
14484         }
14485         
14486         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14487     },
14488     
14489     
14490     getAutoCreateTouchView : function()
14491     {
14492         var id = Roo.id();
14493         
14494         var cfg = {
14495             cls: 'form-group' //input-group
14496         };
14497         
14498         var input =  {
14499             tag: 'input',
14500             id : id,
14501             type : this.inputType,
14502             cls : 'form-control x-combo-noedit',
14503             autocomplete: 'new-password',
14504             placeholder : this.placeholder || '',
14505             readonly : true
14506         };
14507         
14508         if (this.name) {
14509             input.name = this.name;
14510         }
14511         
14512         if (this.size) {
14513             input.cls += ' input-' + this.size;
14514         }
14515         
14516         if (this.disabled) {
14517             input.disabled = true;
14518         }
14519         
14520         var inputblock = {
14521             cls : '',
14522             cn : [
14523                 input
14524             ]
14525         };
14526         
14527         if(this.before){
14528             inputblock.cls += ' input-group';
14529             
14530             inputblock.cn.unshift({
14531                 tag :'span',
14532                 cls : 'input-group-addon',
14533                 html : this.before
14534             });
14535         }
14536         
14537         if(this.removable && !this.multiple){
14538             inputblock.cls += ' roo-removable';
14539             
14540             inputblock.cn.push({
14541                 tag: 'button',
14542                 html : 'x',
14543                 cls : 'roo-combo-removable-btn close'
14544             });
14545         }
14546
14547         if(this.hasFeedback && !this.allowBlank){
14548             
14549             inputblock.cls += ' has-feedback';
14550             
14551             inputblock.cn.push({
14552                 tag: 'span',
14553                 cls: 'glyphicon form-control-feedback'
14554             });
14555             
14556         }
14557         
14558         if (this.after) {
14559             
14560             inputblock.cls += (this.before) ? '' : ' input-group';
14561             
14562             inputblock.cn.push({
14563                 tag :'span',
14564                 cls : 'input-group-addon',
14565                 html : this.after
14566             });
14567         }
14568
14569         var box = {
14570             tag: 'div',
14571             cn: [
14572                 {
14573                     tag: 'input',
14574                     type : 'hidden',
14575                     cls: 'form-hidden-field'
14576                 },
14577                 inputblock
14578             ]
14579             
14580         };
14581         
14582         if(this.multiple){
14583             box = {
14584                 tag: 'div',
14585                 cn: [
14586                     {
14587                         tag: 'input',
14588                         type : 'hidden',
14589                         cls: 'form-hidden-field'
14590                     },
14591                     {
14592                         tag: 'ul',
14593                         cls: 'roo-select2-choices',
14594                         cn:[
14595                             {
14596                                 tag: 'li',
14597                                 cls: 'roo-select2-search-field',
14598                                 cn: [
14599
14600                                     inputblock
14601                                 ]
14602                             }
14603                         ]
14604                     }
14605                 ]
14606             }
14607         };
14608         
14609         var combobox = {
14610             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14611             cn: [
14612                 box
14613             ]
14614         };
14615         
14616         if(!this.multiple && this.showToggleBtn){
14617             
14618             var caret = {
14619                         tag: 'span',
14620                         cls: 'caret'
14621             };
14622             
14623             if (this.caret != false) {
14624                 caret = {
14625                      tag: 'i',
14626                      cls: 'fa fa-' + this.caret
14627                 };
14628                 
14629             }
14630             
14631             combobox.cn.push({
14632                 tag :'span',
14633                 cls : 'input-group-addon btn dropdown-toggle',
14634                 cn : [
14635                     caret,
14636                     {
14637                         tag: 'span',
14638                         cls: 'combobox-clear',
14639                         cn  : [
14640                             {
14641                                 tag : 'i',
14642                                 cls: 'icon-remove'
14643                             }
14644                         ]
14645                     }
14646                 ]
14647
14648             })
14649         }
14650         
14651         if(this.multiple){
14652             combobox.cls += ' roo-select2-container-multi';
14653         }
14654         
14655         var align = this.labelAlign || this.parentLabelAlign();
14656         
14657         if (align ==='left' && this.fieldLabel.length) {
14658
14659             cfg.cn = [
14660                 {
14661                    tag : 'i',
14662                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14663                    tooltip : 'This field is required'
14664                 },
14665                 {
14666                     tag: 'label',
14667                     cls : 'control-label',
14668                     html : this.fieldLabel
14669
14670                 },
14671                 {
14672                     cls : '', 
14673                     cn: [
14674                         combobox
14675                     ]
14676                 }
14677             ];
14678             
14679             var labelCfg = cfg.cn[1];
14680             var contentCfg = cfg.cn[2];
14681             
14682
14683             if(this.indicatorpos == 'right'){
14684                 cfg.cn = [
14685                     {
14686                         tag: 'label',
14687                         cls : 'control-label',
14688                         html : this.fieldLabel,
14689                         cn : [
14690                             {
14691                                tag : 'i',
14692                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14693                                tooltip : 'This field is required'
14694                             }
14695                         ]
14696                     },
14697                     {
14698                         cls : '', 
14699                         cn: [
14700                             combobox
14701                         ]
14702                     }
14703                 ];
14704             }
14705             
14706             labelCfg = cfg.cn[0];
14707             contentCfg = cfg.cn[2];
14708             
14709             if(this.labelWidth > 12){
14710                 labelCfg.style = "width: " + this.labelWidth + 'px';
14711             }
14712             
14713             if(this.labelWidth < 13 && this.labelmd == 0){
14714                 this.labelmd = this.labelWidth;
14715             }
14716             
14717             if(this.labellg > 0){
14718                 labelCfg.cls += ' col-lg-' + this.labellg;
14719                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14720             }
14721             
14722             if(this.labelmd > 0){
14723                 labelCfg.cls += ' col-md-' + this.labelmd;
14724                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14725             }
14726             
14727             if(this.labelsm > 0){
14728                 labelCfg.cls += ' col-sm-' + this.labelsm;
14729                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14730             }
14731             
14732             if(this.labelxs > 0){
14733                 labelCfg.cls += ' col-xs-' + this.labelxs;
14734                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14735             }
14736                 
14737                 
14738         } else if ( this.fieldLabel.length) {
14739             cfg.cn = [
14740                 {
14741                    tag : 'i',
14742                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14743                    tooltip : 'This field is required'
14744                 },
14745                 {
14746                     tag: 'label',
14747                     cls : 'control-label',
14748                     html : this.fieldLabel
14749
14750                 },
14751                 {
14752                     cls : '', 
14753                     cn: [
14754                         combobox
14755                     ]
14756                 }
14757             ];
14758             
14759             if(this.indicatorpos == 'right'){
14760                 cfg.cn = [
14761                     {
14762                         tag: 'label',
14763                         cls : 'control-label',
14764                         html : this.fieldLabel,
14765                         cn : [
14766                             {
14767                                tag : 'i',
14768                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14769                                tooltip : 'This field is required'
14770                             }
14771                         ]
14772                     },
14773                     {
14774                         cls : '', 
14775                         cn: [
14776                             combobox
14777                         ]
14778                     }
14779                 ];
14780             }
14781         } else {
14782             cfg.cn = combobox;    
14783         }
14784         
14785         
14786         var settings = this;
14787         
14788         ['xs','sm','md','lg'].map(function(size){
14789             if (settings[size]) {
14790                 cfg.cls += ' col-' + size + '-' + settings[size];
14791             }
14792         });
14793         
14794         return cfg;
14795     },
14796     
14797     initTouchView : function()
14798     {
14799         this.renderTouchView();
14800         
14801         this.touchViewEl.on('scroll', function(){
14802             this.el.dom.scrollTop = 0;
14803         }, this);
14804         
14805         this.originalValue = this.getValue();
14806         
14807         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14808         
14809         this.inputEl().on("click", this.showTouchView, this);
14810         if (this.triggerEl) {
14811             this.triggerEl.on("click", this.showTouchView, this);
14812         }
14813         
14814         
14815         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14816         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14817         
14818         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14819         
14820         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14821         this.store.on('load', this.onTouchViewLoad, this);
14822         this.store.on('loadexception', this.onTouchViewLoadException, this);
14823         
14824         if(this.hiddenName){
14825             
14826             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14827             
14828             this.hiddenField.dom.value =
14829                 this.hiddenValue !== undefined ? this.hiddenValue :
14830                 this.value !== undefined ? this.value : '';
14831         
14832             this.el.dom.removeAttribute('name');
14833             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14834         }
14835         
14836         if(this.multiple){
14837             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14838             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14839         }
14840         
14841         if(this.removable && !this.multiple){
14842             var close = this.closeTriggerEl();
14843             if(close){
14844                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14845                 close.on('click', this.removeBtnClick, this, close);
14846             }
14847         }
14848         /*
14849          * fix the bug in Safari iOS8
14850          */
14851         this.inputEl().on("focus", function(e){
14852             document.activeElement.blur();
14853         }, this);
14854         
14855         return;
14856         
14857         
14858     },
14859     
14860     renderTouchView : function()
14861     {
14862         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14863         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14864         
14865         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14866         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14867         
14868         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14869         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14870         this.touchViewBodyEl.setStyle('overflow', 'auto');
14871         
14872         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14873         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14874         
14875         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14876         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14877         
14878     },
14879     
14880     showTouchView : function()
14881     {
14882         if(this.disabled){
14883             return;
14884         }
14885         
14886         this.touchViewHeaderEl.hide();
14887
14888         if(this.modalTitle.length){
14889             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14890             this.touchViewHeaderEl.show();
14891         }
14892
14893         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14894         this.touchViewEl.show();
14895
14896         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14897         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14898                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14899
14900         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14901
14902         if(this.modalTitle.length){
14903             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14904         }
14905         
14906         this.touchViewBodyEl.setHeight(bodyHeight);
14907
14908         if(this.animate){
14909             var _this = this;
14910             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14911         }else{
14912             this.touchViewEl.addClass('in');
14913         }
14914
14915         this.doTouchViewQuery();
14916         
14917     },
14918     
14919     hideTouchView : function()
14920     {
14921         this.touchViewEl.removeClass('in');
14922
14923         if(this.animate){
14924             var _this = this;
14925             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14926         }else{
14927             this.touchViewEl.setStyle('display', 'none');
14928         }
14929         
14930     },
14931     
14932     setTouchViewValue : function()
14933     {
14934         if(this.multiple){
14935             this.clearItem();
14936         
14937             var _this = this;
14938
14939             Roo.each(this.tickItems, function(o){
14940                 this.addItem(o);
14941             }, this);
14942         }
14943         
14944         this.hideTouchView();
14945     },
14946     
14947     doTouchViewQuery : function()
14948     {
14949         var qe = {
14950             query: '',
14951             forceAll: true,
14952             combo: this,
14953             cancel:false
14954         };
14955         
14956         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14957             return false;
14958         }
14959         
14960         if(!this.alwaysQuery || this.mode == 'local'){
14961             this.onTouchViewLoad();
14962             return;
14963         }
14964         
14965         this.store.load();
14966     },
14967     
14968     onTouchViewBeforeLoad : function(combo,opts)
14969     {
14970         return;
14971     },
14972
14973     // private
14974     onTouchViewLoad : function()
14975     {
14976         if(this.store.getCount() < 1){
14977             this.onTouchViewEmptyResults();
14978             return;
14979         }
14980         
14981         this.clearTouchView();
14982         
14983         var rawValue = this.getRawValue();
14984         
14985         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14986         
14987         this.tickItems = [];
14988         
14989         this.store.data.each(function(d, rowIndex){
14990             var row = this.touchViewListGroup.createChild(template);
14991             
14992             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14993                 row.addClass(d.data.cls);
14994             }
14995             
14996             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14997                 var cfg = {
14998                     data : d.data,
14999                     html : d.data[this.displayField]
15000                 };
15001                 
15002                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15003                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15004                 }
15005             }
15006             row.removeClass('selected');
15007             if(!this.multiple && this.valueField &&
15008                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15009             {
15010                 // radio buttons..
15011                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15012                 row.addClass('selected');
15013             }
15014             
15015             if(this.multiple && this.valueField &&
15016                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15017             {
15018                 
15019                 // checkboxes...
15020                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15021                 this.tickItems.push(d.data);
15022             }
15023             
15024             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15025             
15026         }, this);
15027         
15028         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15029         
15030         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15031
15032         if(this.modalTitle.length){
15033             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15034         }
15035
15036         var listHeight = this.touchViewListGroup.getHeight();
15037         
15038         var _this = this;
15039         
15040         if(firstChecked && listHeight > bodyHeight){
15041             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15042         }
15043         
15044     },
15045     
15046     onTouchViewLoadException : function()
15047     {
15048         this.hideTouchView();
15049     },
15050     
15051     onTouchViewEmptyResults : function()
15052     {
15053         this.clearTouchView();
15054         
15055         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15056         
15057         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15058         
15059     },
15060     
15061     clearTouchView : function()
15062     {
15063         this.touchViewListGroup.dom.innerHTML = '';
15064     },
15065     
15066     onTouchViewClick : function(e, el, o)
15067     {
15068         e.preventDefault();
15069         
15070         var row = o.row;
15071         var rowIndex = o.rowIndex;
15072         
15073         var r = this.store.getAt(rowIndex);
15074         
15075         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15076             
15077             if(!this.multiple){
15078                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15079                     c.dom.removeAttribute('checked');
15080                 }, this);
15081
15082                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15083
15084                 this.setFromData(r.data);
15085
15086                 var close = this.closeTriggerEl();
15087
15088                 if(close){
15089                     close.show();
15090                 }
15091
15092                 this.hideTouchView();
15093
15094                 this.fireEvent('select', this, r, rowIndex);
15095
15096                 return;
15097             }
15098
15099             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15100                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15101                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15102                 return;
15103             }
15104
15105             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15106             this.addItem(r.data);
15107             this.tickItems.push(r.data);
15108         }
15109     },
15110     
15111     getAutoCreateNativeIOS : function()
15112     {
15113         var cfg = {
15114             cls: 'form-group' //input-group,
15115         };
15116         
15117         var combobox =  {
15118             tag: 'select',
15119             cls : 'roo-ios-select'
15120         };
15121         
15122         if (this.name) {
15123             combobox.name = this.name;
15124         }
15125         
15126         if (this.disabled) {
15127             combobox.disabled = true;
15128         }
15129         
15130         var settings = this;
15131         
15132         ['xs','sm','md','lg'].map(function(size){
15133             if (settings[size]) {
15134                 cfg.cls += ' col-' + size + '-' + settings[size];
15135             }
15136         });
15137         
15138         cfg.cn = combobox;
15139         
15140         return cfg;
15141         
15142     },
15143     
15144     initIOSView : function()
15145     {
15146         this.store.on('load', this.onIOSViewLoad, this);
15147         
15148         return;
15149     },
15150     
15151     onIOSViewLoad : function()
15152     {
15153         if(this.store.getCount() < 1){
15154             return;
15155         }
15156         
15157         this.clearIOSView();
15158         
15159         if(this.allowBlank) {
15160             
15161             var default_text = '-- SELECT --';
15162             
15163             var opt = this.inputEl().createChild({
15164                 tag: 'option',
15165                 value : 0,
15166                 html : default_text
15167             });
15168             
15169             var o = {};
15170             o[this.valueField] = 0;
15171             o[this.displayField] = default_text;
15172             
15173             this.ios_options.push({
15174                 data : o,
15175                 el : opt
15176             });
15177             
15178         }
15179         
15180         this.store.data.each(function(d, rowIndex){
15181             
15182             var html = '';
15183             
15184             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15185                 html = d.data[this.displayField];
15186             }
15187             
15188             var value = '';
15189             
15190             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15191                 value = d.data[this.valueField];
15192             }
15193             
15194             var option = {
15195                 tag: 'option',
15196                 value : value,
15197                 html : html
15198             };
15199             
15200             if(this.value == d.data[this.valueField]){
15201                 option['selected'] = true;
15202             }
15203             
15204             var opt = this.inputEl().createChild(option);
15205             
15206             this.ios_options.push({
15207                 data : d.data,
15208                 el : opt
15209             });
15210             
15211         }, this);
15212         
15213         this.inputEl().on('change', function(){
15214            this.fireEvent('select', this);
15215         }, this);
15216         
15217     },
15218     
15219     clearIOSView: function()
15220     {
15221         this.inputEl().dom.innerHTML = '';
15222         
15223         this.ios_options = [];
15224     },
15225     
15226     setIOSValue: function(v)
15227     {
15228         this.value = v;
15229         
15230         if(!this.ios_options){
15231             return;
15232         }
15233         
15234         Roo.each(this.ios_options, function(opts){
15235            
15236            opts.el.dom.removeAttribute('selected');
15237            
15238            if(opts.data[this.valueField] != v){
15239                return;
15240            }
15241            
15242            opts.el.dom.setAttribute('selected', true);
15243            
15244         }, this);
15245     }
15246
15247     /** 
15248     * @cfg {Boolean} grow 
15249     * @hide 
15250     */
15251     /** 
15252     * @cfg {Number} growMin 
15253     * @hide 
15254     */
15255     /** 
15256     * @cfg {Number} growMax 
15257     * @hide 
15258     */
15259     /**
15260      * @hide
15261      * @method autoSize
15262      */
15263 });
15264
15265 Roo.apply(Roo.bootstrap.ComboBox,  {
15266     
15267     header : {
15268         tag: 'div',
15269         cls: 'modal-header',
15270         cn: [
15271             {
15272                 tag: 'h4',
15273                 cls: 'modal-title'
15274             }
15275         ]
15276     },
15277     
15278     body : {
15279         tag: 'div',
15280         cls: 'modal-body',
15281         cn: [
15282             {
15283                 tag: 'ul',
15284                 cls: 'list-group'
15285             }
15286         ]
15287     },
15288     
15289     listItemRadio : {
15290         tag: 'li',
15291         cls: 'list-group-item',
15292         cn: [
15293             {
15294                 tag: 'span',
15295                 cls: 'roo-combobox-list-group-item-value'
15296             },
15297             {
15298                 tag: 'div',
15299                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15300                 cn: [
15301                     {
15302                         tag: 'input',
15303                         type: 'radio'
15304                     },
15305                     {
15306                         tag: 'label'
15307                     }
15308                 ]
15309             }
15310         ]
15311     },
15312     
15313     listItemCheckbox : {
15314         tag: 'li',
15315         cls: 'list-group-item',
15316         cn: [
15317             {
15318                 tag: 'span',
15319                 cls: 'roo-combobox-list-group-item-value'
15320             },
15321             {
15322                 tag: 'div',
15323                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15324                 cn: [
15325                     {
15326                         tag: 'input',
15327                         type: 'checkbox'
15328                     },
15329                     {
15330                         tag: 'label'
15331                     }
15332                 ]
15333             }
15334         ]
15335     },
15336     
15337     emptyResult : {
15338         tag: 'div',
15339         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15340     },
15341     
15342     footer : {
15343         tag: 'div',
15344         cls: 'modal-footer',
15345         cn: [
15346             {
15347                 tag: 'div',
15348                 cls: 'row',
15349                 cn: [
15350                     {
15351                         tag: 'div',
15352                         cls: 'col-xs-6 text-left',
15353                         cn: {
15354                             tag: 'button',
15355                             cls: 'btn btn-danger roo-touch-view-cancel',
15356                             html: 'Cancel'
15357                         }
15358                     },
15359                     {
15360                         tag: 'div',
15361                         cls: 'col-xs-6 text-right',
15362                         cn: {
15363                             tag: 'button',
15364                             cls: 'btn btn-success roo-touch-view-ok',
15365                             html: 'OK'
15366                         }
15367                     }
15368                 ]
15369             }
15370         ]
15371         
15372     }
15373 });
15374
15375 Roo.apply(Roo.bootstrap.ComboBox,  {
15376     
15377     touchViewTemplate : {
15378         tag: 'div',
15379         cls: 'modal fade roo-combobox-touch-view',
15380         cn: [
15381             {
15382                 tag: 'div',
15383                 cls: 'modal-dialog',
15384                 style : 'position:fixed', // we have to fix position....
15385                 cn: [
15386                     {
15387                         tag: 'div',
15388                         cls: 'modal-content',
15389                         cn: [
15390                             Roo.bootstrap.ComboBox.header,
15391                             Roo.bootstrap.ComboBox.body,
15392                             Roo.bootstrap.ComboBox.footer
15393                         ]
15394                     }
15395                 ]
15396             }
15397         ]
15398     }
15399 });/*
15400  * Based on:
15401  * Ext JS Library 1.1.1
15402  * Copyright(c) 2006-2007, Ext JS, LLC.
15403  *
15404  * Originally Released Under LGPL - original licence link has changed is not relivant.
15405  *
15406  * Fork - LGPL
15407  * <script type="text/javascript">
15408  */
15409
15410 /**
15411  * @class Roo.View
15412  * @extends Roo.util.Observable
15413  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15414  * This class also supports single and multi selection modes. <br>
15415  * Create a data model bound view:
15416  <pre><code>
15417  var store = new Roo.data.Store(...);
15418
15419  var view = new Roo.View({
15420     el : "my-element",
15421     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15422  
15423     singleSelect: true,
15424     selectedClass: "ydataview-selected",
15425     store: store
15426  });
15427
15428  // listen for node click?
15429  view.on("click", function(vw, index, node, e){
15430  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15431  });
15432
15433  // load XML data
15434  dataModel.load("foobar.xml");
15435  </code></pre>
15436  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15437  * <br><br>
15438  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15439  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15440  * 
15441  * Note: old style constructor is still suported (container, template, config)
15442  * 
15443  * @constructor
15444  * Create a new View
15445  * @param {Object} config The config object
15446  * 
15447  */
15448 Roo.View = function(config, depreciated_tpl, depreciated_config){
15449     
15450     this.parent = false;
15451     
15452     if (typeof(depreciated_tpl) == 'undefined') {
15453         // new way.. - universal constructor.
15454         Roo.apply(this, config);
15455         this.el  = Roo.get(this.el);
15456     } else {
15457         // old format..
15458         this.el  = Roo.get(config);
15459         this.tpl = depreciated_tpl;
15460         Roo.apply(this, depreciated_config);
15461     }
15462     this.wrapEl  = this.el.wrap().wrap();
15463     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15464     
15465     
15466     if(typeof(this.tpl) == "string"){
15467         this.tpl = new Roo.Template(this.tpl);
15468     } else {
15469         // support xtype ctors..
15470         this.tpl = new Roo.factory(this.tpl, Roo);
15471     }
15472     
15473     
15474     this.tpl.compile();
15475     
15476     /** @private */
15477     this.addEvents({
15478         /**
15479          * @event beforeclick
15480          * Fires before a click is processed. Returns false to cancel the default action.
15481          * @param {Roo.View} this
15482          * @param {Number} index The index of the target node
15483          * @param {HTMLElement} node The target node
15484          * @param {Roo.EventObject} e The raw event object
15485          */
15486             "beforeclick" : true,
15487         /**
15488          * @event click
15489          * Fires when a template node is clicked.
15490          * @param {Roo.View} this
15491          * @param {Number} index The index of the target node
15492          * @param {HTMLElement} node The target node
15493          * @param {Roo.EventObject} e The raw event object
15494          */
15495             "click" : true,
15496         /**
15497          * @event dblclick
15498          * Fires when a template node is double clicked.
15499          * @param {Roo.View} this
15500          * @param {Number} index The index of the target node
15501          * @param {HTMLElement} node The target node
15502          * @param {Roo.EventObject} e The raw event object
15503          */
15504             "dblclick" : true,
15505         /**
15506          * @event contextmenu
15507          * Fires when a template node is right clicked.
15508          * @param {Roo.View} this
15509          * @param {Number} index The index of the target node
15510          * @param {HTMLElement} node The target node
15511          * @param {Roo.EventObject} e The raw event object
15512          */
15513             "contextmenu" : true,
15514         /**
15515          * @event selectionchange
15516          * Fires when the selected nodes change.
15517          * @param {Roo.View} this
15518          * @param {Array} selections Array of the selected nodes
15519          */
15520             "selectionchange" : true,
15521     
15522         /**
15523          * @event beforeselect
15524          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15525          * @param {Roo.View} this
15526          * @param {HTMLElement} node The node to be selected
15527          * @param {Array} selections Array of currently selected nodes
15528          */
15529             "beforeselect" : true,
15530         /**
15531          * @event preparedata
15532          * Fires on every row to render, to allow you to change the data.
15533          * @param {Roo.View} this
15534          * @param {Object} data to be rendered (change this)
15535          */
15536           "preparedata" : true
15537           
15538           
15539         });
15540
15541
15542
15543     this.el.on({
15544         "click": this.onClick,
15545         "dblclick": this.onDblClick,
15546         "contextmenu": this.onContextMenu,
15547         scope:this
15548     });
15549
15550     this.selections = [];
15551     this.nodes = [];
15552     this.cmp = new Roo.CompositeElementLite([]);
15553     if(this.store){
15554         this.store = Roo.factory(this.store, Roo.data);
15555         this.setStore(this.store, true);
15556     }
15557     
15558     if ( this.footer && this.footer.xtype) {
15559            
15560          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15561         
15562         this.footer.dataSource = this.store;
15563         this.footer.container = fctr;
15564         this.footer = Roo.factory(this.footer, Roo);
15565         fctr.insertFirst(this.el);
15566         
15567         // this is a bit insane - as the paging toolbar seems to detach the el..
15568 //        dom.parentNode.parentNode.parentNode
15569          // they get detached?
15570     }
15571     
15572     
15573     Roo.View.superclass.constructor.call(this);
15574     
15575     
15576 };
15577
15578 Roo.extend(Roo.View, Roo.util.Observable, {
15579     
15580      /**
15581      * @cfg {Roo.data.Store} store Data store to load data from.
15582      */
15583     store : false,
15584     
15585     /**
15586      * @cfg {String|Roo.Element} el The container element.
15587      */
15588     el : '',
15589     
15590     /**
15591      * @cfg {String|Roo.Template} tpl The template used by this View 
15592      */
15593     tpl : false,
15594     /**
15595      * @cfg {String} dataName the named area of the template to use as the data area
15596      *                          Works with domtemplates roo-name="name"
15597      */
15598     dataName: false,
15599     /**
15600      * @cfg {String} selectedClass The css class to add to selected nodes
15601      */
15602     selectedClass : "x-view-selected",
15603      /**
15604      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15605      */
15606     emptyText : "",
15607     
15608     /**
15609      * @cfg {String} text to display on mask (default Loading)
15610      */
15611     mask : false,
15612     /**
15613      * @cfg {Boolean} multiSelect Allow multiple selection
15614      */
15615     multiSelect : false,
15616     /**
15617      * @cfg {Boolean} singleSelect Allow single selection
15618      */
15619     singleSelect:  false,
15620     
15621     /**
15622      * @cfg {Boolean} toggleSelect - selecting 
15623      */
15624     toggleSelect : false,
15625     
15626     /**
15627      * @cfg {Boolean} tickable - selecting 
15628      */
15629     tickable : false,
15630     
15631     /**
15632      * Returns the element this view is bound to.
15633      * @return {Roo.Element}
15634      */
15635     getEl : function(){
15636         return this.wrapEl;
15637     },
15638     
15639     
15640
15641     /**
15642      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15643      */
15644     refresh : function(){
15645         //Roo.log('refresh');
15646         var t = this.tpl;
15647         
15648         // if we are using something like 'domtemplate', then
15649         // the what gets used is:
15650         // t.applySubtemplate(NAME, data, wrapping data..)
15651         // the outer template then get' applied with
15652         //     the store 'extra data'
15653         // and the body get's added to the
15654         //      roo-name="data" node?
15655         //      <span class='roo-tpl-{name}'></span> ?????
15656         
15657         
15658         
15659         this.clearSelections();
15660         this.el.update("");
15661         var html = [];
15662         var records = this.store.getRange();
15663         if(records.length < 1) {
15664             
15665             // is this valid??  = should it render a template??
15666             
15667             this.el.update(this.emptyText);
15668             return;
15669         }
15670         var el = this.el;
15671         if (this.dataName) {
15672             this.el.update(t.apply(this.store.meta)); //????
15673             el = this.el.child('.roo-tpl-' + this.dataName);
15674         }
15675         
15676         for(var i = 0, len = records.length; i < len; i++){
15677             var data = this.prepareData(records[i].data, i, records[i]);
15678             this.fireEvent("preparedata", this, data, i, records[i]);
15679             
15680             var d = Roo.apply({}, data);
15681             
15682             if(this.tickable){
15683                 Roo.apply(d, {'roo-id' : Roo.id()});
15684                 
15685                 var _this = this;
15686             
15687                 Roo.each(this.parent.item, function(item){
15688                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15689                         return;
15690                     }
15691                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15692                 });
15693             }
15694             
15695             html[html.length] = Roo.util.Format.trim(
15696                 this.dataName ?
15697                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15698                     t.apply(d)
15699             );
15700         }
15701         
15702         
15703         
15704         el.update(html.join(""));
15705         this.nodes = el.dom.childNodes;
15706         this.updateIndexes(0);
15707     },
15708     
15709
15710     /**
15711      * Function to override to reformat the data that is sent to
15712      * the template for each node.
15713      * DEPRICATED - use the preparedata event handler.
15714      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15715      * a JSON object for an UpdateManager bound view).
15716      */
15717     prepareData : function(data, index, record)
15718     {
15719         this.fireEvent("preparedata", this, data, index, record);
15720         return data;
15721     },
15722
15723     onUpdate : function(ds, record){
15724         // Roo.log('on update');   
15725         this.clearSelections();
15726         var index = this.store.indexOf(record);
15727         var n = this.nodes[index];
15728         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15729         n.parentNode.removeChild(n);
15730         this.updateIndexes(index, index);
15731     },
15732
15733     
15734     
15735 // --------- FIXME     
15736     onAdd : function(ds, records, index)
15737     {
15738         //Roo.log(['on Add', ds, records, index] );        
15739         this.clearSelections();
15740         if(this.nodes.length == 0){
15741             this.refresh();
15742             return;
15743         }
15744         var n = this.nodes[index];
15745         for(var i = 0, len = records.length; i < len; i++){
15746             var d = this.prepareData(records[i].data, i, records[i]);
15747             if(n){
15748                 this.tpl.insertBefore(n, d);
15749             }else{
15750                 
15751                 this.tpl.append(this.el, d);
15752             }
15753         }
15754         this.updateIndexes(index);
15755     },
15756
15757     onRemove : function(ds, record, index){
15758        // Roo.log('onRemove');
15759         this.clearSelections();
15760         var el = this.dataName  ?
15761             this.el.child('.roo-tpl-' + this.dataName) :
15762             this.el; 
15763         
15764         el.dom.removeChild(this.nodes[index]);
15765         this.updateIndexes(index);
15766     },
15767
15768     /**
15769      * Refresh an individual node.
15770      * @param {Number} index
15771      */
15772     refreshNode : function(index){
15773         this.onUpdate(this.store, this.store.getAt(index));
15774     },
15775
15776     updateIndexes : function(startIndex, endIndex){
15777         var ns = this.nodes;
15778         startIndex = startIndex || 0;
15779         endIndex = endIndex || ns.length - 1;
15780         for(var i = startIndex; i <= endIndex; i++){
15781             ns[i].nodeIndex = i;
15782         }
15783     },
15784
15785     /**
15786      * Changes the data store this view uses and refresh the view.
15787      * @param {Store} store
15788      */
15789     setStore : function(store, initial){
15790         if(!initial && this.store){
15791             this.store.un("datachanged", this.refresh);
15792             this.store.un("add", this.onAdd);
15793             this.store.un("remove", this.onRemove);
15794             this.store.un("update", this.onUpdate);
15795             this.store.un("clear", this.refresh);
15796             this.store.un("beforeload", this.onBeforeLoad);
15797             this.store.un("load", this.onLoad);
15798             this.store.un("loadexception", this.onLoad);
15799         }
15800         if(store){
15801           
15802             store.on("datachanged", this.refresh, this);
15803             store.on("add", this.onAdd, this);
15804             store.on("remove", this.onRemove, this);
15805             store.on("update", this.onUpdate, this);
15806             store.on("clear", this.refresh, this);
15807             store.on("beforeload", this.onBeforeLoad, this);
15808             store.on("load", this.onLoad, this);
15809             store.on("loadexception", this.onLoad, this);
15810         }
15811         
15812         if(store){
15813             this.refresh();
15814         }
15815     },
15816     /**
15817      * onbeforeLoad - masks the loading area.
15818      *
15819      */
15820     onBeforeLoad : function(store,opts)
15821     {
15822          //Roo.log('onBeforeLoad');   
15823         if (!opts.add) {
15824             this.el.update("");
15825         }
15826         this.el.mask(this.mask ? this.mask : "Loading" ); 
15827     },
15828     onLoad : function ()
15829     {
15830         this.el.unmask();
15831     },
15832     
15833
15834     /**
15835      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15836      * @param {HTMLElement} node
15837      * @return {HTMLElement} The template node
15838      */
15839     findItemFromChild : function(node){
15840         var el = this.dataName  ?
15841             this.el.child('.roo-tpl-' + this.dataName,true) :
15842             this.el.dom; 
15843         
15844         if(!node || node.parentNode == el){
15845                     return node;
15846             }
15847             var p = node.parentNode;
15848             while(p && p != el){
15849             if(p.parentNode == el){
15850                 return p;
15851             }
15852             p = p.parentNode;
15853         }
15854             return null;
15855     },
15856
15857     /** @ignore */
15858     onClick : function(e){
15859         var item = this.findItemFromChild(e.getTarget());
15860         if(item){
15861             var index = this.indexOf(item);
15862             if(this.onItemClick(item, index, e) !== false){
15863                 this.fireEvent("click", this, index, item, e);
15864             }
15865         }else{
15866             this.clearSelections();
15867         }
15868     },
15869
15870     /** @ignore */
15871     onContextMenu : function(e){
15872         var item = this.findItemFromChild(e.getTarget());
15873         if(item){
15874             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15875         }
15876     },
15877
15878     /** @ignore */
15879     onDblClick : function(e){
15880         var item = this.findItemFromChild(e.getTarget());
15881         if(item){
15882             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15883         }
15884     },
15885
15886     onItemClick : function(item, index, e)
15887     {
15888         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15889             return false;
15890         }
15891         if (this.toggleSelect) {
15892             var m = this.isSelected(item) ? 'unselect' : 'select';
15893             //Roo.log(m);
15894             var _t = this;
15895             _t[m](item, true, false);
15896             return true;
15897         }
15898         if(this.multiSelect || this.singleSelect){
15899             if(this.multiSelect && e.shiftKey && this.lastSelection){
15900                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15901             }else{
15902                 this.select(item, this.multiSelect && e.ctrlKey);
15903                 this.lastSelection = item;
15904             }
15905             
15906             if(!this.tickable){
15907                 e.preventDefault();
15908             }
15909             
15910         }
15911         return true;
15912     },
15913
15914     /**
15915      * Get the number of selected nodes.
15916      * @return {Number}
15917      */
15918     getSelectionCount : function(){
15919         return this.selections.length;
15920     },
15921
15922     /**
15923      * Get the currently selected nodes.
15924      * @return {Array} An array of HTMLElements
15925      */
15926     getSelectedNodes : function(){
15927         return this.selections;
15928     },
15929
15930     /**
15931      * Get the indexes of the selected nodes.
15932      * @return {Array}
15933      */
15934     getSelectedIndexes : function(){
15935         var indexes = [], s = this.selections;
15936         for(var i = 0, len = s.length; i < len; i++){
15937             indexes.push(s[i].nodeIndex);
15938         }
15939         return indexes;
15940     },
15941
15942     /**
15943      * Clear all selections
15944      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15945      */
15946     clearSelections : function(suppressEvent){
15947         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15948             this.cmp.elements = this.selections;
15949             this.cmp.removeClass(this.selectedClass);
15950             this.selections = [];
15951             if(!suppressEvent){
15952                 this.fireEvent("selectionchange", this, this.selections);
15953             }
15954         }
15955     },
15956
15957     /**
15958      * Returns true if the passed node is selected
15959      * @param {HTMLElement/Number} node The node or node index
15960      * @return {Boolean}
15961      */
15962     isSelected : function(node){
15963         var s = this.selections;
15964         if(s.length < 1){
15965             return false;
15966         }
15967         node = this.getNode(node);
15968         return s.indexOf(node) !== -1;
15969     },
15970
15971     /**
15972      * Selects nodes.
15973      * @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
15974      * @param {Boolean} keepExisting (optional) true to keep existing selections
15975      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15976      */
15977     select : function(nodeInfo, keepExisting, suppressEvent){
15978         if(nodeInfo instanceof Array){
15979             if(!keepExisting){
15980                 this.clearSelections(true);
15981             }
15982             for(var i = 0, len = nodeInfo.length; i < len; i++){
15983                 this.select(nodeInfo[i], true, true);
15984             }
15985             return;
15986         } 
15987         var node = this.getNode(nodeInfo);
15988         if(!node || this.isSelected(node)){
15989             return; // already selected.
15990         }
15991         if(!keepExisting){
15992             this.clearSelections(true);
15993         }
15994         
15995         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15996             Roo.fly(node).addClass(this.selectedClass);
15997             this.selections.push(node);
15998             if(!suppressEvent){
15999                 this.fireEvent("selectionchange", this, this.selections);
16000             }
16001         }
16002         
16003         
16004     },
16005       /**
16006      * Unselects nodes.
16007      * @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
16008      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16009      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16010      */
16011     unselect : function(nodeInfo, keepExisting, suppressEvent)
16012     {
16013         if(nodeInfo instanceof Array){
16014             Roo.each(this.selections, function(s) {
16015                 this.unselect(s, nodeInfo);
16016             }, this);
16017             return;
16018         }
16019         var node = this.getNode(nodeInfo);
16020         if(!node || !this.isSelected(node)){
16021             //Roo.log("not selected");
16022             return; // not selected.
16023         }
16024         // fireevent???
16025         var ns = [];
16026         Roo.each(this.selections, function(s) {
16027             if (s == node ) {
16028                 Roo.fly(node).removeClass(this.selectedClass);
16029
16030                 return;
16031             }
16032             ns.push(s);
16033         },this);
16034         
16035         this.selections= ns;
16036         this.fireEvent("selectionchange", this, this.selections);
16037     },
16038
16039     /**
16040      * Gets a template node.
16041      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16042      * @return {HTMLElement} The node or null if it wasn't found
16043      */
16044     getNode : function(nodeInfo){
16045         if(typeof nodeInfo == "string"){
16046             return document.getElementById(nodeInfo);
16047         }else if(typeof nodeInfo == "number"){
16048             return this.nodes[nodeInfo];
16049         }
16050         return nodeInfo;
16051     },
16052
16053     /**
16054      * Gets a range template nodes.
16055      * @param {Number} startIndex
16056      * @param {Number} endIndex
16057      * @return {Array} An array of nodes
16058      */
16059     getNodes : function(start, end){
16060         var ns = this.nodes;
16061         start = start || 0;
16062         end = typeof end == "undefined" ? ns.length - 1 : end;
16063         var nodes = [];
16064         if(start <= end){
16065             for(var i = start; i <= end; i++){
16066                 nodes.push(ns[i]);
16067             }
16068         } else{
16069             for(var i = start; i >= end; i--){
16070                 nodes.push(ns[i]);
16071             }
16072         }
16073         return nodes;
16074     },
16075
16076     /**
16077      * Finds the index of the passed node
16078      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16079      * @return {Number} The index of the node or -1
16080      */
16081     indexOf : function(node){
16082         node = this.getNode(node);
16083         if(typeof node.nodeIndex == "number"){
16084             return node.nodeIndex;
16085         }
16086         var ns = this.nodes;
16087         for(var i = 0, len = ns.length; i < len; i++){
16088             if(ns[i] == node){
16089                 return i;
16090             }
16091         }
16092         return -1;
16093     }
16094 });
16095 /*
16096  * - LGPL
16097  *
16098  * based on jquery fullcalendar
16099  * 
16100  */
16101
16102 Roo.bootstrap = Roo.bootstrap || {};
16103 /**
16104  * @class Roo.bootstrap.Calendar
16105  * @extends Roo.bootstrap.Component
16106  * Bootstrap Calendar class
16107  * @cfg {Boolean} loadMask (true|false) default false
16108  * @cfg {Object} header generate the user specific header of the calendar, default false
16109
16110  * @constructor
16111  * Create a new Container
16112  * @param {Object} config The config object
16113  */
16114
16115
16116
16117 Roo.bootstrap.Calendar = function(config){
16118     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16119      this.addEvents({
16120         /**
16121              * @event select
16122              * Fires when a date is selected
16123              * @param {DatePicker} this
16124              * @param {Date} date The selected date
16125              */
16126         'select': true,
16127         /**
16128              * @event monthchange
16129              * Fires when the displayed month changes 
16130              * @param {DatePicker} this
16131              * @param {Date} date The selected month
16132              */
16133         'monthchange': true,
16134         /**
16135              * @event evententer
16136              * Fires when mouse over an event
16137              * @param {Calendar} this
16138              * @param {event} Event
16139              */
16140         'evententer': true,
16141         /**
16142              * @event eventleave
16143              * Fires when the mouse leaves an
16144              * @param {Calendar} this
16145              * @param {event}
16146              */
16147         'eventleave': true,
16148         /**
16149              * @event eventclick
16150              * Fires when the mouse click an
16151              * @param {Calendar} this
16152              * @param {event}
16153              */
16154         'eventclick': true
16155         
16156     });
16157
16158 };
16159
16160 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16161     
16162      /**
16163      * @cfg {Number} startDay
16164      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16165      */
16166     startDay : 0,
16167     
16168     loadMask : false,
16169     
16170     header : false,
16171       
16172     getAutoCreate : function(){
16173         
16174         
16175         var fc_button = function(name, corner, style, content ) {
16176             return Roo.apply({},{
16177                 tag : 'span',
16178                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16179                          (corner.length ?
16180                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16181                             ''
16182                         ),
16183                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16184                 unselectable: 'on'
16185             });
16186         };
16187         
16188         var header = {};
16189         
16190         if(!this.header){
16191             header = {
16192                 tag : 'table',
16193                 cls : 'fc-header',
16194                 style : 'width:100%',
16195                 cn : [
16196                     {
16197                         tag: 'tr',
16198                         cn : [
16199                             {
16200                                 tag : 'td',
16201                                 cls : 'fc-header-left',
16202                                 cn : [
16203                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16204                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16205                                     { tag: 'span', cls: 'fc-header-space' },
16206                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16207
16208
16209                                 ]
16210                             },
16211
16212                             {
16213                                 tag : 'td',
16214                                 cls : 'fc-header-center',
16215                                 cn : [
16216                                     {
16217                                         tag: 'span',
16218                                         cls: 'fc-header-title',
16219                                         cn : {
16220                                             tag: 'H2',
16221                                             html : 'month / year'
16222                                         }
16223                                     }
16224
16225                                 ]
16226                             },
16227                             {
16228                                 tag : 'td',
16229                                 cls : 'fc-header-right',
16230                                 cn : [
16231                               /*      fc_button('month', 'left', '', 'month' ),
16232                                     fc_button('week', '', '', 'week' ),
16233                                     fc_button('day', 'right', '', 'day' )
16234                                 */    
16235
16236                                 ]
16237                             }
16238
16239                         ]
16240                     }
16241                 ]
16242             };
16243         }
16244         
16245         header = this.header;
16246         
16247        
16248         var cal_heads = function() {
16249             var ret = [];
16250             // fixme - handle this.
16251             
16252             for (var i =0; i < Date.dayNames.length; i++) {
16253                 var d = Date.dayNames[i];
16254                 ret.push({
16255                     tag: 'th',
16256                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16257                     html : d.substring(0,3)
16258                 });
16259                 
16260             }
16261             ret[0].cls += ' fc-first';
16262             ret[6].cls += ' fc-last';
16263             return ret;
16264         };
16265         var cal_cell = function(n) {
16266             return  {
16267                 tag: 'td',
16268                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16269                 cn : [
16270                     {
16271                         cn : [
16272                             {
16273                                 cls: 'fc-day-number',
16274                                 html: 'D'
16275                             },
16276                             {
16277                                 cls: 'fc-day-content',
16278                              
16279                                 cn : [
16280                                      {
16281                                         style: 'position: relative;' // height: 17px;
16282                                     }
16283                                 ]
16284                             }
16285                             
16286                             
16287                         ]
16288                     }
16289                 ]
16290                 
16291             }
16292         };
16293         var cal_rows = function() {
16294             
16295             var ret = [];
16296             for (var r = 0; r < 6; r++) {
16297                 var row= {
16298                     tag : 'tr',
16299                     cls : 'fc-week',
16300                     cn : []
16301                 };
16302                 
16303                 for (var i =0; i < Date.dayNames.length; i++) {
16304                     var d = Date.dayNames[i];
16305                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16306
16307                 }
16308                 row.cn[0].cls+=' fc-first';
16309                 row.cn[0].cn[0].style = 'min-height:90px';
16310                 row.cn[6].cls+=' fc-last';
16311                 ret.push(row);
16312                 
16313             }
16314             ret[0].cls += ' fc-first';
16315             ret[4].cls += ' fc-prev-last';
16316             ret[5].cls += ' fc-last';
16317             return ret;
16318             
16319         };
16320         
16321         var cal_table = {
16322             tag: 'table',
16323             cls: 'fc-border-separate',
16324             style : 'width:100%',
16325             cellspacing  : 0,
16326             cn : [
16327                 { 
16328                     tag: 'thead',
16329                     cn : [
16330                         { 
16331                             tag: 'tr',
16332                             cls : 'fc-first fc-last',
16333                             cn : cal_heads()
16334                         }
16335                     ]
16336                 },
16337                 { 
16338                     tag: 'tbody',
16339                     cn : cal_rows()
16340                 }
16341                   
16342             ]
16343         };
16344          
16345          var cfg = {
16346             cls : 'fc fc-ltr',
16347             cn : [
16348                 header,
16349                 {
16350                     cls : 'fc-content',
16351                     style : "position: relative;",
16352                     cn : [
16353                         {
16354                             cls : 'fc-view fc-view-month fc-grid',
16355                             style : 'position: relative',
16356                             unselectable : 'on',
16357                             cn : [
16358                                 {
16359                                     cls : 'fc-event-container',
16360                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16361                                 },
16362                                 cal_table
16363                             ]
16364                         }
16365                     ]
16366     
16367                 }
16368            ] 
16369             
16370         };
16371         
16372          
16373         
16374         return cfg;
16375     },
16376     
16377     
16378     initEvents : function()
16379     {
16380         if(!this.store){
16381             throw "can not find store for calendar";
16382         }
16383         
16384         var mark = {
16385             tag: "div",
16386             cls:"x-dlg-mask",
16387             style: "text-align:center",
16388             cn: [
16389                 {
16390                     tag: "div",
16391                     style: "background-color:white;width:50%;margin:250 auto",
16392                     cn: [
16393                         {
16394                             tag: "img",
16395                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16396                         },
16397                         {
16398                             tag: "span",
16399                             html: "Loading"
16400                         }
16401                         
16402                     ]
16403                 }
16404             ]
16405         };
16406         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16407         
16408         var size = this.el.select('.fc-content', true).first().getSize();
16409         this.maskEl.setSize(size.width, size.height);
16410         this.maskEl.enableDisplayMode("block");
16411         if(!this.loadMask){
16412             this.maskEl.hide();
16413         }
16414         
16415         this.store = Roo.factory(this.store, Roo.data);
16416         this.store.on('load', this.onLoad, this);
16417         this.store.on('beforeload', this.onBeforeLoad, this);
16418         
16419         this.resize();
16420         
16421         this.cells = this.el.select('.fc-day',true);
16422         //Roo.log(this.cells);
16423         this.textNodes = this.el.query('.fc-day-number');
16424         this.cells.addClassOnOver('fc-state-hover');
16425         
16426         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16427         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16428         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16429         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16430         
16431         this.on('monthchange', this.onMonthChange, this);
16432         
16433         this.update(new Date().clearTime());
16434     },
16435     
16436     resize : function() {
16437         var sz  = this.el.getSize();
16438         
16439         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16440         this.el.select('.fc-day-content div',true).setHeight(34);
16441     },
16442     
16443     
16444     // private
16445     showPrevMonth : function(e){
16446         this.update(this.activeDate.add("mo", -1));
16447     },
16448     showToday : function(e){
16449         this.update(new Date().clearTime());
16450     },
16451     // private
16452     showNextMonth : function(e){
16453         this.update(this.activeDate.add("mo", 1));
16454     },
16455
16456     // private
16457     showPrevYear : function(){
16458         this.update(this.activeDate.add("y", -1));
16459     },
16460
16461     // private
16462     showNextYear : function(){
16463         this.update(this.activeDate.add("y", 1));
16464     },
16465
16466     
16467    // private
16468     update : function(date)
16469     {
16470         var vd = this.activeDate;
16471         this.activeDate = date;
16472 //        if(vd && this.el){
16473 //            var t = date.getTime();
16474 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16475 //                Roo.log('using add remove');
16476 //                
16477 //                this.fireEvent('monthchange', this, date);
16478 //                
16479 //                this.cells.removeClass("fc-state-highlight");
16480 //                this.cells.each(function(c){
16481 //                   if(c.dateValue == t){
16482 //                       c.addClass("fc-state-highlight");
16483 //                       setTimeout(function(){
16484 //                            try{c.dom.firstChild.focus();}catch(e){}
16485 //                       }, 50);
16486 //                       return false;
16487 //                   }
16488 //                   return true;
16489 //                });
16490 //                return;
16491 //            }
16492 //        }
16493         
16494         var days = date.getDaysInMonth();
16495         
16496         var firstOfMonth = date.getFirstDateOfMonth();
16497         var startingPos = firstOfMonth.getDay()-this.startDay;
16498         
16499         if(startingPos < this.startDay){
16500             startingPos += 7;
16501         }
16502         
16503         var pm = date.add(Date.MONTH, -1);
16504         var prevStart = pm.getDaysInMonth()-startingPos;
16505 //        
16506         this.cells = this.el.select('.fc-day',true);
16507         this.textNodes = this.el.query('.fc-day-number');
16508         this.cells.addClassOnOver('fc-state-hover');
16509         
16510         var cells = this.cells.elements;
16511         var textEls = this.textNodes;
16512         
16513         Roo.each(cells, function(cell){
16514             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16515         });
16516         
16517         days += startingPos;
16518
16519         // convert everything to numbers so it's fast
16520         var day = 86400000;
16521         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16522         //Roo.log(d);
16523         //Roo.log(pm);
16524         //Roo.log(prevStart);
16525         
16526         var today = new Date().clearTime().getTime();
16527         var sel = date.clearTime().getTime();
16528         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16529         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16530         var ddMatch = this.disabledDatesRE;
16531         var ddText = this.disabledDatesText;
16532         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16533         var ddaysText = this.disabledDaysText;
16534         var format = this.format;
16535         
16536         var setCellClass = function(cal, cell){
16537             cell.row = 0;
16538             cell.events = [];
16539             cell.more = [];
16540             //Roo.log('set Cell Class');
16541             cell.title = "";
16542             var t = d.getTime();
16543             
16544             //Roo.log(d);
16545             
16546             cell.dateValue = t;
16547             if(t == today){
16548                 cell.className += " fc-today";
16549                 cell.className += " fc-state-highlight";
16550                 cell.title = cal.todayText;
16551             }
16552             if(t == sel){
16553                 // disable highlight in other month..
16554                 //cell.className += " fc-state-highlight";
16555                 
16556             }
16557             // disabling
16558             if(t < min) {
16559                 cell.className = " fc-state-disabled";
16560                 cell.title = cal.minText;
16561                 return;
16562             }
16563             if(t > max) {
16564                 cell.className = " fc-state-disabled";
16565                 cell.title = cal.maxText;
16566                 return;
16567             }
16568             if(ddays){
16569                 if(ddays.indexOf(d.getDay()) != -1){
16570                     cell.title = ddaysText;
16571                     cell.className = " fc-state-disabled";
16572                 }
16573             }
16574             if(ddMatch && format){
16575                 var fvalue = d.dateFormat(format);
16576                 if(ddMatch.test(fvalue)){
16577                     cell.title = ddText.replace("%0", fvalue);
16578                     cell.className = " fc-state-disabled";
16579                 }
16580             }
16581             
16582             if (!cell.initialClassName) {
16583                 cell.initialClassName = cell.dom.className;
16584             }
16585             
16586             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16587         };
16588
16589         var i = 0;
16590         
16591         for(; i < startingPos; i++) {
16592             textEls[i].innerHTML = (++prevStart);
16593             d.setDate(d.getDate()+1);
16594             
16595             cells[i].className = "fc-past fc-other-month";
16596             setCellClass(this, cells[i]);
16597         }
16598         
16599         var intDay = 0;
16600         
16601         for(; i < days; i++){
16602             intDay = i - startingPos + 1;
16603             textEls[i].innerHTML = (intDay);
16604             d.setDate(d.getDate()+1);
16605             
16606             cells[i].className = ''; // "x-date-active";
16607             setCellClass(this, cells[i]);
16608         }
16609         var extraDays = 0;
16610         
16611         for(; i < 42; i++) {
16612             textEls[i].innerHTML = (++extraDays);
16613             d.setDate(d.getDate()+1);
16614             
16615             cells[i].className = "fc-future fc-other-month";
16616             setCellClass(this, cells[i]);
16617         }
16618         
16619         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16620         
16621         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16622         
16623         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16624         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16625         
16626         if(totalRows != 6){
16627             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16628             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16629         }
16630         
16631         this.fireEvent('monthchange', this, date);
16632         
16633         
16634         /*
16635         if(!this.internalRender){
16636             var main = this.el.dom.firstChild;
16637             var w = main.offsetWidth;
16638             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16639             Roo.fly(main).setWidth(w);
16640             this.internalRender = true;
16641             // opera does not respect the auto grow header center column
16642             // then, after it gets a width opera refuses to recalculate
16643             // without a second pass
16644             if(Roo.isOpera && !this.secondPass){
16645                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16646                 this.secondPass = true;
16647                 this.update.defer(10, this, [date]);
16648             }
16649         }
16650         */
16651         
16652     },
16653     
16654     findCell : function(dt) {
16655         dt = dt.clearTime().getTime();
16656         var ret = false;
16657         this.cells.each(function(c){
16658             //Roo.log("check " +c.dateValue + '?=' + dt);
16659             if(c.dateValue == dt){
16660                 ret = c;
16661                 return false;
16662             }
16663             return true;
16664         });
16665         
16666         return ret;
16667     },
16668     
16669     findCells : function(ev) {
16670         var s = ev.start.clone().clearTime().getTime();
16671        // Roo.log(s);
16672         var e= ev.end.clone().clearTime().getTime();
16673        // Roo.log(e);
16674         var ret = [];
16675         this.cells.each(function(c){
16676              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16677             
16678             if(c.dateValue > e){
16679                 return ;
16680             }
16681             if(c.dateValue < s){
16682                 return ;
16683             }
16684             ret.push(c);
16685         });
16686         
16687         return ret;    
16688     },
16689     
16690 //    findBestRow: function(cells)
16691 //    {
16692 //        var ret = 0;
16693 //        
16694 //        for (var i =0 ; i < cells.length;i++) {
16695 //            ret  = Math.max(cells[i].rows || 0,ret);
16696 //        }
16697 //        return ret;
16698 //        
16699 //    },
16700     
16701     
16702     addItem : function(ev)
16703     {
16704         // look for vertical location slot in
16705         var cells = this.findCells(ev);
16706         
16707 //        ev.row = this.findBestRow(cells);
16708         
16709         // work out the location.
16710         
16711         var crow = false;
16712         var rows = [];
16713         for(var i =0; i < cells.length; i++) {
16714             
16715             cells[i].row = cells[0].row;
16716             
16717             if(i == 0){
16718                 cells[i].row = cells[i].row + 1;
16719             }
16720             
16721             if (!crow) {
16722                 crow = {
16723                     start : cells[i],
16724                     end :  cells[i]
16725                 };
16726                 continue;
16727             }
16728             if (crow.start.getY() == cells[i].getY()) {
16729                 // on same row.
16730                 crow.end = cells[i];
16731                 continue;
16732             }
16733             // different row.
16734             rows.push(crow);
16735             crow = {
16736                 start: cells[i],
16737                 end : cells[i]
16738             };
16739             
16740         }
16741         
16742         rows.push(crow);
16743         ev.els = [];
16744         ev.rows = rows;
16745         ev.cells = cells;
16746         
16747         cells[0].events.push(ev);
16748         
16749         this.calevents.push(ev);
16750     },
16751     
16752     clearEvents: function() {
16753         
16754         if(!this.calevents){
16755             return;
16756         }
16757         
16758         Roo.each(this.cells.elements, function(c){
16759             c.row = 0;
16760             c.events = [];
16761             c.more = [];
16762         });
16763         
16764         Roo.each(this.calevents, function(e) {
16765             Roo.each(e.els, function(el) {
16766                 el.un('mouseenter' ,this.onEventEnter, this);
16767                 el.un('mouseleave' ,this.onEventLeave, this);
16768                 el.remove();
16769             },this);
16770         },this);
16771         
16772         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16773             e.remove();
16774         });
16775         
16776     },
16777     
16778     renderEvents: function()
16779     {   
16780         var _this = this;
16781         
16782         this.cells.each(function(c) {
16783             
16784             if(c.row < 5){
16785                 return;
16786             }
16787             
16788             var ev = c.events;
16789             
16790             var r = 4;
16791             if(c.row != c.events.length){
16792                 r = 4 - (4 - (c.row - c.events.length));
16793             }
16794             
16795             c.events = ev.slice(0, r);
16796             c.more = ev.slice(r);
16797             
16798             if(c.more.length && c.more.length == 1){
16799                 c.events.push(c.more.pop());
16800             }
16801             
16802             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16803             
16804         });
16805             
16806         this.cells.each(function(c) {
16807             
16808             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16809             
16810             
16811             for (var e = 0; e < c.events.length; e++){
16812                 var ev = c.events[e];
16813                 var rows = ev.rows;
16814                 
16815                 for(var i = 0; i < rows.length; i++) {
16816                 
16817                     // how many rows should it span..
16818
16819                     var  cfg = {
16820                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16821                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16822
16823                         unselectable : "on",
16824                         cn : [
16825                             {
16826                                 cls: 'fc-event-inner',
16827                                 cn : [
16828     //                                {
16829     //                                  tag:'span',
16830     //                                  cls: 'fc-event-time',
16831     //                                  html : cells.length > 1 ? '' : ev.time
16832     //                                },
16833                                     {
16834                                       tag:'span',
16835                                       cls: 'fc-event-title',
16836                                       html : String.format('{0}', ev.title)
16837                                     }
16838
16839
16840                                 ]
16841                             },
16842                             {
16843                                 cls: 'ui-resizable-handle ui-resizable-e',
16844                                 html : '&nbsp;&nbsp;&nbsp'
16845                             }
16846
16847                         ]
16848                     };
16849
16850                     if (i == 0) {
16851                         cfg.cls += ' fc-event-start';
16852                     }
16853                     if ((i+1) == rows.length) {
16854                         cfg.cls += ' fc-event-end';
16855                     }
16856
16857                     var ctr = _this.el.select('.fc-event-container',true).first();
16858                     var cg = ctr.createChild(cfg);
16859
16860                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16861                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16862
16863                     var r = (c.more.length) ? 1 : 0;
16864                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16865                     cg.setWidth(ebox.right - sbox.x -2);
16866
16867                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16868                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16869                     cg.on('click', _this.onEventClick, _this, ev);
16870
16871                     ev.els.push(cg);
16872                     
16873                 }
16874                 
16875             }
16876             
16877             
16878             if(c.more.length){
16879                 var  cfg = {
16880                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16881                     style : 'position: absolute',
16882                     unselectable : "on",
16883                     cn : [
16884                         {
16885                             cls: 'fc-event-inner',
16886                             cn : [
16887                                 {
16888                                   tag:'span',
16889                                   cls: 'fc-event-title',
16890                                   html : 'More'
16891                                 }
16892
16893
16894                             ]
16895                         },
16896                         {
16897                             cls: 'ui-resizable-handle ui-resizable-e',
16898                             html : '&nbsp;&nbsp;&nbsp'
16899                         }
16900
16901                     ]
16902                 };
16903
16904                 var ctr = _this.el.select('.fc-event-container',true).first();
16905                 var cg = ctr.createChild(cfg);
16906
16907                 var sbox = c.select('.fc-day-content',true).first().getBox();
16908                 var ebox = c.select('.fc-day-content',true).first().getBox();
16909                 //Roo.log(cg);
16910                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16911                 cg.setWidth(ebox.right - sbox.x -2);
16912
16913                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16914                 
16915             }
16916             
16917         });
16918         
16919         
16920         
16921     },
16922     
16923     onEventEnter: function (e, el,event,d) {
16924         this.fireEvent('evententer', this, el, event);
16925     },
16926     
16927     onEventLeave: function (e, el,event,d) {
16928         this.fireEvent('eventleave', this, el, event);
16929     },
16930     
16931     onEventClick: function (e, el,event,d) {
16932         this.fireEvent('eventclick', this, el, event);
16933     },
16934     
16935     onMonthChange: function () {
16936         this.store.load();
16937     },
16938     
16939     onMoreEventClick: function(e, el, more)
16940     {
16941         var _this = this;
16942         
16943         this.calpopover.placement = 'right';
16944         this.calpopover.setTitle('More');
16945         
16946         this.calpopover.setContent('');
16947         
16948         var ctr = this.calpopover.el.select('.popover-content', true).first();
16949         
16950         Roo.each(more, function(m){
16951             var cfg = {
16952                 cls : 'fc-event-hori fc-event-draggable',
16953                 html : m.title
16954             };
16955             var cg = ctr.createChild(cfg);
16956             
16957             cg.on('click', _this.onEventClick, _this, m);
16958         });
16959         
16960         this.calpopover.show(el);
16961         
16962         
16963     },
16964     
16965     onLoad: function () 
16966     {   
16967         this.calevents = [];
16968         var cal = this;
16969         
16970         if(this.store.getCount() > 0){
16971             this.store.data.each(function(d){
16972                cal.addItem({
16973                     id : d.data.id,
16974                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16975                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16976                     time : d.data.start_time,
16977                     title : d.data.title,
16978                     description : d.data.description,
16979                     venue : d.data.venue
16980                 });
16981             });
16982         }
16983         
16984         this.renderEvents();
16985         
16986         if(this.calevents.length && this.loadMask){
16987             this.maskEl.hide();
16988         }
16989     },
16990     
16991     onBeforeLoad: function()
16992     {
16993         this.clearEvents();
16994         if(this.loadMask){
16995             this.maskEl.show();
16996         }
16997     }
16998 });
16999
17000  
17001  /*
17002  * - LGPL
17003  *
17004  * element
17005  * 
17006  */
17007
17008 /**
17009  * @class Roo.bootstrap.Popover
17010  * @extends Roo.bootstrap.Component
17011  * Bootstrap Popover class
17012  * @cfg {String} html contents of the popover   (or false to use children..)
17013  * @cfg {String} title of popover (or false to hide)
17014  * @cfg {String} placement how it is placed
17015  * @cfg {String} trigger click || hover (or false to trigger manually)
17016  * @cfg {String} over what (parent or false to trigger manually.)
17017  * @cfg {Number} delay - delay before showing
17018  
17019  * @constructor
17020  * Create a new Popover
17021  * @param {Object} config The config object
17022  */
17023
17024 Roo.bootstrap.Popover = function(config){
17025     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17026     
17027     this.addEvents({
17028         // raw events
17029          /**
17030          * @event show
17031          * After the popover show
17032          * 
17033          * @param {Roo.bootstrap.Popover} this
17034          */
17035         "show" : true,
17036         /**
17037          * @event hide
17038          * After the popover hide
17039          * 
17040          * @param {Roo.bootstrap.Popover} this
17041          */
17042         "hide" : true
17043     });
17044 };
17045
17046 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17047     
17048     title: 'Fill in a title',
17049     html: false,
17050     
17051     placement : 'right',
17052     trigger : 'hover', // hover
17053     
17054     delay : 0,
17055     
17056     over: 'parent',
17057     
17058     can_build_overlaid : false,
17059     
17060     getChildContainer : function()
17061     {
17062         return this.el.select('.popover-content',true).first();
17063     },
17064     
17065     getAutoCreate : function(){
17066          
17067         var cfg = {
17068            cls : 'popover roo-dynamic',
17069            style: 'display:block',
17070            cn : [
17071                 {
17072                     cls : 'arrow'
17073                 },
17074                 {
17075                     cls : 'popover-inner',
17076                     cn : [
17077                         {
17078                             tag: 'h3',
17079                             cls: 'popover-title',
17080                             html : this.title
17081                         },
17082                         {
17083                             cls : 'popover-content',
17084                             html : this.html
17085                         }
17086                     ]
17087                     
17088                 }
17089            ]
17090         };
17091         
17092         return cfg;
17093     },
17094     setTitle: function(str)
17095     {
17096         this.title = str;
17097         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17098     },
17099     setContent: function(str)
17100     {
17101         this.html = str;
17102         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17103     },
17104     // as it get's added to the bottom of the page.
17105     onRender : function(ct, position)
17106     {
17107         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17108         if(!this.el){
17109             var cfg = Roo.apply({},  this.getAutoCreate());
17110             cfg.id = Roo.id();
17111             
17112             if (this.cls) {
17113                 cfg.cls += ' ' + this.cls;
17114             }
17115             if (this.style) {
17116                 cfg.style = this.style;
17117             }
17118             //Roo.log("adding to ");
17119             this.el = Roo.get(document.body).createChild(cfg, position);
17120 //            Roo.log(this.el);
17121         }
17122         this.initEvents();
17123     },
17124     
17125     initEvents : function()
17126     {
17127         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17128         this.el.enableDisplayMode('block');
17129         this.el.hide();
17130         if (this.over === false) {
17131             return; 
17132         }
17133         if (this.triggers === false) {
17134             return;
17135         }
17136         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17137         var triggers = this.trigger ? this.trigger.split(' ') : [];
17138         Roo.each(triggers, function(trigger) {
17139         
17140             if (trigger == 'click') {
17141                 on_el.on('click', this.toggle, this);
17142             } else if (trigger != 'manual') {
17143                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17144                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17145       
17146                 on_el.on(eventIn  ,this.enter, this);
17147                 on_el.on(eventOut, this.leave, this);
17148             }
17149         }, this);
17150         
17151     },
17152     
17153     
17154     // private
17155     timeout : null,
17156     hoverState : null,
17157     
17158     toggle : function () {
17159         this.hoverState == 'in' ? this.leave() : this.enter();
17160     },
17161     
17162     enter : function () {
17163         
17164         clearTimeout(this.timeout);
17165     
17166         this.hoverState = 'in';
17167     
17168         if (!this.delay || !this.delay.show) {
17169             this.show();
17170             return;
17171         }
17172         var _t = this;
17173         this.timeout = setTimeout(function () {
17174             if (_t.hoverState == 'in') {
17175                 _t.show();
17176             }
17177         }, this.delay.show)
17178     },
17179     
17180     leave : function() {
17181         clearTimeout(this.timeout);
17182     
17183         this.hoverState = 'out';
17184     
17185         if (!this.delay || !this.delay.hide) {
17186             this.hide();
17187             return;
17188         }
17189         var _t = this;
17190         this.timeout = setTimeout(function () {
17191             if (_t.hoverState == 'out') {
17192                 _t.hide();
17193             }
17194         }, this.delay.hide)
17195     },
17196     
17197     show : function (on_el)
17198     {
17199         if (!on_el) {
17200             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17201         }
17202         
17203         // set content.
17204         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17205         if (this.html !== false) {
17206             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17207         }
17208         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17209         if (!this.title.length) {
17210             this.el.select('.popover-title',true).hide();
17211         }
17212         
17213         var placement = typeof this.placement == 'function' ?
17214             this.placement.call(this, this.el, on_el) :
17215             this.placement;
17216             
17217         var autoToken = /\s?auto?\s?/i;
17218         var autoPlace = autoToken.test(placement);
17219         if (autoPlace) {
17220             placement = placement.replace(autoToken, '') || 'top';
17221         }
17222         
17223         //this.el.detach()
17224         //this.el.setXY([0,0]);
17225         this.el.show();
17226         this.el.dom.style.display='block';
17227         this.el.addClass(placement);
17228         
17229         //this.el.appendTo(on_el);
17230         
17231         var p = this.getPosition();
17232         var box = this.el.getBox();
17233         
17234         if (autoPlace) {
17235             // fixme..
17236         }
17237         var align = Roo.bootstrap.Popover.alignment[placement];
17238         this.el.alignTo(on_el, align[0],align[1]);
17239         //var arrow = this.el.select('.arrow',true).first();
17240         //arrow.set(align[2], 
17241         
17242         this.el.addClass('in');
17243         
17244         
17245         if (this.el.hasClass('fade')) {
17246             // fade it?
17247         }
17248         
17249         this.hoverState = 'in';
17250         
17251         this.fireEvent('show', this);
17252         
17253     },
17254     hide : function()
17255     {
17256         this.el.setXY([0,0]);
17257         this.el.removeClass('in');
17258         this.el.hide();
17259         this.hoverState = null;
17260         
17261         this.fireEvent('hide', this);
17262     }
17263     
17264 });
17265
17266 Roo.bootstrap.Popover.alignment = {
17267     'left' : ['r-l', [-10,0], 'right'],
17268     'right' : ['l-r', [10,0], 'left'],
17269     'bottom' : ['t-b', [0,10], 'top'],
17270     'top' : [ 'b-t', [0,-10], 'bottom']
17271 };
17272
17273  /*
17274  * - LGPL
17275  *
17276  * Progress
17277  * 
17278  */
17279
17280 /**
17281  * @class Roo.bootstrap.Progress
17282  * @extends Roo.bootstrap.Component
17283  * Bootstrap Progress class
17284  * @cfg {Boolean} striped striped of the progress bar
17285  * @cfg {Boolean} active animated of the progress bar
17286  * 
17287  * 
17288  * @constructor
17289  * Create a new Progress
17290  * @param {Object} config The config object
17291  */
17292
17293 Roo.bootstrap.Progress = function(config){
17294     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17295 };
17296
17297 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17298     
17299     striped : false,
17300     active: false,
17301     
17302     getAutoCreate : function(){
17303         var cfg = {
17304             tag: 'div',
17305             cls: 'progress'
17306         };
17307         
17308         
17309         if(this.striped){
17310             cfg.cls += ' progress-striped';
17311         }
17312       
17313         if(this.active){
17314             cfg.cls += ' active';
17315         }
17316         
17317         
17318         return cfg;
17319     }
17320    
17321 });
17322
17323  
17324
17325  /*
17326  * - LGPL
17327  *
17328  * ProgressBar
17329  * 
17330  */
17331
17332 /**
17333  * @class Roo.bootstrap.ProgressBar
17334  * @extends Roo.bootstrap.Component
17335  * Bootstrap ProgressBar class
17336  * @cfg {Number} aria_valuenow aria-value now
17337  * @cfg {Number} aria_valuemin aria-value min
17338  * @cfg {Number} aria_valuemax aria-value max
17339  * @cfg {String} label label for the progress bar
17340  * @cfg {String} panel (success | info | warning | danger )
17341  * @cfg {String} role role of the progress bar
17342  * @cfg {String} sr_only text
17343  * 
17344  * 
17345  * @constructor
17346  * Create a new ProgressBar
17347  * @param {Object} config The config object
17348  */
17349
17350 Roo.bootstrap.ProgressBar = function(config){
17351     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17352 };
17353
17354 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17355     
17356     aria_valuenow : 0,
17357     aria_valuemin : 0,
17358     aria_valuemax : 100,
17359     label : false,
17360     panel : false,
17361     role : false,
17362     sr_only: false,
17363     
17364     getAutoCreate : function()
17365     {
17366         
17367         var cfg = {
17368             tag: 'div',
17369             cls: 'progress-bar',
17370             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17371         };
17372         
17373         if(this.sr_only){
17374             cfg.cn = {
17375                 tag: 'span',
17376                 cls: 'sr-only',
17377                 html: this.sr_only
17378             }
17379         }
17380         
17381         if(this.role){
17382             cfg.role = this.role;
17383         }
17384         
17385         if(this.aria_valuenow){
17386             cfg['aria-valuenow'] = this.aria_valuenow;
17387         }
17388         
17389         if(this.aria_valuemin){
17390             cfg['aria-valuemin'] = this.aria_valuemin;
17391         }
17392         
17393         if(this.aria_valuemax){
17394             cfg['aria-valuemax'] = this.aria_valuemax;
17395         }
17396         
17397         if(this.label && !this.sr_only){
17398             cfg.html = this.label;
17399         }
17400         
17401         if(this.panel){
17402             cfg.cls += ' progress-bar-' + this.panel;
17403         }
17404         
17405         return cfg;
17406     },
17407     
17408     update : function(aria_valuenow)
17409     {
17410         this.aria_valuenow = aria_valuenow;
17411         
17412         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17413     }
17414    
17415 });
17416
17417  
17418
17419  /*
17420  * - LGPL
17421  *
17422  * column
17423  * 
17424  */
17425
17426 /**
17427  * @class Roo.bootstrap.TabGroup
17428  * @extends Roo.bootstrap.Column
17429  * Bootstrap Column class
17430  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17431  * @cfg {Boolean} carousel true to make the group behave like a carousel
17432  * @cfg {Boolean} bullets show bullets for the panels
17433  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17434  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17435  * @cfg {Boolean} showarrow (true|false) show arrow default true
17436  * 
17437  * @constructor
17438  * Create a new TabGroup
17439  * @param {Object} config The config object
17440  */
17441
17442 Roo.bootstrap.TabGroup = function(config){
17443     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17444     if (!this.navId) {
17445         this.navId = Roo.id();
17446     }
17447     this.tabs = [];
17448     Roo.bootstrap.TabGroup.register(this);
17449     
17450 };
17451
17452 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17453     
17454     carousel : false,
17455     transition : false,
17456     bullets : 0,
17457     timer : 0,
17458     autoslide : false,
17459     slideFn : false,
17460     slideOnTouch : false,
17461     showarrow : true,
17462     
17463     getAutoCreate : function()
17464     {
17465         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17466         
17467         cfg.cls += ' tab-content';
17468         
17469         if (this.carousel) {
17470             cfg.cls += ' carousel slide';
17471             
17472             cfg.cn = [{
17473                cls : 'carousel-inner',
17474                cn : []
17475             }];
17476         
17477             if(this.bullets  && !Roo.isTouch){
17478                 
17479                 var bullets = {
17480                     cls : 'carousel-bullets',
17481                     cn : []
17482                 };
17483                
17484                 if(this.bullets_cls){
17485                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17486                 }
17487                 
17488                 bullets.cn.push({
17489                     cls : 'clear'
17490                 });
17491                 
17492                 cfg.cn[0].cn.push(bullets);
17493             }
17494             
17495             if(this.showarrow){
17496                 cfg.cn[0].cn.push({
17497                     tag : 'div',
17498                     class : 'carousel-arrow',
17499                     cn : [
17500                         {
17501                             tag : 'div',
17502                             class : 'carousel-prev',
17503                             cn : [
17504                                 {
17505                                     tag : 'i',
17506                                     class : 'fa fa-chevron-left'
17507                                 }
17508                             ]
17509                         },
17510                         {
17511                             tag : 'div',
17512                             class : 'carousel-next',
17513                             cn : [
17514                                 {
17515                                     tag : 'i',
17516                                     class : 'fa fa-chevron-right'
17517                                 }
17518                             ]
17519                         }
17520                     ]
17521                 });
17522             }
17523             
17524         }
17525         
17526         return cfg;
17527     },
17528     
17529     initEvents:  function()
17530     {
17531 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17532 //            this.el.on("touchstart", this.onTouchStart, this);
17533 //        }
17534         
17535         if(this.autoslide){
17536             var _this = this;
17537             
17538             this.slideFn = window.setInterval(function() {
17539                 _this.showPanelNext();
17540             }, this.timer);
17541         }
17542         
17543         if(this.showarrow){
17544             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17545             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17546         }
17547         
17548         
17549     },
17550     
17551 //    onTouchStart : function(e, el, o)
17552 //    {
17553 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17554 //            return;
17555 //        }
17556 //        
17557 //        this.showPanelNext();
17558 //    },
17559     
17560     
17561     getChildContainer : function()
17562     {
17563         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17564     },
17565     
17566     /**
17567     * register a Navigation item
17568     * @param {Roo.bootstrap.NavItem} the navitem to add
17569     */
17570     register : function(item)
17571     {
17572         this.tabs.push( item);
17573         item.navId = this.navId; // not really needed..
17574         this.addBullet();
17575     
17576     },
17577     
17578     getActivePanel : function()
17579     {
17580         var r = false;
17581         Roo.each(this.tabs, function(t) {
17582             if (t.active) {
17583                 r = t;
17584                 return false;
17585             }
17586             return null;
17587         });
17588         return r;
17589         
17590     },
17591     getPanelByName : function(n)
17592     {
17593         var r = false;
17594         Roo.each(this.tabs, function(t) {
17595             if (t.tabId == n) {
17596                 r = t;
17597                 return false;
17598             }
17599             return null;
17600         });
17601         return r;
17602     },
17603     indexOfPanel : function(p)
17604     {
17605         var r = false;
17606         Roo.each(this.tabs, function(t,i) {
17607             if (t.tabId == p.tabId) {
17608                 r = i;
17609                 return false;
17610             }
17611             return null;
17612         });
17613         return r;
17614     },
17615     /**
17616      * show a specific panel
17617      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17618      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17619      */
17620     showPanel : function (pan)
17621     {
17622         if(this.transition || typeof(pan) == 'undefined'){
17623             Roo.log("waiting for the transitionend");
17624             return;
17625         }
17626         
17627         if (typeof(pan) == 'number') {
17628             pan = this.tabs[pan];
17629         }
17630         
17631         if (typeof(pan) == 'string') {
17632             pan = this.getPanelByName(pan);
17633         }
17634         
17635         var cur = this.getActivePanel();
17636         
17637         if(!pan || !cur){
17638             Roo.log('pan or acitve pan is undefined');
17639             return false;
17640         }
17641         
17642         if (pan.tabId == this.getActivePanel().tabId) {
17643             return true;
17644         }
17645         
17646         if (false === cur.fireEvent('beforedeactivate')) {
17647             return false;
17648         }
17649         
17650         if(this.bullets > 0 && !Roo.isTouch){
17651             this.setActiveBullet(this.indexOfPanel(pan));
17652         }
17653         
17654         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17655             
17656             this.transition = true;
17657             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17658             var lr = dir == 'next' ? 'left' : 'right';
17659             pan.el.addClass(dir); // or prev
17660             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17661             cur.el.addClass(lr); // or right
17662             pan.el.addClass(lr);
17663             
17664             var _this = this;
17665             cur.el.on('transitionend', function() {
17666                 Roo.log("trans end?");
17667                 
17668                 pan.el.removeClass([lr,dir]);
17669                 pan.setActive(true);
17670                 
17671                 cur.el.removeClass([lr]);
17672                 cur.setActive(false);
17673                 
17674                 _this.transition = false;
17675                 
17676             }, this, { single:  true } );
17677             
17678             return true;
17679         }
17680         
17681         cur.setActive(false);
17682         pan.setActive(true);
17683         
17684         return true;
17685         
17686     },
17687     showPanelNext : function()
17688     {
17689         var i = this.indexOfPanel(this.getActivePanel());
17690         
17691         if (i >= this.tabs.length - 1 && !this.autoslide) {
17692             return;
17693         }
17694         
17695         if (i >= this.tabs.length - 1 && this.autoslide) {
17696             i = -1;
17697         }
17698         
17699         this.showPanel(this.tabs[i+1]);
17700     },
17701     
17702     showPanelPrev : function()
17703     {
17704         var i = this.indexOfPanel(this.getActivePanel());
17705         
17706         if (i  < 1 && !this.autoslide) {
17707             return;
17708         }
17709         
17710         if (i < 1 && this.autoslide) {
17711             i = this.tabs.length;
17712         }
17713         
17714         this.showPanel(this.tabs[i-1]);
17715     },
17716     
17717     
17718     addBullet: function()
17719     {
17720         if(!this.bullets || Roo.isTouch){
17721             return;
17722         }
17723         var ctr = this.el.select('.carousel-bullets',true).first();
17724         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17725         var bullet = ctr.createChild({
17726             cls : 'bullet bullet-' + i
17727         },ctr.dom.lastChild);
17728         
17729         
17730         var _this = this;
17731         
17732         bullet.on('click', (function(e, el, o, ii, t){
17733
17734             e.preventDefault();
17735
17736             this.showPanel(ii);
17737
17738             if(this.autoslide && this.slideFn){
17739                 clearInterval(this.slideFn);
17740                 this.slideFn = window.setInterval(function() {
17741                     _this.showPanelNext();
17742                 }, this.timer);
17743             }
17744
17745         }).createDelegate(this, [i, bullet], true));
17746                 
17747         
17748     },
17749      
17750     setActiveBullet : function(i)
17751     {
17752         if(Roo.isTouch){
17753             return;
17754         }
17755         
17756         Roo.each(this.el.select('.bullet', true).elements, function(el){
17757             el.removeClass('selected');
17758         });
17759
17760         var bullet = this.el.select('.bullet-' + i, true).first();
17761         
17762         if(!bullet){
17763             return;
17764         }
17765         
17766         bullet.addClass('selected');
17767     }
17768     
17769     
17770   
17771 });
17772
17773  
17774
17775  
17776  
17777 Roo.apply(Roo.bootstrap.TabGroup, {
17778     
17779     groups: {},
17780      /**
17781     * register a Navigation Group
17782     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17783     */
17784     register : function(navgrp)
17785     {
17786         this.groups[navgrp.navId] = navgrp;
17787         
17788     },
17789     /**
17790     * fetch a Navigation Group based on the navigation ID
17791     * if one does not exist , it will get created.
17792     * @param {string} the navgroup to add
17793     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17794     */
17795     get: function(navId) {
17796         if (typeof(this.groups[navId]) == 'undefined') {
17797             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17798         }
17799         return this.groups[navId] ;
17800     }
17801     
17802     
17803     
17804 });
17805
17806  /*
17807  * - LGPL
17808  *
17809  * TabPanel
17810  * 
17811  */
17812
17813 /**
17814  * @class Roo.bootstrap.TabPanel
17815  * @extends Roo.bootstrap.Component
17816  * Bootstrap TabPanel class
17817  * @cfg {Boolean} active panel active
17818  * @cfg {String} html panel content
17819  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17820  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17821  * @cfg {String} href click to link..
17822  * 
17823  * 
17824  * @constructor
17825  * Create a new TabPanel
17826  * @param {Object} config The config object
17827  */
17828
17829 Roo.bootstrap.TabPanel = function(config){
17830     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17831     this.addEvents({
17832         /**
17833              * @event changed
17834              * Fires when the active status changes
17835              * @param {Roo.bootstrap.TabPanel} this
17836              * @param {Boolean} state the new state
17837             
17838          */
17839         'changed': true,
17840         /**
17841              * @event beforedeactivate
17842              * Fires before a tab is de-activated - can be used to do validation on a form.
17843              * @param {Roo.bootstrap.TabPanel} this
17844              * @return {Boolean} false if there is an error
17845             
17846          */
17847         'beforedeactivate': true
17848      });
17849     
17850     this.tabId = this.tabId || Roo.id();
17851   
17852 };
17853
17854 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17855     
17856     active: false,
17857     html: false,
17858     tabId: false,
17859     navId : false,
17860     href : '',
17861     
17862     getAutoCreate : function(){
17863         var cfg = {
17864             tag: 'div',
17865             // item is needed for carousel - not sure if it has any effect otherwise
17866             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17867             html: this.html || ''
17868         };
17869         
17870         if(this.active){
17871             cfg.cls += ' active';
17872         }
17873         
17874         if(this.tabId){
17875             cfg.tabId = this.tabId;
17876         }
17877         
17878         
17879         return cfg;
17880     },
17881     
17882     initEvents:  function()
17883     {
17884         var p = this.parent();
17885         
17886         this.navId = this.navId || p.navId;
17887         
17888         if (typeof(this.navId) != 'undefined') {
17889             // not really needed.. but just in case.. parent should be a NavGroup.
17890             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17891             
17892             tg.register(this);
17893             
17894             var i = tg.tabs.length - 1;
17895             
17896             if(this.active && tg.bullets > 0 && i < tg.bullets){
17897                 tg.setActiveBullet(i);
17898             }
17899         }
17900         
17901         this.el.on('click', this.onClick, this);
17902         
17903         if(Roo.isTouch){
17904             this.el.on("touchstart", this.onTouchStart, this);
17905             this.el.on("touchmove", this.onTouchMove, this);
17906             this.el.on("touchend", this.onTouchEnd, this);
17907         }
17908         
17909     },
17910     
17911     onRender : function(ct, position)
17912     {
17913         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17914     },
17915     
17916     setActive : function(state)
17917     {
17918         Roo.log("panel - set active " + this.tabId + "=" + state);
17919         
17920         this.active = state;
17921         if (!state) {
17922             this.el.removeClass('active');
17923             
17924         } else  if (!this.el.hasClass('active')) {
17925             this.el.addClass('active');
17926         }
17927         
17928         this.fireEvent('changed', this, state);
17929     },
17930     
17931     onClick : function(e)
17932     {
17933         e.preventDefault();
17934         
17935         if(!this.href.length){
17936             return;
17937         }
17938         
17939         window.location.href = this.href;
17940     },
17941     
17942     startX : 0,
17943     startY : 0,
17944     endX : 0,
17945     endY : 0,
17946     swiping : false,
17947     
17948     onTouchStart : function(e)
17949     {
17950         this.swiping = false;
17951         
17952         this.startX = e.browserEvent.touches[0].clientX;
17953         this.startY = e.browserEvent.touches[0].clientY;
17954     },
17955     
17956     onTouchMove : function(e)
17957     {
17958         this.swiping = true;
17959         
17960         this.endX = e.browserEvent.touches[0].clientX;
17961         this.endY = e.browserEvent.touches[0].clientY;
17962     },
17963     
17964     onTouchEnd : function(e)
17965     {
17966         if(!this.swiping){
17967             this.onClick(e);
17968             return;
17969         }
17970         
17971         var tabGroup = this.parent();
17972         
17973         if(this.endX > this.startX){ // swiping right
17974             tabGroup.showPanelPrev();
17975             return;
17976         }
17977         
17978         if(this.startX > this.endX){ // swiping left
17979             tabGroup.showPanelNext();
17980             return;
17981         }
17982     }
17983     
17984     
17985 });
17986  
17987
17988  
17989
17990  /*
17991  * - LGPL
17992  *
17993  * DateField
17994  * 
17995  */
17996
17997 /**
17998  * @class Roo.bootstrap.DateField
17999  * @extends Roo.bootstrap.Input
18000  * Bootstrap DateField class
18001  * @cfg {Number} weekStart default 0
18002  * @cfg {String} viewMode default empty, (months|years)
18003  * @cfg {String} minViewMode default empty, (months|years)
18004  * @cfg {Number} startDate default -Infinity
18005  * @cfg {Number} endDate default Infinity
18006  * @cfg {Boolean} todayHighlight default false
18007  * @cfg {Boolean} todayBtn default false
18008  * @cfg {Boolean} calendarWeeks default false
18009  * @cfg {Object} daysOfWeekDisabled default empty
18010  * @cfg {Boolean} singleMode default false (true | false)
18011  * 
18012  * @cfg {Boolean} keyboardNavigation default true
18013  * @cfg {String} language default en
18014  * 
18015  * @constructor
18016  * Create a new DateField
18017  * @param {Object} config The config object
18018  */
18019
18020 Roo.bootstrap.DateField = function(config){
18021     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18022      this.addEvents({
18023             /**
18024              * @event show
18025              * Fires when this field show.
18026              * @param {Roo.bootstrap.DateField} this
18027              * @param {Mixed} date The date value
18028              */
18029             show : true,
18030             /**
18031              * @event show
18032              * Fires when this field hide.
18033              * @param {Roo.bootstrap.DateField} this
18034              * @param {Mixed} date The date value
18035              */
18036             hide : true,
18037             /**
18038              * @event select
18039              * Fires when select a date.
18040              * @param {Roo.bootstrap.DateField} this
18041              * @param {Mixed} date The date value
18042              */
18043             select : true,
18044             /**
18045              * @event beforeselect
18046              * Fires when before select a date.
18047              * @param {Roo.bootstrap.DateField} this
18048              * @param {Mixed} date The date value
18049              */
18050             beforeselect : true
18051         });
18052 };
18053
18054 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18055     
18056     /**
18057      * @cfg {String} format
18058      * The default date format string which can be overriden for localization support.  The format must be
18059      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18060      */
18061     format : "m/d/y",
18062     /**
18063      * @cfg {String} altFormats
18064      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18065      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18066      */
18067     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18068     
18069     weekStart : 0,
18070     
18071     viewMode : '',
18072     
18073     minViewMode : '',
18074     
18075     todayHighlight : false,
18076     
18077     todayBtn: false,
18078     
18079     language: 'en',
18080     
18081     keyboardNavigation: true,
18082     
18083     calendarWeeks: false,
18084     
18085     startDate: -Infinity,
18086     
18087     endDate: Infinity,
18088     
18089     daysOfWeekDisabled: [],
18090     
18091     _events: [],
18092     
18093     singleMode : false,
18094     
18095     UTCDate: function()
18096     {
18097         return new Date(Date.UTC.apply(Date, arguments));
18098     },
18099     
18100     UTCToday: function()
18101     {
18102         var today = new Date();
18103         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18104     },
18105     
18106     getDate: function() {
18107             var d = this.getUTCDate();
18108             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18109     },
18110     
18111     getUTCDate: function() {
18112             return this.date;
18113     },
18114     
18115     setDate: function(d) {
18116             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18117     },
18118     
18119     setUTCDate: function(d) {
18120             this.date = d;
18121             this.setValue(this.formatDate(this.date));
18122     },
18123         
18124     onRender: function(ct, position)
18125     {
18126         
18127         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18128         
18129         this.language = this.language || 'en';
18130         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18131         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18132         
18133         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18134         this.format = this.format || 'm/d/y';
18135         this.isInline = false;
18136         this.isInput = true;
18137         this.component = this.el.select('.add-on', true).first() || false;
18138         this.component = (this.component && this.component.length === 0) ? false : this.component;
18139         this.hasInput = this.component && this.inputEl().length;
18140         
18141         if (typeof(this.minViewMode === 'string')) {
18142             switch (this.minViewMode) {
18143                 case 'months':
18144                     this.minViewMode = 1;
18145                     break;
18146                 case 'years':
18147                     this.minViewMode = 2;
18148                     break;
18149                 default:
18150                     this.minViewMode = 0;
18151                     break;
18152             }
18153         }
18154         
18155         if (typeof(this.viewMode === 'string')) {
18156             switch (this.viewMode) {
18157                 case 'months':
18158                     this.viewMode = 1;
18159                     break;
18160                 case 'years':
18161                     this.viewMode = 2;
18162                     break;
18163                 default:
18164                     this.viewMode = 0;
18165                     break;
18166             }
18167         }
18168                 
18169         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18170         
18171 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18172         
18173         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18174         
18175         this.picker().on('mousedown', this.onMousedown, this);
18176         this.picker().on('click', this.onClick, this);
18177         
18178         this.picker().addClass('datepicker-dropdown');
18179         
18180         this.startViewMode = this.viewMode;
18181         
18182         if(this.singleMode){
18183             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18184                 v.setVisibilityMode(Roo.Element.DISPLAY);
18185                 v.hide();
18186             });
18187             
18188             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18189                 v.setStyle('width', '189px');
18190             });
18191         }
18192         
18193         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18194             if(!this.calendarWeeks){
18195                 v.remove();
18196                 return;
18197             }
18198             
18199             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18200             v.attr('colspan', function(i, val){
18201                 return parseInt(val) + 1;
18202             });
18203         });
18204                         
18205         
18206         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18207         
18208         this.setStartDate(this.startDate);
18209         this.setEndDate(this.endDate);
18210         
18211         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18212         
18213         this.fillDow();
18214         this.fillMonths();
18215         this.update();
18216         this.showMode();
18217         
18218         if(this.isInline) {
18219             this.show();
18220         }
18221     },
18222     
18223     picker : function()
18224     {
18225         return this.pickerEl;
18226 //        return this.el.select('.datepicker', true).first();
18227     },
18228     
18229     fillDow: function()
18230     {
18231         var dowCnt = this.weekStart;
18232         
18233         var dow = {
18234             tag: 'tr',
18235             cn: [
18236                 
18237             ]
18238         };
18239         
18240         if(this.calendarWeeks){
18241             dow.cn.push({
18242                 tag: 'th',
18243                 cls: 'cw',
18244                 html: '&nbsp;'
18245             })
18246         }
18247         
18248         while (dowCnt < this.weekStart + 7) {
18249             dow.cn.push({
18250                 tag: 'th',
18251                 cls: 'dow',
18252                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18253             });
18254         }
18255         
18256         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18257     },
18258     
18259     fillMonths: function()
18260     {    
18261         var i = 0;
18262         var months = this.picker().select('>.datepicker-months td', true).first();
18263         
18264         months.dom.innerHTML = '';
18265         
18266         while (i < 12) {
18267             var month = {
18268                 tag: 'span',
18269                 cls: 'month',
18270                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18271             };
18272             
18273             months.createChild(month);
18274         }
18275         
18276     },
18277     
18278     update: function()
18279     {
18280         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;
18281         
18282         if (this.date < this.startDate) {
18283             this.viewDate = new Date(this.startDate);
18284         } else if (this.date > this.endDate) {
18285             this.viewDate = new Date(this.endDate);
18286         } else {
18287             this.viewDate = new Date(this.date);
18288         }
18289         
18290         this.fill();
18291     },
18292     
18293     fill: function() 
18294     {
18295         var d = new Date(this.viewDate),
18296                 year = d.getUTCFullYear(),
18297                 month = d.getUTCMonth(),
18298                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18299                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18300                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18301                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18302                 currentDate = this.date && this.date.valueOf(),
18303                 today = this.UTCToday();
18304         
18305         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18306         
18307 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18308         
18309 //        this.picker.select('>tfoot th.today').
18310 //                                              .text(dates[this.language].today)
18311 //                                              .toggle(this.todayBtn !== false);
18312     
18313         this.updateNavArrows();
18314         this.fillMonths();
18315                                                 
18316         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18317         
18318         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18319          
18320         prevMonth.setUTCDate(day);
18321         
18322         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18323         
18324         var nextMonth = new Date(prevMonth);
18325         
18326         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18327         
18328         nextMonth = nextMonth.valueOf();
18329         
18330         var fillMonths = false;
18331         
18332         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18333         
18334         while(prevMonth.valueOf() < nextMonth) {
18335             var clsName = '';
18336             
18337             if (prevMonth.getUTCDay() === this.weekStart) {
18338                 if(fillMonths){
18339                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18340                 }
18341                     
18342                 fillMonths = {
18343                     tag: 'tr',
18344                     cn: []
18345                 };
18346                 
18347                 if(this.calendarWeeks){
18348                     // ISO 8601: First week contains first thursday.
18349                     // ISO also states week starts on Monday, but we can be more abstract here.
18350                     var
18351                     // Start of current week: based on weekstart/current date
18352                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18353                     // Thursday of this week
18354                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18355                     // First Thursday of year, year from thursday
18356                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18357                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18358                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18359                     
18360                     fillMonths.cn.push({
18361                         tag: 'td',
18362                         cls: 'cw',
18363                         html: calWeek
18364                     });
18365                 }
18366             }
18367             
18368             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18369                 clsName += ' old';
18370             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18371                 clsName += ' new';
18372             }
18373             if (this.todayHighlight &&
18374                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18375                 prevMonth.getUTCMonth() == today.getMonth() &&
18376                 prevMonth.getUTCDate() == today.getDate()) {
18377                 clsName += ' today';
18378             }
18379             
18380             if (currentDate && prevMonth.valueOf() === currentDate) {
18381                 clsName += ' active';
18382             }
18383             
18384             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18385                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18386                     clsName += ' disabled';
18387             }
18388             
18389             fillMonths.cn.push({
18390                 tag: 'td',
18391                 cls: 'day ' + clsName,
18392                 html: prevMonth.getDate()
18393             });
18394             
18395             prevMonth.setDate(prevMonth.getDate()+1);
18396         }
18397           
18398         var currentYear = this.date && this.date.getUTCFullYear();
18399         var currentMonth = this.date && this.date.getUTCMonth();
18400         
18401         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18402         
18403         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18404             v.removeClass('active');
18405             
18406             if(currentYear === year && k === currentMonth){
18407                 v.addClass('active');
18408             }
18409             
18410             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18411                 v.addClass('disabled');
18412             }
18413             
18414         });
18415         
18416         
18417         year = parseInt(year/10, 10) * 10;
18418         
18419         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18420         
18421         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18422         
18423         year -= 1;
18424         for (var i = -1; i < 11; i++) {
18425             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18426                 tag: 'span',
18427                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18428                 html: year
18429             });
18430             
18431             year += 1;
18432         }
18433     },
18434     
18435     showMode: function(dir) 
18436     {
18437         if (dir) {
18438             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18439         }
18440         
18441         Roo.each(this.picker().select('>div',true).elements, function(v){
18442             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18443             v.hide();
18444         });
18445         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18446     },
18447     
18448     place: function()
18449     {
18450         if(this.isInline) {
18451             return;
18452         }
18453         
18454         this.picker().removeClass(['bottom', 'top']);
18455         
18456         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18457             /*
18458              * place to the top of element!
18459              *
18460              */
18461             
18462             this.picker().addClass('top');
18463             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18464             
18465             return;
18466         }
18467         
18468         this.picker().addClass('bottom');
18469         
18470         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18471     },
18472     
18473     parseDate : function(value)
18474     {
18475         if(!value || value instanceof Date){
18476             return value;
18477         }
18478         var v = Date.parseDate(value, this.format);
18479         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18480             v = Date.parseDate(value, 'Y-m-d');
18481         }
18482         if(!v && this.altFormats){
18483             if(!this.altFormatsArray){
18484                 this.altFormatsArray = this.altFormats.split("|");
18485             }
18486             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18487                 v = Date.parseDate(value, this.altFormatsArray[i]);
18488             }
18489         }
18490         return v;
18491     },
18492     
18493     formatDate : function(date, fmt)
18494     {   
18495         return (!date || !(date instanceof Date)) ?
18496         date : date.dateFormat(fmt || this.format);
18497     },
18498     
18499     onFocus : function()
18500     {
18501         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18502         this.show();
18503     },
18504     
18505     onBlur : function()
18506     {
18507         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18508         
18509         var d = this.inputEl().getValue();
18510         
18511         this.setValue(d);
18512                 
18513         this.hide();
18514     },
18515     
18516     show : function()
18517     {
18518         this.picker().show();
18519         this.update();
18520         this.place();
18521         
18522         this.fireEvent('show', this, this.date);
18523     },
18524     
18525     hide : function()
18526     {
18527         if(this.isInline) {
18528             return;
18529         }
18530         this.picker().hide();
18531         this.viewMode = this.startViewMode;
18532         this.showMode();
18533         
18534         this.fireEvent('hide', this, this.date);
18535         
18536     },
18537     
18538     onMousedown: function(e)
18539     {
18540         e.stopPropagation();
18541         e.preventDefault();
18542     },
18543     
18544     keyup: function(e)
18545     {
18546         Roo.bootstrap.DateField.superclass.keyup.call(this);
18547         this.update();
18548     },
18549
18550     setValue: function(v)
18551     {
18552         if(this.fireEvent('beforeselect', this, v) !== false){
18553             var d = new Date(this.parseDate(v) ).clearTime();
18554         
18555             if(isNaN(d.getTime())){
18556                 this.date = this.viewDate = '';
18557                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18558                 return;
18559             }
18560
18561             v = this.formatDate(d);
18562
18563             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18564
18565             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18566
18567             this.update();
18568
18569             this.fireEvent('select', this, this.date);
18570         }
18571     },
18572     
18573     getValue: function()
18574     {
18575         return this.formatDate(this.date);
18576     },
18577     
18578     fireKey: function(e)
18579     {
18580         if (!this.picker().isVisible()){
18581             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18582                 this.show();
18583             }
18584             return;
18585         }
18586         
18587         var dateChanged = false,
18588         dir, day, month,
18589         newDate, newViewDate;
18590         
18591         switch(e.keyCode){
18592             case 27: // escape
18593                 this.hide();
18594                 e.preventDefault();
18595                 break;
18596             case 37: // left
18597             case 39: // right
18598                 if (!this.keyboardNavigation) {
18599                     break;
18600                 }
18601                 dir = e.keyCode == 37 ? -1 : 1;
18602                 
18603                 if (e.ctrlKey){
18604                     newDate = this.moveYear(this.date, dir);
18605                     newViewDate = this.moveYear(this.viewDate, dir);
18606                 } else if (e.shiftKey){
18607                     newDate = this.moveMonth(this.date, dir);
18608                     newViewDate = this.moveMonth(this.viewDate, dir);
18609                 } else {
18610                     newDate = new Date(this.date);
18611                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18612                     newViewDate = new Date(this.viewDate);
18613                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18614                 }
18615                 if (this.dateWithinRange(newDate)){
18616                     this.date = newDate;
18617                     this.viewDate = newViewDate;
18618                     this.setValue(this.formatDate(this.date));
18619 //                    this.update();
18620                     e.preventDefault();
18621                     dateChanged = true;
18622                 }
18623                 break;
18624             case 38: // up
18625             case 40: // down
18626                 if (!this.keyboardNavigation) {
18627                     break;
18628                 }
18629                 dir = e.keyCode == 38 ? -1 : 1;
18630                 if (e.ctrlKey){
18631                     newDate = this.moveYear(this.date, dir);
18632                     newViewDate = this.moveYear(this.viewDate, dir);
18633                 } else if (e.shiftKey){
18634                     newDate = this.moveMonth(this.date, dir);
18635                     newViewDate = this.moveMonth(this.viewDate, dir);
18636                 } else {
18637                     newDate = new Date(this.date);
18638                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18639                     newViewDate = new Date(this.viewDate);
18640                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18641                 }
18642                 if (this.dateWithinRange(newDate)){
18643                     this.date = newDate;
18644                     this.viewDate = newViewDate;
18645                     this.setValue(this.formatDate(this.date));
18646 //                    this.update();
18647                     e.preventDefault();
18648                     dateChanged = true;
18649                 }
18650                 break;
18651             case 13: // enter
18652                 this.setValue(this.formatDate(this.date));
18653                 this.hide();
18654                 e.preventDefault();
18655                 break;
18656             case 9: // tab
18657                 this.setValue(this.formatDate(this.date));
18658                 this.hide();
18659                 break;
18660             case 16: // shift
18661             case 17: // ctrl
18662             case 18: // alt
18663                 break;
18664             default :
18665                 this.hide();
18666                 
18667         }
18668     },
18669     
18670     
18671     onClick: function(e) 
18672     {
18673         e.stopPropagation();
18674         e.preventDefault();
18675         
18676         var target = e.getTarget();
18677         
18678         if(target.nodeName.toLowerCase() === 'i'){
18679             target = Roo.get(target).dom.parentNode;
18680         }
18681         
18682         var nodeName = target.nodeName;
18683         var className = target.className;
18684         var html = target.innerHTML;
18685         //Roo.log(nodeName);
18686         
18687         switch(nodeName.toLowerCase()) {
18688             case 'th':
18689                 switch(className) {
18690                     case 'switch':
18691                         this.showMode(1);
18692                         break;
18693                     case 'prev':
18694                     case 'next':
18695                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18696                         switch(this.viewMode){
18697                                 case 0:
18698                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18699                                         break;
18700                                 case 1:
18701                                 case 2:
18702                                         this.viewDate = this.moveYear(this.viewDate, dir);
18703                                         break;
18704                         }
18705                         this.fill();
18706                         break;
18707                     case 'today':
18708                         var date = new Date();
18709                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18710 //                        this.fill()
18711                         this.setValue(this.formatDate(this.date));
18712                         
18713                         this.hide();
18714                         break;
18715                 }
18716                 break;
18717             case 'span':
18718                 if (className.indexOf('disabled') < 0) {
18719                     this.viewDate.setUTCDate(1);
18720                     if (className.indexOf('month') > -1) {
18721                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18722                     } else {
18723                         var year = parseInt(html, 10) || 0;
18724                         this.viewDate.setUTCFullYear(year);
18725                         
18726                     }
18727                     
18728                     if(this.singleMode){
18729                         this.setValue(this.formatDate(this.viewDate));
18730                         this.hide();
18731                         return;
18732                     }
18733                     
18734                     this.showMode(-1);
18735                     this.fill();
18736                 }
18737                 break;
18738                 
18739             case 'td':
18740                 //Roo.log(className);
18741                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18742                     var day = parseInt(html, 10) || 1;
18743                     var year = this.viewDate.getUTCFullYear(),
18744                         month = this.viewDate.getUTCMonth();
18745
18746                     if (className.indexOf('old') > -1) {
18747                         if(month === 0 ){
18748                             month = 11;
18749                             year -= 1;
18750                         }else{
18751                             month -= 1;
18752                         }
18753                     } else if (className.indexOf('new') > -1) {
18754                         if (month == 11) {
18755                             month = 0;
18756                             year += 1;
18757                         } else {
18758                             month += 1;
18759                         }
18760                     }
18761                     //Roo.log([year,month,day]);
18762                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18763                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18764 //                    this.fill();
18765                     //Roo.log(this.formatDate(this.date));
18766                     this.setValue(this.formatDate(this.date));
18767                     this.hide();
18768                 }
18769                 break;
18770         }
18771     },
18772     
18773     setStartDate: function(startDate)
18774     {
18775         this.startDate = startDate || -Infinity;
18776         if (this.startDate !== -Infinity) {
18777             this.startDate = this.parseDate(this.startDate);
18778         }
18779         this.update();
18780         this.updateNavArrows();
18781     },
18782
18783     setEndDate: function(endDate)
18784     {
18785         this.endDate = endDate || Infinity;
18786         if (this.endDate !== Infinity) {
18787             this.endDate = this.parseDate(this.endDate);
18788         }
18789         this.update();
18790         this.updateNavArrows();
18791     },
18792     
18793     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18794     {
18795         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18796         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18797             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18798         }
18799         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18800             return parseInt(d, 10);
18801         });
18802         this.update();
18803         this.updateNavArrows();
18804     },
18805     
18806     updateNavArrows: function() 
18807     {
18808         if(this.singleMode){
18809             return;
18810         }
18811         
18812         var d = new Date(this.viewDate),
18813         year = d.getUTCFullYear(),
18814         month = d.getUTCMonth();
18815         
18816         Roo.each(this.picker().select('.prev', true).elements, function(v){
18817             v.show();
18818             switch (this.viewMode) {
18819                 case 0:
18820
18821                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18822                         v.hide();
18823                     }
18824                     break;
18825                 case 1:
18826                 case 2:
18827                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18828                         v.hide();
18829                     }
18830                     break;
18831             }
18832         });
18833         
18834         Roo.each(this.picker().select('.next', true).elements, function(v){
18835             v.show();
18836             switch (this.viewMode) {
18837                 case 0:
18838
18839                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18840                         v.hide();
18841                     }
18842                     break;
18843                 case 1:
18844                 case 2:
18845                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18846                         v.hide();
18847                     }
18848                     break;
18849             }
18850         })
18851     },
18852     
18853     moveMonth: function(date, dir)
18854     {
18855         if (!dir) {
18856             return date;
18857         }
18858         var new_date = new Date(date.valueOf()),
18859         day = new_date.getUTCDate(),
18860         month = new_date.getUTCMonth(),
18861         mag = Math.abs(dir),
18862         new_month, test;
18863         dir = dir > 0 ? 1 : -1;
18864         if (mag == 1){
18865             test = dir == -1
18866             // If going back one month, make sure month is not current month
18867             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18868             ? function(){
18869                 return new_date.getUTCMonth() == month;
18870             }
18871             // If going forward one month, make sure month is as expected
18872             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18873             : function(){
18874                 return new_date.getUTCMonth() != new_month;
18875             };
18876             new_month = month + dir;
18877             new_date.setUTCMonth(new_month);
18878             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18879             if (new_month < 0 || new_month > 11) {
18880                 new_month = (new_month + 12) % 12;
18881             }
18882         } else {
18883             // For magnitudes >1, move one month at a time...
18884             for (var i=0; i<mag; i++) {
18885                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18886                 new_date = this.moveMonth(new_date, dir);
18887             }
18888             // ...then reset the day, keeping it in the new month
18889             new_month = new_date.getUTCMonth();
18890             new_date.setUTCDate(day);
18891             test = function(){
18892                 return new_month != new_date.getUTCMonth();
18893             };
18894         }
18895         // Common date-resetting loop -- if date is beyond end of month, make it
18896         // end of month
18897         while (test()){
18898             new_date.setUTCDate(--day);
18899             new_date.setUTCMonth(new_month);
18900         }
18901         return new_date;
18902     },
18903
18904     moveYear: function(date, dir)
18905     {
18906         return this.moveMonth(date, dir*12);
18907     },
18908
18909     dateWithinRange: function(date)
18910     {
18911         return date >= this.startDate && date <= this.endDate;
18912     },
18913
18914     
18915     remove: function() 
18916     {
18917         this.picker().remove();
18918     },
18919     
18920     validateValue : function(value)
18921     {
18922         if(value.length < 1)  {
18923             if(this.allowBlank){
18924                 return true;
18925             }
18926             return false;
18927         }
18928         
18929         if(value.length < this.minLength){
18930             return false;
18931         }
18932         if(value.length > this.maxLength){
18933             return false;
18934         }
18935         if(this.vtype){
18936             var vt = Roo.form.VTypes;
18937             if(!vt[this.vtype](value, this)){
18938                 return false;
18939             }
18940         }
18941         if(typeof this.validator == "function"){
18942             var msg = this.validator(value);
18943             if(msg !== true){
18944                 return false;
18945             }
18946         }
18947         
18948         if(this.regex && !this.regex.test(value)){
18949             return false;
18950         }
18951         
18952         if(typeof(this.parseDate(value)) == 'undefined'){
18953             return false;
18954         }
18955         
18956         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18957             return false;
18958         }      
18959         
18960         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18961             return false;
18962         } 
18963         
18964         
18965         return true;
18966     }
18967    
18968 });
18969
18970 Roo.apply(Roo.bootstrap.DateField,  {
18971     
18972     head : {
18973         tag: 'thead',
18974         cn: [
18975         {
18976             tag: 'tr',
18977             cn: [
18978             {
18979                 tag: 'th',
18980                 cls: 'prev',
18981                 html: '<i class="fa fa-arrow-left"/>'
18982             },
18983             {
18984                 tag: 'th',
18985                 cls: 'switch',
18986                 colspan: '5'
18987             },
18988             {
18989                 tag: 'th',
18990                 cls: 'next',
18991                 html: '<i class="fa fa-arrow-right"/>'
18992             }
18993
18994             ]
18995         }
18996         ]
18997     },
18998     
18999     content : {
19000         tag: 'tbody',
19001         cn: [
19002         {
19003             tag: 'tr',
19004             cn: [
19005             {
19006                 tag: 'td',
19007                 colspan: '7'
19008             }
19009             ]
19010         }
19011         ]
19012     },
19013     
19014     footer : {
19015         tag: 'tfoot',
19016         cn: [
19017         {
19018             tag: 'tr',
19019             cn: [
19020             {
19021                 tag: 'th',
19022                 colspan: '7',
19023                 cls: 'today'
19024             }
19025                     
19026             ]
19027         }
19028         ]
19029     },
19030     
19031     dates:{
19032         en: {
19033             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19034             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19035             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19036             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19037             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19038             today: "Today"
19039         }
19040     },
19041     
19042     modes: [
19043     {
19044         clsName: 'days',
19045         navFnc: 'Month',
19046         navStep: 1
19047     },
19048     {
19049         clsName: 'months',
19050         navFnc: 'FullYear',
19051         navStep: 1
19052     },
19053     {
19054         clsName: 'years',
19055         navFnc: 'FullYear',
19056         navStep: 10
19057     }]
19058 });
19059
19060 Roo.apply(Roo.bootstrap.DateField,  {
19061   
19062     template : {
19063         tag: 'div',
19064         cls: 'datepicker dropdown-menu roo-dynamic',
19065         cn: [
19066         {
19067             tag: 'div',
19068             cls: 'datepicker-days',
19069             cn: [
19070             {
19071                 tag: 'table',
19072                 cls: 'table-condensed',
19073                 cn:[
19074                 Roo.bootstrap.DateField.head,
19075                 {
19076                     tag: 'tbody'
19077                 },
19078                 Roo.bootstrap.DateField.footer
19079                 ]
19080             }
19081             ]
19082         },
19083         {
19084             tag: 'div',
19085             cls: 'datepicker-months',
19086             cn: [
19087             {
19088                 tag: 'table',
19089                 cls: 'table-condensed',
19090                 cn:[
19091                 Roo.bootstrap.DateField.head,
19092                 Roo.bootstrap.DateField.content,
19093                 Roo.bootstrap.DateField.footer
19094                 ]
19095             }
19096             ]
19097         },
19098         {
19099             tag: 'div',
19100             cls: 'datepicker-years',
19101             cn: [
19102             {
19103                 tag: 'table',
19104                 cls: 'table-condensed',
19105                 cn:[
19106                 Roo.bootstrap.DateField.head,
19107                 Roo.bootstrap.DateField.content,
19108                 Roo.bootstrap.DateField.footer
19109                 ]
19110             }
19111             ]
19112         }
19113         ]
19114     }
19115 });
19116
19117  
19118
19119  /*
19120  * - LGPL
19121  *
19122  * TimeField
19123  * 
19124  */
19125
19126 /**
19127  * @class Roo.bootstrap.TimeField
19128  * @extends Roo.bootstrap.Input
19129  * Bootstrap DateField class
19130  * 
19131  * 
19132  * @constructor
19133  * Create a new TimeField
19134  * @param {Object} config The config object
19135  */
19136
19137 Roo.bootstrap.TimeField = function(config){
19138     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19139     this.addEvents({
19140             /**
19141              * @event show
19142              * Fires when this field show.
19143              * @param {Roo.bootstrap.DateField} thisthis
19144              * @param {Mixed} date The date value
19145              */
19146             show : true,
19147             /**
19148              * @event show
19149              * Fires when this field hide.
19150              * @param {Roo.bootstrap.DateField} this
19151              * @param {Mixed} date The date value
19152              */
19153             hide : true,
19154             /**
19155              * @event select
19156              * Fires when select a date.
19157              * @param {Roo.bootstrap.DateField} this
19158              * @param {Mixed} date The date value
19159              */
19160             select : true
19161         });
19162 };
19163
19164 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19165     
19166     /**
19167      * @cfg {String} format
19168      * The default time format string which can be overriden for localization support.  The format must be
19169      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19170      */
19171     format : "H:i",
19172        
19173     onRender: function(ct, position)
19174     {
19175         
19176         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19177                 
19178         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19179         
19180         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19181         
19182         this.pop = this.picker().select('>.datepicker-time',true).first();
19183         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19184         
19185         this.picker().on('mousedown', this.onMousedown, this);
19186         this.picker().on('click', this.onClick, this);
19187         
19188         this.picker().addClass('datepicker-dropdown');
19189     
19190         this.fillTime();
19191         this.update();
19192             
19193         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19194         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19195         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19196         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19197         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19198         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19199
19200     },
19201     
19202     fireKey: function(e){
19203         if (!this.picker().isVisible()){
19204             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19205                 this.show();
19206             }
19207             return;
19208         }
19209
19210         e.preventDefault();
19211         
19212         switch(e.keyCode){
19213             case 27: // escape
19214                 this.hide();
19215                 break;
19216             case 37: // left
19217             case 39: // right
19218                 this.onTogglePeriod();
19219                 break;
19220             case 38: // up
19221                 this.onIncrementMinutes();
19222                 break;
19223             case 40: // down
19224                 this.onDecrementMinutes();
19225                 break;
19226             case 13: // enter
19227             case 9: // tab
19228                 this.setTime();
19229                 break;
19230         }
19231     },
19232     
19233     onClick: function(e) {
19234         e.stopPropagation();
19235         e.preventDefault();
19236     },
19237     
19238     picker : function()
19239     {
19240         return this.el.select('.datepicker', true).first();
19241     },
19242     
19243     fillTime: function()
19244     {    
19245         var time = this.pop.select('tbody', true).first();
19246         
19247         time.dom.innerHTML = '';
19248         
19249         time.createChild({
19250             tag: 'tr',
19251             cn: [
19252                 {
19253                     tag: 'td',
19254                     cn: [
19255                         {
19256                             tag: 'a',
19257                             href: '#',
19258                             cls: 'btn',
19259                             cn: [
19260                                 {
19261                                     tag: 'span',
19262                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19263                                 }
19264                             ]
19265                         } 
19266                     ]
19267                 },
19268                 {
19269                     tag: 'td',
19270                     cls: 'separator'
19271                 },
19272                 {
19273                     tag: 'td',
19274                     cn: [
19275                         {
19276                             tag: 'a',
19277                             href: '#',
19278                             cls: 'btn',
19279                             cn: [
19280                                 {
19281                                     tag: 'span',
19282                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19283                                 }
19284                             ]
19285                         }
19286                     ]
19287                 },
19288                 {
19289                     tag: 'td',
19290                     cls: 'separator'
19291                 }
19292             ]
19293         });
19294         
19295         time.createChild({
19296             tag: 'tr',
19297             cn: [
19298                 {
19299                     tag: 'td',
19300                     cn: [
19301                         {
19302                             tag: 'span',
19303                             cls: 'timepicker-hour',
19304                             html: '00'
19305                         }  
19306                     ]
19307                 },
19308                 {
19309                     tag: 'td',
19310                     cls: 'separator',
19311                     html: ':'
19312                 },
19313                 {
19314                     tag: 'td',
19315                     cn: [
19316                         {
19317                             tag: 'span',
19318                             cls: 'timepicker-minute',
19319                             html: '00'
19320                         }  
19321                     ]
19322                 },
19323                 {
19324                     tag: 'td',
19325                     cls: 'separator'
19326                 },
19327                 {
19328                     tag: 'td',
19329                     cn: [
19330                         {
19331                             tag: 'button',
19332                             type: 'button',
19333                             cls: 'btn btn-primary period',
19334                             html: 'AM'
19335                             
19336                         }
19337                     ]
19338                 }
19339             ]
19340         });
19341         
19342         time.createChild({
19343             tag: 'tr',
19344             cn: [
19345                 {
19346                     tag: 'td',
19347                     cn: [
19348                         {
19349                             tag: 'a',
19350                             href: '#',
19351                             cls: 'btn',
19352                             cn: [
19353                                 {
19354                                     tag: 'span',
19355                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19356                                 }
19357                             ]
19358                         }
19359                     ]
19360                 },
19361                 {
19362                     tag: 'td',
19363                     cls: 'separator'
19364                 },
19365                 {
19366                     tag: 'td',
19367                     cn: [
19368                         {
19369                             tag: 'a',
19370                             href: '#',
19371                             cls: 'btn',
19372                             cn: [
19373                                 {
19374                                     tag: 'span',
19375                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19376                                 }
19377                             ]
19378                         }
19379                     ]
19380                 },
19381                 {
19382                     tag: 'td',
19383                     cls: 'separator'
19384                 }
19385             ]
19386         });
19387         
19388     },
19389     
19390     update: function()
19391     {
19392         
19393         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19394         
19395         this.fill();
19396     },
19397     
19398     fill: function() 
19399     {
19400         var hours = this.time.getHours();
19401         var minutes = this.time.getMinutes();
19402         var period = 'AM';
19403         
19404         if(hours > 11){
19405             period = 'PM';
19406         }
19407         
19408         if(hours == 0){
19409             hours = 12;
19410         }
19411         
19412         
19413         if(hours > 12){
19414             hours = hours - 12;
19415         }
19416         
19417         if(hours < 10){
19418             hours = '0' + hours;
19419         }
19420         
19421         if(minutes < 10){
19422             minutes = '0' + minutes;
19423         }
19424         
19425         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19426         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19427         this.pop.select('button', true).first().dom.innerHTML = period;
19428         
19429     },
19430     
19431     place: function()
19432     {   
19433         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19434         
19435         var cls = ['bottom'];
19436         
19437         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19438             cls.pop();
19439             cls.push('top');
19440         }
19441         
19442         cls.push('right');
19443         
19444         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19445             cls.pop();
19446             cls.push('left');
19447         }
19448         
19449         this.picker().addClass(cls.join('-'));
19450         
19451         var _this = this;
19452         
19453         Roo.each(cls, function(c){
19454             if(c == 'bottom'){
19455                 _this.picker().setTop(_this.inputEl().getHeight());
19456                 return;
19457             }
19458             if(c == 'top'){
19459                 _this.picker().setTop(0 - _this.picker().getHeight());
19460                 return;
19461             }
19462             
19463             if(c == 'left'){
19464                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19465                 return;
19466             }
19467             if(c == 'right'){
19468                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19469                 return;
19470             }
19471         });
19472         
19473     },
19474   
19475     onFocus : function()
19476     {
19477         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19478         this.show();
19479     },
19480     
19481     onBlur : function()
19482     {
19483         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19484         this.hide();
19485     },
19486     
19487     show : function()
19488     {
19489         this.picker().show();
19490         this.pop.show();
19491         this.update();
19492         this.place();
19493         
19494         this.fireEvent('show', this, this.date);
19495     },
19496     
19497     hide : function()
19498     {
19499         this.picker().hide();
19500         this.pop.hide();
19501         
19502         this.fireEvent('hide', this, this.date);
19503     },
19504     
19505     setTime : function()
19506     {
19507         this.hide();
19508         this.setValue(this.time.format(this.format));
19509         
19510         this.fireEvent('select', this, this.date);
19511         
19512         
19513     },
19514     
19515     onMousedown: function(e){
19516         e.stopPropagation();
19517         e.preventDefault();
19518     },
19519     
19520     onIncrementHours: function()
19521     {
19522         Roo.log('onIncrementHours');
19523         this.time = this.time.add(Date.HOUR, 1);
19524         this.update();
19525         
19526     },
19527     
19528     onDecrementHours: function()
19529     {
19530         Roo.log('onDecrementHours');
19531         this.time = this.time.add(Date.HOUR, -1);
19532         this.update();
19533     },
19534     
19535     onIncrementMinutes: function()
19536     {
19537         Roo.log('onIncrementMinutes');
19538         this.time = this.time.add(Date.MINUTE, 1);
19539         this.update();
19540     },
19541     
19542     onDecrementMinutes: function()
19543     {
19544         Roo.log('onDecrementMinutes');
19545         this.time = this.time.add(Date.MINUTE, -1);
19546         this.update();
19547     },
19548     
19549     onTogglePeriod: function()
19550     {
19551         Roo.log('onTogglePeriod');
19552         this.time = this.time.add(Date.HOUR, 12);
19553         this.update();
19554     }
19555     
19556    
19557 });
19558
19559 Roo.apply(Roo.bootstrap.TimeField,  {
19560     
19561     content : {
19562         tag: 'tbody',
19563         cn: [
19564             {
19565                 tag: 'tr',
19566                 cn: [
19567                 {
19568                     tag: 'td',
19569                     colspan: '7'
19570                 }
19571                 ]
19572             }
19573         ]
19574     },
19575     
19576     footer : {
19577         tag: 'tfoot',
19578         cn: [
19579             {
19580                 tag: 'tr',
19581                 cn: [
19582                 {
19583                     tag: 'th',
19584                     colspan: '7',
19585                     cls: '',
19586                     cn: [
19587                         {
19588                             tag: 'button',
19589                             cls: 'btn btn-info ok',
19590                             html: 'OK'
19591                         }
19592                     ]
19593                 }
19594
19595                 ]
19596             }
19597         ]
19598     }
19599 });
19600
19601 Roo.apply(Roo.bootstrap.TimeField,  {
19602   
19603     template : {
19604         tag: 'div',
19605         cls: 'datepicker dropdown-menu',
19606         cn: [
19607             {
19608                 tag: 'div',
19609                 cls: 'datepicker-time',
19610                 cn: [
19611                 {
19612                     tag: 'table',
19613                     cls: 'table-condensed',
19614                     cn:[
19615                     Roo.bootstrap.TimeField.content,
19616                     Roo.bootstrap.TimeField.footer
19617                     ]
19618                 }
19619                 ]
19620             }
19621         ]
19622     }
19623 });
19624
19625  
19626
19627  /*
19628  * - LGPL
19629  *
19630  * MonthField
19631  * 
19632  */
19633
19634 /**
19635  * @class Roo.bootstrap.MonthField
19636  * @extends Roo.bootstrap.Input
19637  * Bootstrap MonthField class
19638  * 
19639  * @cfg {String} language default en
19640  * 
19641  * @constructor
19642  * Create a new MonthField
19643  * @param {Object} config The config object
19644  */
19645
19646 Roo.bootstrap.MonthField = function(config){
19647     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19648     
19649     this.addEvents({
19650         /**
19651          * @event show
19652          * Fires when this field show.
19653          * @param {Roo.bootstrap.MonthField} this
19654          * @param {Mixed} date The date value
19655          */
19656         show : true,
19657         /**
19658          * @event show
19659          * Fires when this field hide.
19660          * @param {Roo.bootstrap.MonthField} this
19661          * @param {Mixed} date The date value
19662          */
19663         hide : true,
19664         /**
19665          * @event select
19666          * Fires when select a date.
19667          * @param {Roo.bootstrap.MonthField} this
19668          * @param {String} oldvalue The old value
19669          * @param {String} newvalue The new value
19670          */
19671         select : true
19672     });
19673 };
19674
19675 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19676     
19677     onRender: function(ct, position)
19678     {
19679         
19680         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19681         
19682         this.language = this.language || 'en';
19683         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19684         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19685         
19686         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19687         this.isInline = false;
19688         this.isInput = true;
19689         this.component = this.el.select('.add-on', true).first() || false;
19690         this.component = (this.component && this.component.length === 0) ? false : this.component;
19691         this.hasInput = this.component && this.inputEL().length;
19692         
19693         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19694         
19695         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19696         
19697         this.picker().on('mousedown', this.onMousedown, this);
19698         this.picker().on('click', this.onClick, this);
19699         
19700         this.picker().addClass('datepicker-dropdown');
19701         
19702         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19703             v.setStyle('width', '189px');
19704         });
19705         
19706         this.fillMonths();
19707         
19708         this.update();
19709         
19710         if(this.isInline) {
19711             this.show();
19712         }
19713         
19714     },
19715     
19716     setValue: function(v, suppressEvent)
19717     {   
19718         var o = this.getValue();
19719         
19720         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19721         
19722         this.update();
19723
19724         if(suppressEvent !== true){
19725             this.fireEvent('select', this, o, v);
19726         }
19727         
19728     },
19729     
19730     getValue: function()
19731     {
19732         return this.value;
19733     },
19734     
19735     onClick: function(e) 
19736     {
19737         e.stopPropagation();
19738         e.preventDefault();
19739         
19740         var target = e.getTarget();
19741         
19742         if(target.nodeName.toLowerCase() === 'i'){
19743             target = Roo.get(target).dom.parentNode;
19744         }
19745         
19746         var nodeName = target.nodeName;
19747         var className = target.className;
19748         var html = target.innerHTML;
19749         
19750         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19751             return;
19752         }
19753         
19754         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19755         
19756         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19757         
19758         this.hide();
19759                         
19760     },
19761     
19762     picker : function()
19763     {
19764         return this.pickerEl;
19765     },
19766     
19767     fillMonths: function()
19768     {    
19769         var i = 0;
19770         var months = this.picker().select('>.datepicker-months td', true).first();
19771         
19772         months.dom.innerHTML = '';
19773         
19774         while (i < 12) {
19775             var month = {
19776                 tag: 'span',
19777                 cls: 'month',
19778                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19779             };
19780             
19781             months.createChild(month);
19782         }
19783         
19784     },
19785     
19786     update: function()
19787     {
19788         var _this = this;
19789         
19790         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19791             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19792         }
19793         
19794         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19795             e.removeClass('active');
19796             
19797             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19798                 e.addClass('active');
19799             }
19800         })
19801     },
19802     
19803     place: function()
19804     {
19805         if(this.isInline) {
19806             return;
19807         }
19808         
19809         this.picker().removeClass(['bottom', 'top']);
19810         
19811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19812             /*
19813              * place to the top of element!
19814              *
19815              */
19816             
19817             this.picker().addClass('top');
19818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19819             
19820             return;
19821         }
19822         
19823         this.picker().addClass('bottom');
19824         
19825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19826     },
19827     
19828     onFocus : function()
19829     {
19830         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19831         this.show();
19832     },
19833     
19834     onBlur : function()
19835     {
19836         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19837         
19838         var d = this.inputEl().getValue();
19839         
19840         this.setValue(d);
19841                 
19842         this.hide();
19843     },
19844     
19845     show : function()
19846     {
19847         this.picker().show();
19848         this.picker().select('>.datepicker-months', true).first().show();
19849         this.update();
19850         this.place();
19851         
19852         this.fireEvent('show', this, this.date);
19853     },
19854     
19855     hide : function()
19856     {
19857         if(this.isInline) {
19858             return;
19859         }
19860         this.picker().hide();
19861         this.fireEvent('hide', this, this.date);
19862         
19863     },
19864     
19865     onMousedown: function(e)
19866     {
19867         e.stopPropagation();
19868         e.preventDefault();
19869     },
19870     
19871     keyup: function(e)
19872     {
19873         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19874         this.update();
19875     },
19876
19877     fireKey: function(e)
19878     {
19879         if (!this.picker().isVisible()){
19880             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19881                 this.show();
19882             }
19883             return;
19884         }
19885         
19886         var dir;
19887         
19888         switch(e.keyCode){
19889             case 27: // escape
19890                 this.hide();
19891                 e.preventDefault();
19892                 break;
19893             case 37: // left
19894             case 39: // right
19895                 dir = e.keyCode == 37 ? -1 : 1;
19896                 
19897                 this.vIndex = this.vIndex + dir;
19898                 
19899                 if(this.vIndex < 0){
19900                     this.vIndex = 0;
19901                 }
19902                 
19903                 if(this.vIndex > 11){
19904                     this.vIndex = 11;
19905                 }
19906                 
19907                 if(isNaN(this.vIndex)){
19908                     this.vIndex = 0;
19909                 }
19910                 
19911                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19912                 
19913                 break;
19914             case 38: // up
19915             case 40: // down
19916                 
19917                 dir = e.keyCode == 38 ? -1 : 1;
19918                 
19919                 this.vIndex = this.vIndex + dir * 4;
19920                 
19921                 if(this.vIndex < 0){
19922                     this.vIndex = 0;
19923                 }
19924                 
19925                 if(this.vIndex > 11){
19926                     this.vIndex = 11;
19927                 }
19928                 
19929                 if(isNaN(this.vIndex)){
19930                     this.vIndex = 0;
19931                 }
19932                 
19933                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19934                 break;
19935                 
19936             case 13: // enter
19937                 
19938                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19939                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19940                 }
19941                 
19942                 this.hide();
19943                 e.preventDefault();
19944                 break;
19945             case 9: // tab
19946                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19947                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19948                 }
19949                 this.hide();
19950                 break;
19951             case 16: // shift
19952             case 17: // ctrl
19953             case 18: // alt
19954                 break;
19955             default :
19956                 this.hide();
19957                 
19958         }
19959     },
19960     
19961     remove: function() 
19962     {
19963         this.picker().remove();
19964     }
19965    
19966 });
19967
19968 Roo.apply(Roo.bootstrap.MonthField,  {
19969     
19970     content : {
19971         tag: 'tbody',
19972         cn: [
19973         {
19974             tag: 'tr',
19975             cn: [
19976             {
19977                 tag: 'td',
19978                 colspan: '7'
19979             }
19980             ]
19981         }
19982         ]
19983     },
19984     
19985     dates:{
19986         en: {
19987             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19988             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19989         }
19990     }
19991 });
19992
19993 Roo.apply(Roo.bootstrap.MonthField,  {
19994   
19995     template : {
19996         tag: 'div',
19997         cls: 'datepicker dropdown-menu roo-dynamic',
19998         cn: [
19999             {
20000                 tag: 'div',
20001                 cls: 'datepicker-months',
20002                 cn: [
20003                 {
20004                     tag: 'table',
20005                     cls: 'table-condensed',
20006                     cn:[
20007                         Roo.bootstrap.DateField.content
20008                     ]
20009                 }
20010                 ]
20011             }
20012         ]
20013     }
20014 });
20015
20016  
20017
20018  
20019  /*
20020  * - LGPL
20021  *
20022  * CheckBox
20023  * 
20024  */
20025
20026 /**
20027  * @class Roo.bootstrap.CheckBox
20028  * @extends Roo.bootstrap.Input
20029  * Bootstrap CheckBox class
20030  * 
20031  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20032  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20033  * @cfg {String} boxLabel The text that appears beside the checkbox
20034  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20035  * @cfg {Boolean} checked initnal the element
20036  * @cfg {Boolean} inline inline the element (default false)
20037  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20038  * 
20039  * @constructor
20040  * Create a new CheckBox
20041  * @param {Object} config The config object
20042  */
20043
20044 Roo.bootstrap.CheckBox = function(config){
20045     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20046    
20047     this.addEvents({
20048         /**
20049         * @event check
20050         * Fires when the element is checked or unchecked.
20051         * @param {Roo.bootstrap.CheckBox} this This input
20052         * @param {Boolean} checked The new checked value
20053         */
20054        check : true
20055     });
20056     
20057 };
20058
20059 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20060   
20061     inputType: 'checkbox',
20062     inputValue: 1,
20063     valueOff: 0,
20064     boxLabel: false,
20065     checked: false,
20066     weight : false,
20067     inline: false,
20068     
20069     getAutoCreate : function()
20070     {
20071         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20072         
20073         var id = Roo.id();
20074         
20075         var cfg = {};
20076         
20077         cfg.cls = 'form-group ' + this.inputType; //input-group
20078         
20079         if(this.inline){
20080             cfg.cls += ' ' + this.inputType + '-inline';
20081         }
20082         
20083         var input =  {
20084             tag: 'input',
20085             id : id,
20086             type : this.inputType,
20087             value : this.inputValue,
20088             cls : 'roo-' + this.inputType, //'form-box',
20089             placeholder : this.placeholder || ''
20090             
20091         };
20092         
20093         if(this.inputType != 'radio'){
20094             var hidden =  {
20095                 tag: 'input',
20096                 type : 'hidden',
20097                 cls : 'roo-hidden-value',
20098                 value : this.checked ? this.valueOff : this.inputValue
20099             };
20100         }
20101         
20102             
20103         if (this.weight) { // Validity check?
20104             cfg.cls += " " + this.inputType + "-" + this.weight;
20105         }
20106         
20107         if (this.disabled) {
20108             input.disabled=true;
20109         }
20110         
20111         if(this.checked){
20112             input.checked = this.checked;
20113             
20114         }
20115         
20116         
20117         if (this.name) {
20118             
20119             input.name = this.name;
20120             
20121             if(this.inputType != 'radio'){
20122                 hidden.name = this.name;
20123                 input.name = '_hidden_' + this.name;
20124             }
20125         }
20126         
20127         if (this.size) {
20128             input.cls += ' input-' + this.size;
20129         }
20130         
20131         var settings=this;
20132         
20133         ['xs','sm','md','lg'].map(function(size){
20134             if (settings[size]) {
20135                 cfg.cls += ' col-' + size + '-' + settings[size];
20136             }
20137         });
20138         
20139         var inputblock = input;
20140          
20141         if (this.before || this.after) {
20142             
20143             inputblock = {
20144                 cls : 'input-group',
20145                 cn :  [] 
20146             };
20147             
20148             if (this.before) {
20149                 inputblock.cn.push({
20150                     tag :'span',
20151                     cls : 'input-group-addon',
20152                     html : this.before
20153                 });
20154             }
20155             
20156             inputblock.cn.push(input);
20157             
20158             if(this.inputType != 'radio'){
20159                 inputblock.cn.push(hidden);
20160             }
20161             
20162             if (this.after) {
20163                 inputblock.cn.push({
20164                     tag :'span',
20165                     cls : 'input-group-addon',
20166                     html : this.after
20167                 });
20168             }
20169             
20170         }
20171         
20172         if (align ==='left' && this.fieldLabel.length) {
20173 //                Roo.log("left and has label");
20174             cfg.cn = [
20175                 {
20176                     tag: 'label',
20177                     'for' :  id,
20178                     cls : 'control-label',
20179                     html : this.fieldLabel
20180
20181                 },
20182                 {
20183                     cls : "", 
20184                     cn: [
20185                         inputblock
20186                     ]
20187                 }
20188             ];
20189             
20190             if(this.labelWidth > 12){
20191                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20192             }
20193             
20194             if(this.labelWidth < 13 && this.labelmd == 0){
20195                 this.labelmd = this.labelWidth;
20196             }
20197             
20198             if(this.labellg > 0){
20199                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20200                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20201             }
20202             
20203             if(this.labelmd > 0){
20204                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20205                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20206             }
20207             
20208             if(this.labelsm > 0){
20209                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20210                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20211             }
20212             
20213             if(this.labelxs > 0){
20214                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20215                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20216             }
20217             
20218         } else if ( this.fieldLabel.length) {
20219 //                Roo.log(" label");
20220                 cfg.cn = [
20221                    
20222                     {
20223                         tag: this.boxLabel ? 'span' : 'label',
20224                         'for': id,
20225                         cls: 'control-label box-input-label',
20226                         //cls : 'input-group-addon',
20227                         html : this.fieldLabel
20228                         
20229                     },
20230                     
20231                     inputblock
20232                     
20233                 ];
20234
20235         } else {
20236             
20237 //                Roo.log(" no label && no align");
20238                 cfg.cn = [  inputblock ] ;
20239                 
20240                 
20241         }
20242         
20243         if(this.boxLabel){
20244              var boxLabelCfg = {
20245                 tag: 'label',
20246                 //'for': id, // box label is handled by onclick - so no for...
20247                 cls: 'box-label',
20248                 html: this.boxLabel
20249             };
20250             
20251             if(this.tooltip){
20252                 boxLabelCfg.tooltip = this.tooltip;
20253             }
20254              
20255             cfg.cn.push(boxLabelCfg);
20256         }
20257         
20258         if(this.inputType != 'radio'){
20259             cfg.cn.push(hidden);
20260         }
20261         
20262         return cfg;
20263         
20264     },
20265     
20266     /**
20267      * return the real input element.
20268      */
20269     inputEl: function ()
20270     {
20271         return this.el.select('input.roo-' + this.inputType,true).first();
20272     },
20273     hiddenEl: function ()
20274     {
20275         return this.el.select('input.roo-hidden-value',true).first();
20276     },
20277     
20278     labelEl: function()
20279     {
20280         return this.el.select('label.control-label',true).first();
20281     },
20282     /* depricated... */
20283     
20284     label: function()
20285     {
20286         return this.labelEl();
20287     },
20288     
20289     boxLabelEl: function()
20290     {
20291         return this.el.select('label.box-label',true).first();
20292     },
20293     
20294     initEvents : function()
20295     {
20296 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20297         
20298         this.inputEl().on('click', this.onClick,  this);
20299         
20300         if (this.boxLabel) { 
20301             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20302         }
20303         
20304         this.startValue = this.getValue();
20305         
20306         if(this.groupId){
20307             Roo.bootstrap.CheckBox.register(this);
20308         }
20309     },
20310     
20311     onClick : function()
20312     {   
20313         this.setChecked(!this.checked);
20314     },
20315     
20316     setChecked : function(state,suppressEvent)
20317     {
20318         this.startValue = this.getValue();
20319
20320         if(this.inputType == 'radio'){
20321             
20322             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20323                 e.dom.checked = false;
20324             });
20325             
20326             this.inputEl().dom.checked = true;
20327             
20328             this.inputEl().dom.value = this.inputValue;
20329             
20330             if(suppressEvent !== true){
20331                 this.fireEvent('check', this, true);
20332             }
20333             
20334             this.validate();
20335             
20336             return;
20337         }
20338         
20339         this.checked = state;
20340         
20341         this.inputEl().dom.checked = state;
20342         
20343         
20344         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20345         
20346         if(suppressEvent !== true){
20347             this.fireEvent('check', this, state);
20348         }
20349         
20350         this.validate();
20351     },
20352     
20353     getValue : function()
20354     {
20355         if(this.inputType == 'radio'){
20356             return this.getGroupValue();
20357         }
20358         
20359         return this.hiddenEl().dom.value;
20360         
20361     },
20362     
20363     getGroupValue : function()
20364     {
20365         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20366             return '';
20367         }
20368         
20369         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20370     },
20371     
20372     setValue : function(v,suppressEvent)
20373     {
20374         if(this.inputType == 'radio'){
20375             this.setGroupValue(v, suppressEvent);
20376             return;
20377         }
20378         
20379         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20380         
20381         this.validate();
20382     },
20383     
20384     setGroupValue : function(v, suppressEvent)
20385     {
20386         this.startValue = this.getValue();
20387         
20388         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20389             e.dom.checked = false;
20390             
20391             if(e.dom.value == v){
20392                 e.dom.checked = true;
20393             }
20394         });
20395         
20396         if(suppressEvent !== true){
20397             this.fireEvent('check', this, true);
20398         }
20399
20400         this.validate();
20401         
20402         return;
20403     },
20404     
20405     validate : function()
20406     {
20407         if(
20408                 this.disabled || 
20409                 (this.inputType == 'radio' && this.validateRadio()) ||
20410                 (this.inputType == 'checkbox' && this.validateCheckbox())
20411         ){
20412             this.markValid();
20413             return true;
20414         }
20415         
20416         this.markInvalid();
20417         return false;
20418     },
20419     
20420     validateRadio : function()
20421     {
20422         if(this.allowBlank){
20423             return true;
20424         }
20425         
20426         var valid = false;
20427         
20428         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20429             if(!e.dom.checked){
20430                 return;
20431             }
20432             
20433             valid = true;
20434             
20435             return false;
20436         });
20437         
20438         return valid;
20439     },
20440     
20441     validateCheckbox : function()
20442     {
20443         if(!this.groupId){
20444             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20445             //return (this.getValue() == this.inputValue) ? true : false;
20446         }
20447         
20448         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20449         
20450         if(!group){
20451             return false;
20452         }
20453         
20454         var r = false;
20455         
20456         for(var i in group){
20457             if(r){
20458                 break;
20459             }
20460             
20461             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20462         }
20463         
20464         return r;
20465     },
20466     
20467     /**
20468      * Mark this field as valid
20469      */
20470     markValid : function()
20471     {
20472         var _this = this;
20473         
20474         this.fireEvent('valid', this);
20475         
20476         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20477         
20478         if(this.groupId){
20479             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20480         }
20481         
20482         if(label){
20483             label.markValid();
20484         }
20485
20486         if(this.inputType == 'radio'){
20487             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20488                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20489                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20490             });
20491             
20492             return;
20493         }
20494
20495         if(!this.groupId){
20496             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20497             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20498             return;
20499         }
20500         
20501         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20502         
20503         if(!group){
20504             return;
20505         }
20506         
20507         for(var i in group){
20508             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20509             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20510         }
20511     },
20512     
20513      /**
20514      * Mark this field as invalid
20515      * @param {String} msg The validation message
20516      */
20517     markInvalid : function(msg)
20518     {
20519         if(this.allowBlank){
20520             return;
20521         }
20522         
20523         var _this = this;
20524         
20525         this.fireEvent('invalid', this, msg);
20526         
20527         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20528         
20529         if(this.groupId){
20530             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20531         }
20532         
20533         if(label){
20534             label.markInvalid();
20535         }
20536             
20537         if(this.inputType == 'radio'){
20538             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20539                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20540                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20541             });
20542             
20543             return;
20544         }
20545         
20546         if(!this.groupId){
20547             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20548             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20549             return;
20550         }
20551         
20552         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20553         
20554         if(!group){
20555             return;
20556         }
20557         
20558         for(var i in group){
20559             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20560             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20561         }
20562         
20563     },
20564     
20565     clearInvalid : function()
20566     {
20567         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20568         
20569         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20570         
20571         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20572         
20573         if (label) {
20574             label.iconEl.removeClass(label.validClass);
20575             label.iconEl.removeClass(label.invalidClass);
20576         }
20577     },
20578     
20579     disable : function()
20580     {
20581         if(this.inputType != 'radio'){
20582             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20583             return;
20584         }
20585         
20586         var _this = this;
20587         
20588         if(this.rendered){
20589             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20590                 _this.getActionEl().addClass(this.disabledClass);
20591                 e.dom.disabled = true;
20592             });
20593         }
20594         
20595         this.disabled = true;
20596         this.fireEvent("disable", this);
20597         return this;
20598     },
20599
20600     enable : function()
20601     {
20602         if(this.inputType != 'radio'){
20603             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20604             return;
20605         }
20606         
20607         var _this = this;
20608         
20609         if(this.rendered){
20610             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20611                 _this.getActionEl().removeClass(this.disabledClass);
20612                 e.dom.disabled = false;
20613             });
20614         }
20615         
20616         this.disabled = false;
20617         this.fireEvent("enable", this);
20618         return this;
20619     }
20620
20621 });
20622
20623 Roo.apply(Roo.bootstrap.CheckBox, {
20624     
20625     groups: {},
20626     
20627      /**
20628     * register a CheckBox Group
20629     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20630     */
20631     register : function(checkbox)
20632     {
20633         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20634             this.groups[checkbox.groupId] = {};
20635         }
20636         
20637         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20638             return;
20639         }
20640         
20641         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20642         
20643     },
20644     /**
20645     * fetch a CheckBox Group based on the group ID
20646     * @param {string} the group ID
20647     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20648     */
20649     get: function(groupId) {
20650         if (typeof(this.groups[groupId]) == 'undefined') {
20651             return false;
20652         }
20653         
20654         return this.groups[groupId] ;
20655     }
20656     
20657     
20658 });
20659 /*
20660  * - LGPL
20661  *
20662  * RadioItem
20663  * 
20664  */
20665
20666 /**
20667  * @class Roo.bootstrap.Radio
20668  * @extends Roo.bootstrap.Component
20669  * Bootstrap Radio class
20670  * @cfg {String} boxLabel - the label associated
20671  * @cfg {String} value - the value of radio
20672  * 
20673  * @constructor
20674  * Create a new Radio
20675  * @param {Object} config The config object
20676  */
20677 Roo.bootstrap.Radio = function(config){
20678     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20679     
20680 };
20681
20682 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20683     
20684     boxLabel : '',
20685     
20686     value : '',
20687     
20688     getAutoCreate : function()
20689     {
20690         var cfg = {
20691             tag : 'div',
20692             cls : 'form-group radio',
20693             cn : [
20694                 {
20695                     tag : 'label',
20696                     cls : 'box-label',
20697                     html : this.boxLabel
20698                 }
20699             ]
20700         };
20701         
20702         return cfg;
20703     },
20704     
20705     initEvents : function() 
20706     {
20707         this.parent().register(this);
20708         
20709         this.el.on('click', this.onClick, this);
20710         
20711     },
20712     
20713     onClick : function()
20714     {
20715         this.setChecked(true);
20716     },
20717     
20718     setChecked : function(state, suppressEvent)
20719     {
20720         this.parent().setValue(this.value, suppressEvent);
20721         
20722     }
20723     
20724 });
20725  
20726
20727  /*
20728  * - LGPL
20729  *
20730  * Input
20731  * 
20732  */
20733
20734 /**
20735  * @class Roo.bootstrap.SecurePass
20736  * @extends Roo.bootstrap.Input
20737  * Bootstrap SecurePass class
20738  *
20739  * 
20740  * @constructor
20741  * Create a new SecurePass
20742  * @param {Object} config The config object
20743  */
20744  
20745 Roo.bootstrap.SecurePass = function (config) {
20746     // these go here, so the translation tool can replace them..
20747     this.errors = {
20748         PwdEmpty: "Please type a password, and then retype it to confirm.",
20749         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20750         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20751         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20752         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20753         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20754         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20755         TooWeak: "Your password is Too Weak."
20756     },
20757     this.meterLabel = "Password strength:";
20758     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20759     this.meterClass = [
20760         "roo-password-meter-tooweak", 
20761         "roo-password-meter-weak", 
20762         "roo-password-meter-medium", 
20763         "roo-password-meter-strong", 
20764         "roo-password-meter-grey"
20765     ];
20766     
20767     this.errors = {};
20768     
20769     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20770 }
20771
20772 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20773     /**
20774      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20775      * {
20776      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20777      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20778      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20779      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20780      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20781      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20782      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20783      * })
20784      */
20785     // private
20786     
20787     meterWidth: 300,
20788     errorMsg :'',    
20789     errors: false,
20790     imageRoot: '/',
20791     /**
20792      * @cfg {String/Object} Label for the strength meter (defaults to
20793      * 'Password strength:')
20794      */
20795     // private
20796     meterLabel: '',
20797     /**
20798      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20799      * ['Weak', 'Medium', 'Strong'])
20800      */
20801     // private    
20802     pwdStrengths: false,    
20803     // private
20804     strength: 0,
20805     // private
20806     _lastPwd: null,
20807     // private
20808     kCapitalLetter: 0,
20809     kSmallLetter: 1,
20810     kDigit: 2,
20811     kPunctuation: 3,
20812     
20813     insecure: false,
20814     // private
20815     initEvents: function ()
20816     {
20817         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20818
20819         if (this.el.is('input[type=password]') && Roo.isSafari) {
20820             this.el.on('keydown', this.SafariOnKeyDown, this);
20821         }
20822
20823         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20824     },
20825     // private
20826     onRender: function (ct, position)
20827     {
20828         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20829         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20830         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20831
20832         this.trigger.createChild({
20833                    cn: [
20834                     {
20835                     //id: 'PwdMeter',
20836                     tag: 'div',
20837                     cls: 'roo-password-meter-grey col-xs-12',
20838                     style: {
20839                         //width: 0,
20840                         //width: this.meterWidth + 'px'                                                
20841                         }
20842                     },
20843                     {                            
20844                          cls: 'roo-password-meter-text'                          
20845                     }
20846                 ]            
20847         });
20848
20849          
20850         if (this.hideTrigger) {
20851             this.trigger.setDisplayed(false);
20852         }
20853         this.setSize(this.width || '', this.height || '');
20854     },
20855     // private
20856     onDestroy: function ()
20857     {
20858         if (this.trigger) {
20859             this.trigger.removeAllListeners();
20860             this.trigger.remove();
20861         }
20862         if (this.wrap) {
20863             this.wrap.remove();
20864         }
20865         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20866     },
20867     // private
20868     checkStrength: function ()
20869     {
20870         var pwd = this.inputEl().getValue();
20871         if (pwd == this._lastPwd) {
20872             return;
20873         }
20874
20875         var strength;
20876         if (this.ClientSideStrongPassword(pwd)) {
20877             strength = 3;
20878         } else if (this.ClientSideMediumPassword(pwd)) {
20879             strength = 2;
20880         } else if (this.ClientSideWeakPassword(pwd)) {
20881             strength = 1;
20882         } else {
20883             strength = 0;
20884         }
20885         
20886         Roo.log('strength1: ' + strength);
20887         
20888         //var pm = this.trigger.child('div/div/div').dom;
20889         var pm = this.trigger.child('div/div');
20890         pm.removeClass(this.meterClass);
20891         pm.addClass(this.meterClass[strength]);
20892                 
20893         
20894         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20895                 
20896         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20897         
20898         this._lastPwd = pwd;
20899     },
20900     reset: function ()
20901     {
20902         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20903         
20904         this._lastPwd = '';
20905         
20906         var pm = this.trigger.child('div/div');
20907         pm.removeClass(this.meterClass);
20908         pm.addClass('roo-password-meter-grey');        
20909         
20910         
20911         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20912         
20913         pt.innerHTML = '';
20914         this.inputEl().dom.type='password';
20915     },
20916     // private
20917     validateValue: function (value)
20918     {
20919         
20920         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20921             return false;
20922         }
20923         if (value.length == 0) {
20924             if (this.allowBlank) {
20925                 this.clearInvalid();
20926                 return true;
20927             }
20928
20929             this.markInvalid(this.errors.PwdEmpty);
20930             this.errorMsg = this.errors.PwdEmpty;
20931             return false;
20932         }
20933         
20934         if(this.insecure){
20935             return true;
20936         }
20937         
20938         if ('[\x21-\x7e]*'.match(value)) {
20939             this.markInvalid(this.errors.PwdBadChar);
20940             this.errorMsg = this.errors.PwdBadChar;
20941             return false;
20942         }
20943         if (value.length < 6) {
20944             this.markInvalid(this.errors.PwdShort);
20945             this.errorMsg = this.errors.PwdShort;
20946             return false;
20947         }
20948         if (value.length > 16) {
20949             this.markInvalid(this.errors.PwdLong);
20950             this.errorMsg = this.errors.PwdLong;
20951             return false;
20952         }
20953         var strength;
20954         if (this.ClientSideStrongPassword(value)) {
20955             strength = 3;
20956         } else if (this.ClientSideMediumPassword(value)) {
20957             strength = 2;
20958         } else if (this.ClientSideWeakPassword(value)) {
20959             strength = 1;
20960         } else {
20961             strength = 0;
20962         }
20963
20964         
20965         if (strength < 2) {
20966             //this.markInvalid(this.errors.TooWeak);
20967             this.errorMsg = this.errors.TooWeak;
20968             //return false;
20969         }
20970         
20971         
20972         console.log('strength2: ' + strength);
20973         
20974         //var pm = this.trigger.child('div/div/div').dom;
20975         
20976         var pm = this.trigger.child('div/div');
20977         pm.removeClass(this.meterClass);
20978         pm.addClass(this.meterClass[strength]);
20979                 
20980         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20981                 
20982         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20983         
20984         this.errorMsg = ''; 
20985         return true;
20986     },
20987     // private
20988     CharacterSetChecks: function (type)
20989     {
20990         this.type = type;
20991         this.fResult = false;
20992     },
20993     // private
20994     isctype: function (character, type)
20995     {
20996         switch (type) {  
20997             case this.kCapitalLetter:
20998                 if (character >= 'A' && character <= 'Z') {
20999                     return true;
21000                 }
21001                 break;
21002             
21003             case this.kSmallLetter:
21004                 if (character >= 'a' && character <= 'z') {
21005                     return true;
21006                 }
21007                 break;
21008             
21009             case this.kDigit:
21010                 if (character >= '0' && character <= '9') {
21011                     return true;
21012                 }
21013                 break;
21014             
21015             case this.kPunctuation:
21016                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21017                     return true;
21018                 }
21019                 break;
21020             
21021             default:
21022                 return false;
21023         }
21024
21025     },
21026     // private
21027     IsLongEnough: function (pwd, size)
21028     {
21029         return !(pwd == null || isNaN(size) || pwd.length < size);
21030     },
21031     // private
21032     SpansEnoughCharacterSets: function (word, nb)
21033     {
21034         if (!this.IsLongEnough(word, nb))
21035         {
21036             return false;
21037         }
21038
21039         var characterSetChecks = new Array(
21040             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21041             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21042         );
21043         
21044         for (var index = 0; index < word.length; ++index) {
21045             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21046                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21047                     characterSetChecks[nCharSet].fResult = true;
21048                     break;
21049                 }
21050             }
21051         }
21052
21053         var nCharSets = 0;
21054         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21055             if (characterSetChecks[nCharSet].fResult) {
21056                 ++nCharSets;
21057             }
21058         }
21059
21060         if (nCharSets < nb) {
21061             return false;
21062         }
21063         return true;
21064     },
21065     // private
21066     ClientSideStrongPassword: function (pwd)
21067     {
21068         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21069     },
21070     // private
21071     ClientSideMediumPassword: function (pwd)
21072     {
21073         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21074     },
21075     // private
21076     ClientSideWeakPassword: function (pwd)
21077     {
21078         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21079     }
21080           
21081 })//<script type="text/javascript">
21082
21083 /*
21084  * Based  Ext JS Library 1.1.1
21085  * Copyright(c) 2006-2007, Ext JS, LLC.
21086  * LGPL
21087  *
21088  */
21089  
21090 /**
21091  * @class Roo.HtmlEditorCore
21092  * @extends Roo.Component
21093  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21094  *
21095  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21096  */
21097
21098 Roo.HtmlEditorCore = function(config){
21099     
21100     
21101     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21102     
21103     
21104     this.addEvents({
21105         /**
21106          * @event initialize
21107          * Fires when the editor is fully initialized (including the iframe)
21108          * @param {Roo.HtmlEditorCore} this
21109          */
21110         initialize: true,
21111         /**
21112          * @event activate
21113          * Fires when the editor is first receives the focus. Any insertion must wait
21114          * until after this event.
21115          * @param {Roo.HtmlEditorCore} this
21116          */
21117         activate: true,
21118          /**
21119          * @event beforesync
21120          * Fires before the textarea is updated with content from the editor iframe. Return false
21121          * to cancel the sync.
21122          * @param {Roo.HtmlEditorCore} this
21123          * @param {String} html
21124          */
21125         beforesync: true,
21126          /**
21127          * @event beforepush
21128          * Fires before the iframe editor is updated with content from the textarea. Return false
21129          * to cancel the push.
21130          * @param {Roo.HtmlEditorCore} this
21131          * @param {String} html
21132          */
21133         beforepush: true,
21134          /**
21135          * @event sync
21136          * Fires when the textarea is updated with content from the editor iframe.
21137          * @param {Roo.HtmlEditorCore} this
21138          * @param {String} html
21139          */
21140         sync: true,
21141          /**
21142          * @event push
21143          * Fires when the iframe editor is updated with content from the textarea.
21144          * @param {Roo.HtmlEditorCore} this
21145          * @param {String} html
21146          */
21147         push: true,
21148         
21149         /**
21150          * @event editorevent
21151          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21152          * @param {Roo.HtmlEditorCore} this
21153          */
21154         editorevent: true
21155         
21156     });
21157     
21158     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21159     
21160     // defaults : white / black...
21161     this.applyBlacklists();
21162     
21163     
21164     
21165 };
21166
21167
21168 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21169
21170
21171      /**
21172      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21173      */
21174     
21175     owner : false,
21176     
21177      /**
21178      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21179      *                        Roo.resizable.
21180      */
21181     resizable : false,
21182      /**
21183      * @cfg {Number} height (in pixels)
21184      */   
21185     height: 300,
21186    /**
21187      * @cfg {Number} width (in pixels)
21188      */   
21189     width: 500,
21190     
21191     /**
21192      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21193      * 
21194      */
21195     stylesheets: false,
21196     
21197     // id of frame..
21198     frameId: false,
21199     
21200     // private properties
21201     validationEvent : false,
21202     deferHeight: true,
21203     initialized : false,
21204     activated : false,
21205     sourceEditMode : false,
21206     onFocus : Roo.emptyFn,
21207     iframePad:3,
21208     hideMode:'offsets',
21209     
21210     clearUp: true,
21211     
21212     // blacklist + whitelisted elements..
21213     black: false,
21214     white: false,
21215      
21216     
21217
21218     /**
21219      * Protected method that will not generally be called directly. It
21220      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21221      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21222      */
21223     getDocMarkup : function(){
21224         // body styles..
21225         var st = '';
21226         
21227         // inherit styels from page...?? 
21228         if (this.stylesheets === false) {
21229             
21230             Roo.get(document.head).select('style').each(function(node) {
21231                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21232             });
21233             
21234             Roo.get(document.head).select('link').each(function(node) { 
21235                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21236             });
21237             
21238         } else if (!this.stylesheets.length) {
21239                 // simple..
21240                 st = '<style type="text/css">' +
21241                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21242                    '</style>';
21243         } else { 
21244             
21245         }
21246         
21247         st +=  '<style type="text/css">' +
21248             'IMG { cursor: pointer } ' +
21249         '</style>';
21250
21251         
21252         return '<html><head>' + st  +
21253             //<style type="text/css">' +
21254             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21255             //'</style>' +
21256             ' </head><body class="roo-htmleditor-body"></body></html>';
21257     },
21258
21259     // private
21260     onRender : function(ct, position)
21261     {
21262         var _t = this;
21263         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21264         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21265         
21266         
21267         this.el.dom.style.border = '0 none';
21268         this.el.dom.setAttribute('tabIndex', -1);
21269         this.el.addClass('x-hidden hide');
21270         
21271         
21272         
21273         if(Roo.isIE){ // fix IE 1px bogus margin
21274             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21275         }
21276        
21277         
21278         this.frameId = Roo.id();
21279         
21280          
21281         
21282         var iframe = this.owner.wrap.createChild({
21283             tag: 'iframe',
21284             cls: 'form-control', // bootstrap..
21285             id: this.frameId,
21286             name: this.frameId,
21287             frameBorder : 'no',
21288             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21289         }, this.el
21290         );
21291         
21292         
21293         this.iframe = iframe.dom;
21294
21295          this.assignDocWin();
21296         
21297         this.doc.designMode = 'on';
21298        
21299         this.doc.open();
21300         this.doc.write(this.getDocMarkup());
21301         this.doc.close();
21302
21303         
21304         var task = { // must defer to wait for browser to be ready
21305             run : function(){
21306                 //console.log("run task?" + this.doc.readyState);
21307                 this.assignDocWin();
21308                 if(this.doc.body || this.doc.readyState == 'complete'){
21309                     try {
21310                         this.doc.designMode="on";
21311                     } catch (e) {
21312                         return;
21313                     }
21314                     Roo.TaskMgr.stop(task);
21315                     this.initEditor.defer(10, this);
21316                 }
21317             },
21318             interval : 10,
21319             duration: 10000,
21320             scope: this
21321         };
21322         Roo.TaskMgr.start(task);
21323
21324     },
21325
21326     // private
21327     onResize : function(w, h)
21328     {
21329          Roo.log('resize: ' +w + ',' + h );
21330         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21331         if(!this.iframe){
21332             return;
21333         }
21334         if(typeof w == 'number'){
21335             
21336             this.iframe.style.width = w + 'px';
21337         }
21338         if(typeof h == 'number'){
21339             
21340             this.iframe.style.height = h + 'px';
21341             if(this.doc){
21342                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21343             }
21344         }
21345         
21346     },
21347
21348     /**
21349      * Toggles the editor between standard and source edit mode.
21350      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21351      */
21352     toggleSourceEdit : function(sourceEditMode){
21353         
21354         this.sourceEditMode = sourceEditMode === true;
21355         
21356         if(this.sourceEditMode){
21357  
21358             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21359             
21360         }else{
21361             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21362             //this.iframe.className = '';
21363             this.deferFocus();
21364         }
21365         //this.setSize(this.owner.wrap.getSize());
21366         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21367     },
21368
21369     
21370   
21371
21372     /**
21373      * Protected method that will not generally be called directly. If you need/want
21374      * custom HTML cleanup, this is the method you should override.
21375      * @param {String} html The HTML to be cleaned
21376      * return {String} The cleaned HTML
21377      */
21378     cleanHtml : function(html){
21379         html = String(html);
21380         if(html.length > 5){
21381             if(Roo.isSafari){ // strip safari nonsense
21382                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21383             }
21384         }
21385         if(html == '&nbsp;'){
21386             html = '';
21387         }
21388         return html;
21389     },
21390
21391     /**
21392      * HTML Editor -> Textarea
21393      * Protected method that will not generally be called directly. Syncs the contents
21394      * of the editor iframe with the textarea.
21395      */
21396     syncValue : function(){
21397         if(this.initialized){
21398             var bd = (this.doc.body || this.doc.documentElement);
21399             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21400             var html = bd.innerHTML;
21401             if(Roo.isSafari){
21402                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21403                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21404                 if(m && m[1]){
21405                     html = '<div style="'+m[0]+'">' + html + '</div>';
21406                 }
21407             }
21408             html = this.cleanHtml(html);
21409             // fix up the special chars.. normaly like back quotes in word...
21410             // however we do not want to do this with chinese..
21411             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21412                 var cc = b.charCodeAt();
21413                 if (
21414                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21415                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21416                     (cc >= 0xf900 && cc < 0xfb00 )
21417                 ) {
21418                         return b;
21419                 }
21420                 return "&#"+cc+";" 
21421             });
21422             if(this.owner.fireEvent('beforesync', this, html) !== false){
21423                 this.el.dom.value = html;
21424                 this.owner.fireEvent('sync', this, html);
21425             }
21426         }
21427     },
21428
21429     /**
21430      * Protected method that will not generally be called directly. Pushes the value of the textarea
21431      * into the iframe editor.
21432      */
21433     pushValue : function(){
21434         if(this.initialized){
21435             var v = this.el.dom.value.trim();
21436             
21437 //            if(v.length < 1){
21438 //                v = '&#160;';
21439 //            }
21440             
21441             if(this.owner.fireEvent('beforepush', this, v) !== false){
21442                 var d = (this.doc.body || this.doc.documentElement);
21443                 d.innerHTML = v;
21444                 this.cleanUpPaste();
21445                 this.el.dom.value = d.innerHTML;
21446                 this.owner.fireEvent('push', this, v);
21447             }
21448         }
21449     },
21450
21451     // private
21452     deferFocus : function(){
21453         this.focus.defer(10, this);
21454     },
21455
21456     // doc'ed in Field
21457     focus : function(){
21458         if(this.win && !this.sourceEditMode){
21459             this.win.focus();
21460         }else{
21461             this.el.focus();
21462         }
21463     },
21464     
21465     assignDocWin: function()
21466     {
21467         var iframe = this.iframe;
21468         
21469          if(Roo.isIE){
21470             this.doc = iframe.contentWindow.document;
21471             this.win = iframe.contentWindow;
21472         } else {
21473 //            if (!Roo.get(this.frameId)) {
21474 //                return;
21475 //            }
21476 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21477 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21478             
21479             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21480                 return;
21481             }
21482             
21483             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21484             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21485         }
21486     },
21487     
21488     // private
21489     initEditor : function(){
21490         //console.log("INIT EDITOR");
21491         this.assignDocWin();
21492         
21493         
21494         
21495         this.doc.designMode="on";
21496         this.doc.open();
21497         this.doc.write(this.getDocMarkup());
21498         this.doc.close();
21499         
21500         var dbody = (this.doc.body || this.doc.documentElement);
21501         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21502         // this copies styles from the containing element into thsi one..
21503         // not sure why we need all of this..
21504         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21505         
21506         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21507         //ss['background-attachment'] = 'fixed'; // w3c
21508         dbody.bgProperties = 'fixed'; // ie
21509         //Roo.DomHelper.applyStyles(dbody, ss);
21510         Roo.EventManager.on(this.doc, {
21511             //'mousedown': this.onEditorEvent,
21512             'mouseup': this.onEditorEvent,
21513             'dblclick': this.onEditorEvent,
21514             'click': this.onEditorEvent,
21515             'keyup': this.onEditorEvent,
21516             buffer:100,
21517             scope: this
21518         });
21519         if(Roo.isGecko){
21520             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21521         }
21522         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21523             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21524         }
21525         this.initialized = true;
21526
21527         this.owner.fireEvent('initialize', this);
21528         this.pushValue();
21529     },
21530
21531     // private
21532     onDestroy : function(){
21533         
21534         
21535         
21536         if(this.rendered){
21537             
21538             //for (var i =0; i < this.toolbars.length;i++) {
21539             //    // fixme - ask toolbars for heights?
21540             //    this.toolbars[i].onDestroy();
21541            // }
21542             
21543             //this.wrap.dom.innerHTML = '';
21544             //this.wrap.remove();
21545         }
21546     },
21547
21548     // private
21549     onFirstFocus : function(){
21550         
21551         this.assignDocWin();
21552         
21553         
21554         this.activated = true;
21555          
21556     
21557         if(Roo.isGecko){ // prevent silly gecko errors
21558             this.win.focus();
21559             var s = this.win.getSelection();
21560             if(!s.focusNode || s.focusNode.nodeType != 3){
21561                 var r = s.getRangeAt(0);
21562                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21563                 r.collapse(true);
21564                 this.deferFocus();
21565             }
21566             try{
21567                 this.execCmd('useCSS', true);
21568                 this.execCmd('styleWithCSS', false);
21569             }catch(e){}
21570         }
21571         this.owner.fireEvent('activate', this);
21572     },
21573
21574     // private
21575     adjustFont: function(btn){
21576         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21577         //if(Roo.isSafari){ // safari
21578         //    adjust *= 2;
21579        // }
21580         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21581         if(Roo.isSafari){ // safari
21582             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21583             v =  (v < 10) ? 10 : v;
21584             v =  (v > 48) ? 48 : v;
21585             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21586             
21587         }
21588         
21589         
21590         v = Math.max(1, v+adjust);
21591         
21592         this.execCmd('FontSize', v  );
21593     },
21594
21595     onEditorEvent : function(e)
21596     {
21597         this.owner.fireEvent('editorevent', this, e);
21598       //  this.updateToolbar();
21599         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21600     },
21601
21602     insertTag : function(tg)
21603     {
21604         // could be a bit smarter... -> wrap the current selected tRoo..
21605         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21606             
21607             range = this.createRange(this.getSelection());
21608             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21609             wrappingNode.appendChild(range.extractContents());
21610             range.insertNode(wrappingNode);
21611
21612             return;
21613             
21614             
21615             
21616         }
21617         this.execCmd("formatblock",   tg);
21618         
21619     },
21620     
21621     insertText : function(txt)
21622     {
21623         
21624         
21625         var range = this.createRange();
21626         range.deleteContents();
21627                //alert(Sender.getAttribute('label'));
21628                
21629         range.insertNode(this.doc.createTextNode(txt));
21630     } ,
21631     
21632      
21633
21634     /**
21635      * Executes a Midas editor command on the editor document and performs necessary focus and
21636      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21637      * @param {String} cmd The Midas command
21638      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21639      */
21640     relayCmd : function(cmd, value){
21641         this.win.focus();
21642         this.execCmd(cmd, value);
21643         this.owner.fireEvent('editorevent', this);
21644         //this.updateToolbar();
21645         this.owner.deferFocus();
21646     },
21647
21648     /**
21649      * Executes a Midas editor command directly on the editor document.
21650      * For visual commands, you should use {@link #relayCmd} instead.
21651      * <b>This should only be called after the editor is initialized.</b>
21652      * @param {String} cmd The Midas command
21653      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21654      */
21655     execCmd : function(cmd, value){
21656         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21657         this.syncValue();
21658     },
21659  
21660  
21661    
21662     /**
21663      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21664      * to insert tRoo.
21665      * @param {String} text | dom node.. 
21666      */
21667     insertAtCursor : function(text)
21668     {
21669         
21670         if(!this.activated){
21671             return;
21672         }
21673         /*
21674         if(Roo.isIE){
21675             this.win.focus();
21676             var r = this.doc.selection.createRange();
21677             if(r){
21678                 r.collapse(true);
21679                 r.pasteHTML(text);
21680                 this.syncValue();
21681                 this.deferFocus();
21682             
21683             }
21684             return;
21685         }
21686         */
21687         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21688             this.win.focus();
21689             
21690             
21691             // from jquery ui (MIT licenced)
21692             var range, node;
21693             var win = this.win;
21694             
21695             if (win.getSelection && win.getSelection().getRangeAt) {
21696                 range = win.getSelection().getRangeAt(0);
21697                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21698                 range.insertNode(node);
21699             } else if (win.document.selection && win.document.selection.createRange) {
21700                 // no firefox support
21701                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21702                 win.document.selection.createRange().pasteHTML(txt);
21703             } else {
21704                 // no firefox support
21705                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21706                 this.execCmd('InsertHTML', txt);
21707             } 
21708             
21709             this.syncValue();
21710             
21711             this.deferFocus();
21712         }
21713     },
21714  // private
21715     mozKeyPress : function(e){
21716         if(e.ctrlKey){
21717             var c = e.getCharCode(), cmd;
21718           
21719             if(c > 0){
21720                 c = String.fromCharCode(c).toLowerCase();
21721                 switch(c){
21722                     case 'b':
21723                         cmd = 'bold';
21724                         break;
21725                     case 'i':
21726                         cmd = 'italic';
21727                         break;
21728                     
21729                     case 'u':
21730                         cmd = 'underline';
21731                         break;
21732                     
21733                     case 'v':
21734                         this.cleanUpPaste.defer(100, this);
21735                         return;
21736                         
21737                 }
21738                 if(cmd){
21739                     this.win.focus();
21740                     this.execCmd(cmd);
21741                     this.deferFocus();
21742                     e.preventDefault();
21743                 }
21744                 
21745             }
21746         }
21747     },
21748
21749     // private
21750     fixKeys : function(){ // load time branching for fastest keydown performance
21751         if(Roo.isIE){
21752             return function(e){
21753                 var k = e.getKey(), r;
21754                 if(k == e.TAB){
21755                     e.stopEvent();
21756                     r = this.doc.selection.createRange();
21757                     if(r){
21758                         r.collapse(true);
21759                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21760                         this.deferFocus();
21761                     }
21762                     return;
21763                 }
21764                 
21765                 if(k == e.ENTER){
21766                     r = this.doc.selection.createRange();
21767                     if(r){
21768                         var target = r.parentElement();
21769                         if(!target || target.tagName.toLowerCase() != 'li'){
21770                             e.stopEvent();
21771                             r.pasteHTML('<br />');
21772                             r.collapse(false);
21773                             r.select();
21774                         }
21775                     }
21776                 }
21777                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21778                     this.cleanUpPaste.defer(100, this);
21779                     return;
21780                 }
21781                 
21782                 
21783             };
21784         }else if(Roo.isOpera){
21785             return function(e){
21786                 var k = e.getKey();
21787                 if(k == e.TAB){
21788                     e.stopEvent();
21789                     this.win.focus();
21790                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21791                     this.deferFocus();
21792                 }
21793                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21794                     this.cleanUpPaste.defer(100, this);
21795                     return;
21796                 }
21797                 
21798             };
21799         }else if(Roo.isSafari){
21800             return function(e){
21801                 var k = e.getKey();
21802                 
21803                 if(k == e.TAB){
21804                     e.stopEvent();
21805                     this.execCmd('InsertText','\t');
21806                     this.deferFocus();
21807                     return;
21808                 }
21809                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21810                     this.cleanUpPaste.defer(100, this);
21811                     return;
21812                 }
21813                 
21814              };
21815         }
21816     }(),
21817     
21818     getAllAncestors: function()
21819     {
21820         var p = this.getSelectedNode();
21821         var a = [];
21822         if (!p) {
21823             a.push(p); // push blank onto stack..
21824             p = this.getParentElement();
21825         }
21826         
21827         
21828         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21829             a.push(p);
21830             p = p.parentNode;
21831         }
21832         a.push(this.doc.body);
21833         return a;
21834     },
21835     lastSel : false,
21836     lastSelNode : false,
21837     
21838     
21839     getSelection : function() 
21840     {
21841         this.assignDocWin();
21842         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21843     },
21844     
21845     getSelectedNode: function() 
21846     {
21847         // this may only work on Gecko!!!
21848         
21849         // should we cache this!!!!
21850         
21851         
21852         
21853          
21854         var range = this.createRange(this.getSelection()).cloneRange();
21855         
21856         if (Roo.isIE) {
21857             var parent = range.parentElement();
21858             while (true) {
21859                 var testRange = range.duplicate();
21860                 testRange.moveToElementText(parent);
21861                 if (testRange.inRange(range)) {
21862                     break;
21863                 }
21864                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21865                     break;
21866                 }
21867                 parent = parent.parentElement;
21868             }
21869             return parent;
21870         }
21871         
21872         // is ancestor a text element.
21873         var ac =  range.commonAncestorContainer;
21874         if (ac.nodeType == 3) {
21875             ac = ac.parentNode;
21876         }
21877         
21878         var ar = ac.childNodes;
21879          
21880         var nodes = [];
21881         var other_nodes = [];
21882         var has_other_nodes = false;
21883         for (var i=0;i<ar.length;i++) {
21884             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21885                 continue;
21886             }
21887             // fullly contained node.
21888             
21889             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21890                 nodes.push(ar[i]);
21891                 continue;
21892             }
21893             
21894             // probably selected..
21895             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21896                 other_nodes.push(ar[i]);
21897                 continue;
21898             }
21899             // outer..
21900             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21901                 continue;
21902             }
21903             
21904             
21905             has_other_nodes = true;
21906         }
21907         if (!nodes.length && other_nodes.length) {
21908             nodes= other_nodes;
21909         }
21910         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21911             return false;
21912         }
21913         
21914         return nodes[0];
21915     },
21916     createRange: function(sel)
21917     {
21918         // this has strange effects when using with 
21919         // top toolbar - not sure if it's a great idea.
21920         //this.editor.contentWindow.focus();
21921         if (typeof sel != "undefined") {
21922             try {
21923                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21924             } catch(e) {
21925                 return this.doc.createRange();
21926             }
21927         } else {
21928             return this.doc.createRange();
21929         }
21930     },
21931     getParentElement: function()
21932     {
21933         
21934         this.assignDocWin();
21935         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21936         
21937         var range = this.createRange(sel);
21938          
21939         try {
21940             var p = range.commonAncestorContainer;
21941             while (p.nodeType == 3) { // text node
21942                 p = p.parentNode;
21943             }
21944             return p;
21945         } catch (e) {
21946             return null;
21947         }
21948     
21949     },
21950     /***
21951      *
21952      * Range intersection.. the hard stuff...
21953      *  '-1' = before
21954      *  '0' = hits..
21955      *  '1' = after.
21956      *         [ -- selected range --- ]
21957      *   [fail]                        [fail]
21958      *
21959      *    basically..
21960      *      if end is before start or  hits it. fail.
21961      *      if start is after end or hits it fail.
21962      *
21963      *   if either hits (but other is outside. - then it's not 
21964      *   
21965      *    
21966      **/
21967     
21968     
21969     // @see http://www.thismuchiknow.co.uk/?p=64.
21970     rangeIntersectsNode : function(range, node)
21971     {
21972         var nodeRange = node.ownerDocument.createRange();
21973         try {
21974             nodeRange.selectNode(node);
21975         } catch (e) {
21976             nodeRange.selectNodeContents(node);
21977         }
21978     
21979         var rangeStartRange = range.cloneRange();
21980         rangeStartRange.collapse(true);
21981     
21982         var rangeEndRange = range.cloneRange();
21983         rangeEndRange.collapse(false);
21984     
21985         var nodeStartRange = nodeRange.cloneRange();
21986         nodeStartRange.collapse(true);
21987     
21988         var nodeEndRange = nodeRange.cloneRange();
21989         nodeEndRange.collapse(false);
21990     
21991         return rangeStartRange.compareBoundaryPoints(
21992                  Range.START_TO_START, nodeEndRange) == -1 &&
21993                rangeEndRange.compareBoundaryPoints(
21994                  Range.START_TO_START, nodeStartRange) == 1;
21995         
21996          
21997     },
21998     rangeCompareNode : function(range, node)
21999     {
22000         var nodeRange = node.ownerDocument.createRange();
22001         try {
22002             nodeRange.selectNode(node);
22003         } catch (e) {
22004             nodeRange.selectNodeContents(node);
22005         }
22006         
22007         
22008         range.collapse(true);
22009     
22010         nodeRange.collapse(true);
22011      
22012         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22013         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22014          
22015         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22016         
22017         var nodeIsBefore   =  ss == 1;
22018         var nodeIsAfter    = ee == -1;
22019         
22020         if (nodeIsBefore && nodeIsAfter) {
22021             return 0; // outer
22022         }
22023         if (!nodeIsBefore && nodeIsAfter) {
22024             return 1; //right trailed.
22025         }
22026         
22027         if (nodeIsBefore && !nodeIsAfter) {
22028             return 2;  // left trailed.
22029         }
22030         // fully contined.
22031         return 3;
22032     },
22033
22034     // private? - in a new class?
22035     cleanUpPaste :  function()
22036     {
22037         // cleans up the whole document..
22038         Roo.log('cleanuppaste');
22039         
22040         this.cleanUpChildren(this.doc.body);
22041         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22042         if (clean != this.doc.body.innerHTML) {
22043             this.doc.body.innerHTML = clean;
22044         }
22045         
22046     },
22047     
22048     cleanWordChars : function(input) {// change the chars to hex code
22049         var he = Roo.HtmlEditorCore;
22050         
22051         var output = input;
22052         Roo.each(he.swapCodes, function(sw) { 
22053             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22054             
22055             output = output.replace(swapper, sw[1]);
22056         });
22057         
22058         return output;
22059     },
22060     
22061     
22062     cleanUpChildren : function (n)
22063     {
22064         if (!n.childNodes.length) {
22065             return;
22066         }
22067         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22068            this.cleanUpChild(n.childNodes[i]);
22069         }
22070     },
22071     
22072     
22073         
22074     
22075     cleanUpChild : function (node)
22076     {
22077         var ed = this;
22078         //console.log(node);
22079         if (node.nodeName == "#text") {
22080             // clean up silly Windows -- stuff?
22081             return; 
22082         }
22083         if (node.nodeName == "#comment") {
22084             node.parentNode.removeChild(node);
22085             // clean up silly Windows -- stuff?
22086             return; 
22087         }
22088         var lcname = node.tagName.toLowerCase();
22089         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22090         // whitelist of tags..
22091         
22092         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22093             // remove node.
22094             node.parentNode.removeChild(node);
22095             return;
22096             
22097         }
22098         
22099         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22100         
22101         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22102         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22103         
22104         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22105         //    remove_keep_children = true;
22106         //}
22107         
22108         if (remove_keep_children) {
22109             this.cleanUpChildren(node);
22110             // inserts everything just before this node...
22111             while (node.childNodes.length) {
22112                 var cn = node.childNodes[0];
22113                 node.removeChild(cn);
22114                 node.parentNode.insertBefore(cn, node);
22115             }
22116             node.parentNode.removeChild(node);
22117             return;
22118         }
22119         
22120         if (!node.attributes || !node.attributes.length) {
22121             this.cleanUpChildren(node);
22122             return;
22123         }
22124         
22125         function cleanAttr(n,v)
22126         {
22127             
22128             if (v.match(/^\./) || v.match(/^\//)) {
22129                 return;
22130             }
22131             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22132                 return;
22133             }
22134             if (v.match(/^#/)) {
22135                 return;
22136             }
22137 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22138             node.removeAttribute(n);
22139             
22140         }
22141         
22142         var cwhite = this.cwhite;
22143         var cblack = this.cblack;
22144             
22145         function cleanStyle(n,v)
22146         {
22147             if (v.match(/expression/)) { //XSS?? should we even bother..
22148                 node.removeAttribute(n);
22149                 return;
22150             }
22151             
22152             var parts = v.split(/;/);
22153             var clean = [];
22154             
22155             Roo.each(parts, function(p) {
22156                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22157                 if (!p.length) {
22158                     return true;
22159                 }
22160                 var l = p.split(':').shift().replace(/\s+/g,'');
22161                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22162                 
22163                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22164 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22165                     //node.removeAttribute(n);
22166                     return true;
22167                 }
22168                 //Roo.log()
22169                 // only allow 'c whitelisted system attributes'
22170                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22171 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22172                     //node.removeAttribute(n);
22173                     return true;
22174                 }
22175                 
22176                 
22177                  
22178                 
22179                 clean.push(p);
22180                 return true;
22181             });
22182             if (clean.length) { 
22183                 node.setAttribute(n, clean.join(';'));
22184             } else {
22185                 node.removeAttribute(n);
22186             }
22187             
22188         }
22189         
22190         
22191         for (var i = node.attributes.length-1; i > -1 ; i--) {
22192             var a = node.attributes[i];
22193             //console.log(a);
22194             
22195             if (a.name.toLowerCase().substr(0,2)=='on')  {
22196                 node.removeAttribute(a.name);
22197                 continue;
22198             }
22199             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22200                 node.removeAttribute(a.name);
22201                 continue;
22202             }
22203             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22204                 cleanAttr(a.name,a.value); // fixme..
22205                 continue;
22206             }
22207             if (a.name == 'style') {
22208                 cleanStyle(a.name,a.value);
22209                 continue;
22210             }
22211             /// clean up MS crap..
22212             // tecnically this should be a list of valid class'es..
22213             
22214             
22215             if (a.name == 'class') {
22216                 if (a.value.match(/^Mso/)) {
22217                     node.className = '';
22218                 }
22219                 
22220                 if (a.value.match(/^body$/)) {
22221                     node.className = '';
22222                 }
22223                 continue;
22224             }
22225             
22226             // style cleanup!?
22227             // class cleanup?
22228             
22229         }
22230         
22231         
22232         this.cleanUpChildren(node);
22233         
22234         
22235     },
22236     
22237     /**
22238      * Clean up MS wordisms...
22239      */
22240     cleanWord : function(node)
22241     {
22242         
22243         
22244         if (!node) {
22245             this.cleanWord(this.doc.body);
22246             return;
22247         }
22248         if (node.nodeName == "#text") {
22249             // clean up silly Windows -- stuff?
22250             return; 
22251         }
22252         if (node.nodeName == "#comment") {
22253             node.parentNode.removeChild(node);
22254             // clean up silly Windows -- stuff?
22255             return; 
22256         }
22257         
22258         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22259             node.parentNode.removeChild(node);
22260             return;
22261         }
22262         
22263         // remove - but keep children..
22264         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22265             while (node.childNodes.length) {
22266                 var cn = node.childNodes[0];
22267                 node.removeChild(cn);
22268                 node.parentNode.insertBefore(cn, node);
22269             }
22270             node.parentNode.removeChild(node);
22271             this.iterateChildren(node, this.cleanWord);
22272             return;
22273         }
22274         // clean styles
22275         if (node.className.length) {
22276             
22277             var cn = node.className.split(/\W+/);
22278             var cna = [];
22279             Roo.each(cn, function(cls) {
22280                 if (cls.match(/Mso[a-zA-Z]+/)) {
22281                     return;
22282                 }
22283                 cna.push(cls);
22284             });
22285             node.className = cna.length ? cna.join(' ') : '';
22286             if (!cna.length) {
22287                 node.removeAttribute("class");
22288             }
22289         }
22290         
22291         if (node.hasAttribute("lang")) {
22292             node.removeAttribute("lang");
22293         }
22294         
22295         if (node.hasAttribute("style")) {
22296             
22297             var styles = node.getAttribute("style").split(";");
22298             var nstyle = [];
22299             Roo.each(styles, function(s) {
22300                 if (!s.match(/:/)) {
22301                     return;
22302                 }
22303                 var kv = s.split(":");
22304                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22305                     return;
22306                 }
22307                 // what ever is left... we allow.
22308                 nstyle.push(s);
22309             });
22310             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22311             if (!nstyle.length) {
22312                 node.removeAttribute('style');
22313             }
22314         }
22315         this.iterateChildren(node, this.cleanWord);
22316         
22317         
22318         
22319     },
22320     /**
22321      * iterateChildren of a Node, calling fn each time, using this as the scole..
22322      * @param {DomNode} node node to iterate children of.
22323      * @param {Function} fn method of this class to call on each item.
22324      */
22325     iterateChildren : function(node, fn)
22326     {
22327         if (!node.childNodes.length) {
22328                 return;
22329         }
22330         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22331            fn.call(this, node.childNodes[i])
22332         }
22333     },
22334     
22335     
22336     /**
22337      * cleanTableWidths.
22338      *
22339      * Quite often pasting from word etc.. results in tables with column and widths.
22340      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22341      *
22342      */
22343     cleanTableWidths : function(node)
22344     {
22345          
22346          
22347         if (!node) {
22348             this.cleanTableWidths(this.doc.body);
22349             return;
22350         }
22351         
22352         // ignore list...
22353         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22354             return; 
22355         }
22356         Roo.log(node.tagName);
22357         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22358             this.iterateChildren(node, this.cleanTableWidths);
22359             return;
22360         }
22361         if (node.hasAttribute('width')) {
22362             node.removeAttribute('width');
22363         }
22364         
22365          
22366         if (node.hasAttribute("style")) {
22367             // pretty basic...
22368             
22369             var styles = node.getAttribute("style").split(";");
22370             var nstyle = [];
22371             Roo.each(styles, function(s) {
22372                 if (!s.match(/:/)) {
22373                     return;
22374                 }
22375                 var kv = s.split(":");
22376                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22377                     return;
22378                 }
22379                 // what ever is left... we allow.
22380                 nstyle.push(s);
22381             });
22382             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22383             if (!nstyle.length) {
22384                 node.removeAttribute('style');
22385             }
22386         }
22387         
22388         this.iterateChildren(node, this.cleanTableWidths);
22389         
22390         
22391     },
22392     
22393     
22394     
22395     
22396     domToHTML : function(currentElement, depth, nopadtext) {
22397         
22398         depth = depth || 0;
22399         nopadtext = nopadtext || false;
22400     
22401         if (!currentElement) {
22402             return this.domToHTML(this.doc.body);
22403         }
22404         
22405         //Roo.log(currentElement);
22406         var j;
22407         var allText = false;
22408         var nodeName = currentElement.nodeName;
22409         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22410         
22411         if  (nodeName == '#text') {
22412             
22413             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22414         }
22415         
22416         
22417         var ret = '';
22418         if (nodeName != 'BODY') {
22419              
22420             var i = 0;
22421             // Prints the node tagName, such as <A>, <IMG>, etc
22422             if (tagName) {
22423                 var attr = [];
22424                 for(i = 0; i < currentElement.attributes.length;i++) {
22425                     // quoting?
22426                     var aname = currentElement.attributes.item(i).name;
22427                     if (!currentElement.attributes.item(i).value.length) {
22428                         continue;
22429                     }
22430                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22431                 }
22432                 
22433                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22434             } 
22435             else {
22436                 
22437                 // eack
22438             }
22439         } else {
22440             tagName = false;
22441         }
22442         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22443             return ret;
22444         }
22445         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22446             nopadtext = true;
22447         }
22448         
22449         
22450         // Traverse the tree
22451         i = 0;
22452         var currentElementChild = currentElement.childNodes.item(i);
22453         var allText = true;
22454         var innerHTML  = '';
22455         lastnode = '';
22456         while (currentElementChild) {
22457             // Formatting code (indent the tree so it looks nice on the screen)
22458             var nopad = nopadtext;
22459             if (lastnode == 'SPAN') {
22460                 nopad  = true;
22461             }
22462             // text
22463             if  (currentElementChild.nodeName == '#text') {
22464                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22465                 toadd = nopadtext ? toadd : toadd.trim();
22466                 if (!nopad && toadd.length > 80) {
22467                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22468                 }
22469                 innerHTML  += toadd;
22470                 
22471                 i++;
22472                 currentElementChild = currentElement.childNodes.item(i);
22473                 lastNode = '';
22474                 continue;
22475             }
22476             allText = false;
22477             
22478             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22479                 
22480             // Recursively traverse the tree structure of the child node
22481             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22482             lastnode = currentElementChild.nodeName;
22483             i++;
22484             currentElementChild=currentElement.childNodes.item(i);
22485         }
22486         
22487         ret += innerHTML;
22488         
22489         if (!allText) {
22490                 // The remaining code is mostly for formatting the tree
22491             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22492         }
22493         
22494         
22495         if (tagName) {
22496             ret+= "</"+tagName+">";
22497         }
22498         return ret;
22499         
22500     },
22501         
22502     applyBlacklists : function()
22503     {
22504         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22505         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22506         
22507         this.white = [];
22508         this.black = [];
22509         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22510             if (b.indexOf(tag) > -1) {
22511                 return;
22512             }
22513             this.white.push(tag);
22514             
22515         }, this);
22516         
22517         Roo.each(w, function(tag) {
22518             if (b.indexOf(tag) > -1) {
22519                 return;
22520             }
22521             if (this.white.indexOf(tag) > -1) {
22522                 return;
22523             }
22524             this.white.push(tag);
22525             
22526         }, this);
22527         
22528         
22529         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22530             if (w.indexOf(tag) > -1) {
22531                 return;
22532             }
22533             this.black.push(tag);
22534             
22535         }, this);
22536         
22537         Roo.each(b, function(tag) {
22538             if (w.indexOf(tag) > -1) {
22539                 return;
22540             }
22541             if (this.black.indexOf(tag) > -1) {
22542                 return;
22543             }
22544             this.black.push(tag);
22545             
22546         }, this);
22547         
22548         
22549         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22550         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22551         
22552         this.cwhite = [];
22553         this.cblack = [];
22554         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22555             if (b.indexOf(tag) > -1) {
22556                 return;
22557             }
22558             this.cwhite.push(tag);
22559             
22560         }, this);
22561         
22562         Roo.each(w, function(tag) {
22563             if (b.indexOf(tag) > -1) {
22564                 return;
22565             }
22566             if (this.cwhite.indexOf(tag) > -1) {
22567                 return;
22568             }
22569             this.cwhite.push(tag);
22570             
22571         }, this);
22572         
22573         
22574         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22575             if (w.indexOf(tag) > -1) {
22576                 return;
22577             }
22578             this.cblack.push(tag);
22579             
22580         }, this);
22581         
22582         Roo.each(b, function(tag) {
22583             if (w.indexOf(tag) > -1) {
22584                 return;
22585             }
22586             if (this.cblack.indexOf(tag) > -1) {
22587                 return;
22588             }
22589             this.cblack.push(tag);
22590             
22591         }, this);
22592     },
22593     
22594     setStylesheets : function(stylesheets)
22595     {
22596         if(typeof(stylesheets) == 'string'){
22597             Roo.get(this.iframe.contentDocument.head).createChild({
22598                 tag : 'link',
22599                 rel : 'stylesheet',
22600                 type : 'text/css',
22601                 href : stylesheets
22602             });
22603             
22604             return;
22605         }
22606         var _this = this;
22607      
22608         Roo.each(stylesheets, function(s) {
22609             if(!s.length){
22610                 return;
22611             }
22612             
22613             Roo.get(_this.iframe.contentDocument.head).createChild({
22614                 tag : 'link',
22615                 rel : 'stylesheet',
22616                 type : 'text/css',
22617                 href : s
22618             });
22619         });
22620
22621         
22622     },
22623     
22624     removeStylesheets : function()
22625     {
22626         var _this = this;
22627         
22628         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22629             s.remove();
22630         });
22631     }
22632     
22633     // hide stuff that is not compatible
22634     /**
22635      * @event blur
22636      * @hide
22637      */
22638     /**
22639      * @event change
22640      * @hide
22641      */
22642     /**
22643      * @event focus
22644      * @hide
22645      */
22646     /**
22647      * @event specialkey
22648      * @hide
22649      */
22650     /**
22651      * @cfg {String} fieldClass @hide
22652      */
22653     /**
22654      * @cfg {String} focusClass @hide
22655      */
22656     /**
22657      * @cfg {String} autoCreate @hide
22658      */
22659     /**
22660      * @cfg {String} inputType @hide
22661      */
22662     /**
22663      * @cfg {String} invalidClass @hide
22664      */
22665     /**
22666      * @cfg {String} invalidText @hide
22667      */
22668     /**
22669      * @cfg {String} msgFx @hide
22670      */
22671     /**
22672      * @cfg {String} validateOnBlur @hide
22673      */
22674 });
22675
22676 Roo.HtmlEditorCore.white = [
22677         'area', 'br', 'img', 'input', 'hr', 'wbr',
22678         
22679        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22680        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22681        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22682        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22683        'table',   'ul',         'xmp', 
22684        
22685        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22686       'thead',   'tr', 
22687      
22688       'dir', 'menu', 'ol', 'ul', 'dl',
22689        
22690       'embed',  'object'
22691 ];
22692
22693
22694 Roo.HtmlEditorCore.black = [
22695     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22696         'applet', // 
22697         'base',   'basefont', 'bgsound', 'blink',  'body', 
22698         'frame',  'frameset', 'head',    'html',   'ilayer', 
22699         'iframe', 'layer',  'link',     'meta',    'object',   
22700         'script', 'style' ,'title',  'xml' // clean later..
22701 ];
22702 Roo.HtmlEditorCore.clean = [
22703     'script', 'style', 'title', 'xml'
22704 ];
22705 Roo.HtmlEditorCore.remove = [
22706     'font'
22707 ];
22708 // attributes..
22709
22710 Roo.HtmlEditorCore.ablack = [
22711     'on'
22712 ];
22713     
22714 Roo.HtmlEditorCore.aclean = [ 
22715     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22716 ];
22717
22718 // protocols..
22719 Roo.HtmlEditorCore.pwhite= [
22720         'http',  'https',  'mailto'
22721 ];
22722
22723 // white listed style attributes.
22724 Roo.HtmlEditorCore.cwhite= [
22725       //  'text-align', /// default is to allow most things..
22726       
22727          
22728 //        'font-size'//??
22729 ];
22730
22731 // black listed style attributes.
22732 Roo.HtmlEditorCore.cblack= [
22733       //  'font-size' -- this can be set by the project 
22734 ];
22735
22736
22737 Roo.HtmlEditorCore.swapCodes   =[ 
22738     [    8211, "--" ], 
22739     [    8212, "--" ], 
22740     [    8216,  "'" ],  
22741     [    8217, "'" ],  
22742     [    8220, '"' ],  
22743     [    8221, '"' ],  
22744     [    8226, "*" ],  
22745     [    8230, "..." ]
22746 ]; 
22747
22748     /*
22749  * - LGPL
22750  *
22751  * HtmlEditor
22752  * 
22753  */
22754
22755 /**
22756  * @class Roo.bootstrap.HtmlEditor
22757  * @extends Roo.bootstrap.TextArea
22758  * Bootstrap HtmlEditor class
22759
22760  * @constructor
22761  * Create a new HtmlEditor
22762  * @param {Object} config The config object
22763  */
22764
22765 Roo.bootstrap.HtmlEditor = function(config){
22766     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22767     if (!this.toolbars) {
22768         this.toolbars = [];
22769     }
22770     
22771     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22772     this.addEvents({
22773             /**
22774              * @event initialize
22775              * Fires when the editor is fully initialized (including the iframe)
22776              * @param {HtmlEditor} this
22777              */
22778             initialize: true,
22779             /**
22780              * @event activate
22781              * Fires when the editor is first receives the focus. Any insertion must wait
22782              * until after this event.
22783              * @param {HtmlEditor} this
22784              */
22785             activate: true,
22786              /**
22787              * @event beforesync
22788              * Fires before the textarea is updated with content from the editor iframe. Return false
22789              * to cancel the sync.
22790              * @param {HtmlEditor} this
22791              * @param {String} html
22792              */
22793             beforesync: true,
22794              /**
22795              * @event beforepush
22796              * Fires before the iframe editor is updated with content from the textarea. Return false
22797              * to cancel the push.
22798              * @param {HtmlEditor} this
22799              * @param {String} html
22800              */
22801             beforepush: true,
22802              /**
22803              * @event sync
22804              * Fires when the textarea is updated with content from the editor iframe.
22805              * @param {HtmlEditor} this
22806              * @param {String} html
22807              */
22808             sync: true,
22809              /**
22810              * @event push
22811              * Fires when the iframe editor is updated with content from the textarea.
22812              * @param {HtmlEditor} this
22813              * @param {String} html
22814              */
22815             push: true,
22816              /**
22817              * @event editmodechange
22818              * Fires when the editor switches edit modes
22819              * @param {HtmlEditor} this
22820              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22821              */
22822             editmodechange: true,
22823             /**
22824              * @event editorevent
22825              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22826              * @param {HtmlEditor} this
22827              */
22828             editorevent: true,
22829             /**
22830              * @event firstfocus
22831              * Fires when on first focus - needed by toolbars..
22832              * @param {HtmlEditor} this
22833              */
22834             firstfocus: true,
22835             /**
22836              * @event autosave
22837              * Auto save the htmlEditor value as a file into Events
22838              * @param {HtmlEditor} this
22839              */
22840             autosave: true,
22841             /**
22842              * @event savedpreview
22843              * preview the saved version of htmlEditor
22844              * @param {HtmlEditor} this
22845              */
22846             savedpreview: true
22847         });
22848 };
22849
22850
22851 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22852     
22853     
22854       /**
22855      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22856      */
22857     toolbars : false,
22858     
22859      /**
22860     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22861     */
22862     btns : [],
22863    
22864      /**
22865      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22866      *                        Roo.resizable.
22867      */
22868     resizable : false,
22869      /**
22870      * @cfg {Number} height (in pixels)
22871      */   
22872     height: 300,
22873    /**
22874      * @cfg {Number} width (in pixels)
22875      */   
22876     width: false,
22877     
22878     /**
22879      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22880      * 
22881      */
22882     stylesheets: false,
22883     
22884     // id of frame..
22885     frameId: false,
22886     
22887     // private properties
22888     validationEvent : false,
22889     deferHeight: true,
22890     initialized : false,
22891     activated : false,
22892     
22893     onFocus : Roo.emptyFn,
22894     iframePad:3,
22895     hideMode:'offsets',
22896     
22897     tbContainer : false,
22898     
22899     toolbarContainer :function() {
22900         return this.wrap.select('.x-html-editor-tb',true).first();
22901     },
22902
22903     /**
22904      * Protected method that will not generally be called directly. It
22905      * is called when the editor creates its toolbar. Override this method if you need to
22906      * add custom toolbar buttons.
22907      * @param {HtmlEditor} editor
22908      */
22909     createToolbar : function(){
22910         Roo.log('renewing');
22911         Roo.log("create toolbars");
22912         
22913         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22914         this.toolbars[0].render(this.toolbarContainer());
22915         
22916         return;
22917         
22918 //        if (!editor.toolbars || !editor.toolbars.length) {
22919 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22920 //        }
22921 //        
22922 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22923 //            editor.toolbars[i] = Roo.factory(
22924 //                    typeof(editor.toolbars[i]) == 'string' ?
22925 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22926 //                Roo.bootstrap.HtmlEditor);
22927 //            editor.toolbars[i].init(editor);
22928 //        }
22929     },
22930
22931      
22932     // private
22933     onRender : function(ct, position)
22934     {
22935        // Roo.log("Call onRender: " + this.xtype);
22936         var _t = this;
22937         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22938       
22939         this.wrap = this.inputEl().wrap({
22940             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22941         });
22942         
22943         this.editorcore.onRender(ct, position);
22944          
22945         if (this.resizable) {
22946             this.resizeEl = new Roo.Resizable(this.wrap, {
22947                 pinned : true,
22948                 wrap: true,
22949                 dynamic : true,
22950                 minHeight : this.height,
22951                 height: this.height,
22952                 handles : this.resizable,
22953                 width: this.width,
22954                 listeners : {
22955                     resize : function(r, w, h) {
22956                         _t.onResize(w,h); // -something
22957                     }
22958                 }
22959             });
22960             
22961         }
22962         this.createToolbar(this);
22963        
22964         
22965         if(!this.width && this.resizable){
22966             this.setSize(this.wrap.getSize());
22967         }
22968         if (this.resizeEl) {
22969             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22970             // should trigger onReize..
22971         }
22972         
22973     },
22974
22975     // private
22976     onResize : function(w, h)
22977     {
22978         Roo.log('resize: ' +w + ',' + h );
22979         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22980         var ew = false;
22981         var eh = false;
22982         
22983         if(this.inputEl() ){
22984             if(typeof w == 'number'){
22985                 var aw = w - this.wrap.getFrameWidth('lr');
22986                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22987                 ew = aw;
22988             }
22989             if(typeof h == 'number'){
22990                  var tbh = -11;  // fixme it needs to tool bar size!
22991                 for (var i =0; i < this.toolbars.length;i++) {
22992                     // fixme - ask toolbars for heights?
22993                     tbh += this.toolbars[i].el.getHeight();
22994                     //if (this.toolbars[i].footer) {
22995                     //    tbh += this.toolbars[i].footer.el.getHeight();
22996                     //}
22997                 }
22998               
22999                 
23000                 
23001                 
23002                 
23003                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23004                 ah -= 5; // knock a few pixes off for look..
23005                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23006                 var eh = ah;
23007             }
23008         }
23009         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23010         this.editorcore.onResize(ew,eh);
23011         
23012     },
23013
23014     /**
23015      * Toggles the editor between standard and source edit mode.
23016      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23017      */
23018     toggleSourceEdit : function(sourceEditMode)
23019     {
23020         this.editorcore.toggleSourceEdit(sourceEditMode);
23021         
23022         if(this.editorcore.sourceEditMode){
23023             Roo.log('editor - showing textarea');
23024             
23025 //            Roo.log('in');
23026 //            Roo.log(this.syncValue());
23027             this.syncValue();
23028             this.inputEl().removeClass(['hide', 'x-hidden']);
23029             this.inputEl().dom.removeAttribute('tabIndex');
23030             this.inputEl().focus();
23031         }else{
23032             Roo.log('editor - hiding textarea');
23033 //            Roo.log('out')
23034 //            Roo.log(this.pushValue()); 
23035             this.pushValue();
23036             
23037             this.inputEl().addClass(['hide', 'x-hidden']);
23038             this.inputEl().dom.setAttribute('tabIndex', -1);
23039             //this.deferFocus();
23040         }
23041          
23042         if(this.resizable){
23043             this.setSize(this.wrap.getSize());
23044         }
23045         
23046         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23047     },
23048  
23049     // private (for BoxComponent)
23050     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23051
23052     // private (for BoxComponent)
23053     getResizeEl : function(){
23054         return this.wrap;
23055     },
23056
23057     // private (for BoxComponent)
23058     getPositionEl : function(){
23059         return this.wrap;
23060     },
23061
23062     // private
23063     initEvents : function(){
23064         this.originalValue = this.getValue();
23065     },
23066
23067 //    /**
23068 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23069 //     * @method
23070 //     */
23071 //    markInvalid : Roo.emptyFn,
23072 //    /**
23073 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23074 //     * @method
23075 //     */
23076 //    clearInvalid : Roo.emptyFn,
23077
23078     setValue : function(v){
23079         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23080         this.editorcore.pushValue();
23081     },
23082
23083      
23084     // private
23085     deferFocus : function(){
23086         this.focus.defer(10, this);
23087     },
23088
23089     // doc'ed in Field
23090     focus : function(){
23091         this.editorcore.focus();
23092         
23093     },
23094       
23095
23096     // private
23097     onDestroy : function(){
23098         
23099         
23100         
23101         if(this.rendered){
23102             
23103             for (var i =0; i < this.toolbars.length;i++) {
23104                 // fixme - ask toolbars for heights?
23105                 this.toolbars[i].onDestroy();
23106             }
23107             
23108             this.wrap.dom.innerHTML = '';
23109             this.wrap.remove();
23110         }
23111     },
23112
23113     // private
23114     onFirstFocus : function(){
23115         //Roo.log("onFirstFocus");
23116         this.editorcore.onFirstFocus();
23117          for (var i =0; i < this.toolbars.length;i++) {
23118             this.toolbars[i].onFirstFocus();
23119         }
23120         
23121     },
23122     
23123     // private
23124     syncValue : function()
23125     {   
23126         this.editorcore.syncValue();
23127     },
23128     
23129     pushValue : function()
23130     {   
23131         this.editorcore.pushValue();
23132     }
23133      
23134     
23135     // hide stuff that is not compatible
23136     /**
23137      * @event blur
23138      * @hide
23139      */
23140     /**
23141      * @event change
23142      * @hide
23143      */
23144     /**
23145      * @event focus
23146      * @hide
23147      */
23148     /**
23149      * @event specialkey
23150      * @hide
23151      */
23152     /**
23153      * @cfg {String} fieldClass @hide
23154      */
23155     /**
23156      * @cfg {String} focusClass @hide
23157      */
23158     /**
23159      * @cfg {String} autoCreate @hide
23160      */
23161     /**
23162      * @cfg {String} inputType @hide
23163      */
23164     /**
23165      * @cfg {String} invalidClass @hide
23166      */
23167     /**
23168      * @cfg {String} invalidText @hide
23169      */
23170     /**
23171      * @cfg {String} msgFx @hide
23172      */
23173     /**
23174      * @cfg {String} validateOnBlur @hide
23175      */
23176 });
23177  
23178     
23179    
23180    
23181    
23182       
23183 Roo.namespace('Roo.bootstrap.htmleditor');
23184 /**
23185  * @class Roo.bootstrap.HtmlEditorToolbar1
23186  * Basic Toolbar
23187  * 
23188  * Usage:
23189  *
23190  new Roo.bootstrap.HtmlEditor({
23191     ....
23192     toolbars : [
23193         new Roo.bootstrap.HtmlEditorToolbar1({
23194             disable : { fonts: 1 , format: 1, ..., ... , ...],
23195             btns : [ .... ]
23196         })
23197     }
23198      
23199  * 
23200  * @cfg {Object} disable List of elements to disable..
23201  * @cfg {Array} btns List of additional buttons.
23202  * 
23203  * 
23204  * NEEDS Extra CSS? 
23205  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23206  */
23207  
23208 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23209 {
23210     
23211     Roo.apply(this, config);
23212     
23213     // default disabled, based on 'good practice'..
23214     this.disable = this.disable || {};
23215     Roo.applyIf(this.disable, {
23216         fontSize : true,
23217         colors : true,
23218         specialElements : true
23219     });
23220     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23221     
23222     this.editor = config.editor;
23223     this.editorcore = config.editor.editorcore;
23224     
23225     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23226     
23227     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23228     // dont call parent... till later.
23229 }
23230 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23231      
23232     bar : true,
23233     
23234     editor : false,
23235     editorcore : false,
23236     
23237     
23238     formats : [
23239         "p" ,  
23240         "h1","h2","h3","h4","h5","h6", 
23241         "pre", "code", 
23242         "abbr", "acronym", "address", "cite", "samp", "var",
23243         'div','span'
23244     ],
23245     
23246     onRender : function(ct, position)
23247     {
23248        // Roo.log("Call onRender: " + this.xtype);
23249         
23250        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23251        Roo.log(this.el);
23252        this.el.dom.style.marginBottom = '0';
23253        var _this = this;
23254        var editorcore = this.editorcore;
23255        var editor= this.editor;
23256        
23257        var children = [];
23258        var btn = function(id,cmd , toggle, handler, html){
23259        
23260             var  event = toggle ? 'toggle' : 'click';
23261        
23262             var a = {
23263                 size : 'sm',
23264                 xtype: 'Button',
23265                 xns: Roo.bootstrap,
23266                 glyphicon : id,
23267                 cmd : id || cmd,
23268                 enableToggle:toggle !== false,
23269                 html : html || '',
23270                 pressed : toggle ? false : null,
23271                 listeners : {}
23272             };
23273             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23274                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23275             };
23276             children.push(a);
23277             return a;
23278        }
23279        
23280     //    var cb_box = function...
23281         
23282         var style = {
23283                 xtype: 'Button',
23284                 size : 'sm',
23285                 xns: Roo.bootstrap,
23286                 glyphicon : 'font',
23287                 //html : 'submit'
23288                 menu : {
23289                     xtype: 'Menu',
23290                     xns: Roo.bootstrap,
23291                     items:  []
23292                 }
23293         };
23294         Roo.each(this.formats, function(f) {
23295             style.menu.items.push({
23296                 xtype :'MenuItem',
23297                 xns: Roo.bootstrap,
23298                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23299                 tagname : f,
23300                 listeners : {
23301                     click : function()
23302                     {
23303                         editorcore.insertTag(this.tagname);
23304                         editor.focus();
23305                     }
23306                 }
23307                 
23308             });
23309         });
23310         children.push(style);   
23311         
23312         btn('bold',false,true);
23313         btn('italic',false,true);
23314         btn('align-left', 'justifyleft',true);
23315         btn('align-center', 'justifycenter',true);
23316         btn('align-right' , 'justifyright',true);
23317         btn('link', false, false, function(btn) {
23318             //Roo.log("create link?");
23319             var url = prompt(this.createLinkText, this.defaultLinkValue);
23320             if(url && url != 'http:/'+'/'){
23321                 this.editorcore.relayCmd('createlink', url);
23322             }
23323         }),
23324         btn('list','insertunorderedlist',true);
23325         btn('pencil', false,true, function(btn){
23326                 Roo.log(this);
23327                 this.toggleSourceEdit(btn.pressed);
23328         });
23329         
23330         if (this.editor.btns.length > 0) {
23331             for (var i = 0; i<this.editor.btns.length; i++) {
23332                 children.push(this.editor.btns[i]);
23333             }
23334         }
23335         
23336         /*
23337         var cog = {
23338                 xtype: 'Button',
23339                 size : 'sm',
23340                 xns: Roo.bootstrap,
23341                 glyphicon : 'cog',
23342                 //html : 'submit'
23343                 menu : {
23344                     xtype: 'Menu',
23345                     xns: Roo.bootstrap,
23346                     items:  []
23347                 }
23348         };
23349         
23350         cog.menu.items.push({
23351             xtype :'MenuItem',
23352             xns: Roo.bootstrap,
23353             html : Clean styles,
23354             tagname : f,
23355             listeners : {
23356                 click : function()
23357                 {
23358                     editorcore.insertTag(this.tagname);
23359                     editor.focus();
23360                 }
23361             }
23362             
23363         });
23364        */
23365         
23366          
23367        this.xtype = 'NavSimplebar';
23368         
23369         for(var i=0;i< children.length;i++) {
23370             
23371             this.buttons.add(this.addxtypeChild(children[i]));
23372             
23373         }
23374         
23375         editor.on('editorevent', this.updateToolbar, this);
23376     },
23377     onBtnClick : function(id)
23378     {
23379        this.editorcore.relayCmd(id);
23380        this.editorcore.focus();
23381     },
23382     
23383     /**
23384      * Protected method that will not generally be called directly. It triggers
23385      * a toolbar update by reading the markup state of the current selection in the editor.
23386      */
23387     updateToolbar: function(){
23388
23389         if(!this.editorcore.activated){
23390             this.editor.onFirstFocus(); // is this neeed?
23391             return;
23392         }
23393
23394         var btns = this.buttons; 
23395         var doc = this.editorcore.doc;
23396         btns.get('bold').setActive(doc.queryCommandState('bold'));
23397         btns.get('italic').setActive(doc.queryCommandState('italic'));
23398         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23399         
23400         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23401         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23402         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23403         
23404         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23405         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23406          /*
23407         
23408         var ans = this.editorcore.getAllAncestors();
23409         if (this.formatCombo) {
23410             
23411             
23412             var store = this.formatCombo.store;
23413             this.formatCombo.setValue("");
23414             for (var i =0; i < ans.length;i++) {
23415                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23416                     // select it..
23417                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23418                     break;
23419                 }
23420             }
23421         }
23422         
23423         
23424         
23425         // hides menus... - so this cant be on a menu...
23426         Roo.bootstrap.MenuMgr.hideAll();
23427         */
23428         Roo.bootstrap.MenuMgr.hideAll();
23429         //this.editorsyncValue();
23430     },
23431     onFirstFocus: function() {
23432         this.buttons.each(function(item){
23433            item.enable();
23434         });
23435     },
23436     toggleSourceEdit : function(sourceEditMode){
23437         
23438           
23439         if(sourceEditMode){
23440             Roo.log("disabling buttons");
23441            this.buttons.each( function(item){
23442                 if(item.cmd != 'pencil'){
23443                     item.disable();
23444                 }
23445             });
23446           
23447         }else{
23448             Roo.log("enabling buttons");
23449             if(this.editorcore.initialized){
23450                 this.buttons.each( function(item){
23451                     item.enable();
23452                 });
23453             }
23454             
23455         }
23456         Roo.log("calling toggole on editor");
23457         // tell the editor that it's been pressed..
23458         this.editor.toggleSourceEdit(sourceEditMode);
23459        
23460     }
23461 });
23462
23463
23464
23465
23466
23467 /**
23468  * @class Roo.bootstrap.Table.AbstractSelectionModel
23469  * @extends Roo.util.Observable
23470  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23471  * implemented by descendant classes.  This class should not be directly instantiated.
23472  * @constructor
23473  */
23474 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23475     this.locked = false;
23476     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23477 };
23478
23479
23480 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23481     /** @ignore Called by the grid automatically. Do not call directly. */
23482     init : function(grid){
23483         this.grid = grid;
23484         this.initEvents();
23485     },
23486
23487     /**
23488      * Locks the selections.
23489      */
23490     lock : function(){
23491         this.locked = true;
23492     },
23493
23494     /**
23495      * Unlocks the selections.
23496      */
23497     unlock : function(){
23498         this.locked = false;
23499     },
23500
23501     /**
23502      * Returns true if the selections are locked.
23503      * @return {Boolean}
23504      */
23505     isLocked : function(){
23506         return this.locked;
23507     }
23508 });
23509 /**
23510  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23511  * @class Roo.bootstrap.Table.RowSelectionModel
23512  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23513  * It supports multiple selections and keyboard selection/navigation. 
23514  * @constructor
23515  * @param {Object} config
23516  */
23517
23518 Roo.bootstrap.Table.RowSelectionModel = function(config){
23519     Roo.apply(this, config);
23520     this.selections = new Roo.util.MixedCollection(false, function(o){
23521         return o.id;
23522     });
23523
23524     this.last = false;
23525     this.lastActive = false;
23526
23527     this.addEvents({
23528         /**
23529              * @event selectionchange
23530              * Fires when the selection changes
23531              * @param {SelectionModel} this
23532              */
23533             "selectionchange" : true,
23534         /**
23535              * @event afterselectionchange
23536              * Fires after the selection changes (eg. by key press or clicking)
23537              * @param {SelectionModel} this
23538              */
23539             "afterselectionchange" : true,
23540         /**
23541              * @event beforerowselect
23542              * Fires when a row is selected being selected, return false to cancel.
23543              * @param {SelectionModel} this
23544              * @param {Number} rowIndex The selected index
23545              * @param {Boolean} keepExisting False if other selections will be cleared
23546              */
23547             "beforerowselect" : true,
23548         /**
23549              * @event rowselect
23550              * Fires when a row is selected.
23551              * @param {SelectionModel} this
23552              * @param {Number} rowIndex The selected index
23553              * @param {Roo.data.Record} r The record
23554              */
23555             "rowselect" : true,
23556         /**
23557              * @event rowdeselect
23558              * Fires when a row is deselected.
23559              * @param {SelectionModel} this
23560              * @param {Number} rowIndex The selected index
23561              */
23562         "rowdeselect" : true
23563     });
23564     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23565     this.locked = false;
23566  };
23567
23568 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23569     /**
23570      * @cfg {Boolean} singleSelect
23571      * True to allow selection of only one row at a time (defaults to false)
23572      */
23573     singleSelect : false,
23574
23575     // private
23576     initEvents : function()
23577     {
23578
23579         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23580         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23581         //}else{ // allow click to work like normal
23582          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23583         //}
23584         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23585         this.grid.on("rowclick", this.handleMouseDown, this);
23586         
23587         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23588             "up" : function(e){
23589                 if(!e.shiftKey){
23590                     this.selectPrevious(e.shiftKey);
23591                 }else if(this.last !== false && this.lastActive !== false){
23592                     var last = this.last;
23593                     this.selectRange(this.last,  this.lastActive-1);
23594                     this.grid.getView().focusRow(this.lastActive);
23595                     if(last !== false){
23596                         this.last = last;
23597                     }
23598                 }else{
23599                     this.selectFirstRow();
23600                 }
23601                 this.fireEvent("afterselectionchange", this);
23602             },
23603             "down" : function(e){
23604                 if(!e.shiftKey){
23605                     this.selectNext(e.shiftKey);
23606                 }else if(this.last !== false && this.lastActive !== false){
23607                     var last = this.last;
23608                     this.selectRange(this.last,  this.lastActive+1);
23609                     this.grid.getView().focusRow(this.lastActive);
23610                     if(last !== false){
23611                         this.last = last;
23612                     }
23613                 }else{
23614                     this.selectFirstRow();
23615                 }
23616                 this.fireEvent("afterselectionchange", this);
23617             },
23618             scope: this
23619         });
23620         this.grid.store.on('load', function(){
23621             this.selections.clear();
23622         },this);
23623         /*
23624         var view = this.grid.view;
23625         view.on("refresh", this.onRefresh, this);
23626         view.on("rowupdated", this.onRowUpdated, this);
23627         view.on("rowremoved", this.onRemove, this);
23628         */
23629     },
23630
23631     // private
23632     onRefresh : function()
23633     {
23634         var ds = this.grid.store, i, v = this.grid.view;
23635         var s = this.selections;
23636         s.each(function(r){
23637             if((i = ds.indexOfId(r.id)) != -1){
23638                 v.onRowSelect(i);
23639             }else{
23640                 s.remove(r);
23641             }
23642         });
23643     },
23644
23645     // private
23646     onRemove : function(v, index, r){
23647         this.selections.remove(r);
23648     },
23649
23650     // private
23651     onRowUpdated : function(v, index, r){
23652         if(this.isSelected(r)){
23653             v.onRowSelect(index);
23654         }
23655     },
23656
23657     /**
23658      * Select records.
23659      * @param {Array} records The records to select
23660      * @param {Boolean} keepExisting (optional) True to keep existing selections
23661      */
23662     selectRecords : function(records, keepExisting)
23663     {
23664         if(!keepExisting){
23665             this.clearSelections();
23666         }
23667             var ds = this.grid.store;
23668         for(var i = 0, len = records.length; i < len; i++){
23669             this.selectRow(ds.indexOf(records[i]), true);
23670         }
23671     },
23672
23673     /**
23674      * Gets the number of selected rows.
23675      * @return {Number}
23676      */
23677     getCount : function(){
23678         return this.selections.length;
23679     },
23680
23681     /**
23682      * Selects the first row in the grid.
23683      */
23684     selectFirstRow : function(){
23685         this.selectRow(0);
23686     },
23687
23688     /**
23689      * Select the last row.
23690      * @param {Boolean} keepExisting (optional) True to keep existing selections
23691      */
23692     selectLastRow : function(keepExisting){
23693         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23694         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23695     },
23696
23697     /**
23698      * Selects the row immediately following the last selected row.
23699      * @param {Boolean} keepExisting (optional) True to keep existing selections
23700      */
23701     selectNext : function(keepExisting)
23702     {
23703             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23704             this.selectRow(this.last+1, keepExisting);
23705             this.grid.getView().focusRow(this.last);
23706         }
23707     },
23708
23709     /**
23710      * Selects the row that precedes the last selected row.
23711      * @param {Boolean} keepExisting (optional) True to keep existing selections
23712      */
23713     selectPrevious : function(keepExisting){
23714         if(this.last){
23715             this.selectRow(this.last-1, keepExisting);
23716             this.grid.getView().focusRow(this.last);
23717         }
23718     },
23719
23720     /**
23721      * Returns the selected records
23722      * @return {Array} Array of selected records
23723      */
23724     getSelections : function(){
23725         return [].concat(this.selections.items);
23726     },
23727
23728     /**
23729      * Returns the first selected record.
23730      * @return {Record}
23731      */
23732     getSelected : function(){
23733         return this.selections.itemAt(0);
23734     },
23735
23736
23737     /**
23738      * Clears all selections.
23739      */
23740     clearSelections : function(fast)
23741     {
23742         if(this.locked) {
23743             return;
23744         }
23745         if(fast !== true){
23746                 var ds = this.grid.store;
23747             var s = this.selections;
23748             s.each(function(r){
23749                 this.deselectRow(ds.indexOfId(r.id));
23750             }, this);
23751             s.clear();
23752         }else{
23753             this.selections.clear();
23754         }
23755         this.last = false;
23756     },
23757
23758
23759     /**
23760      * Selects all rows.
23761      */
23762     selectAll : function(){
23763         if(this.locked) {
23764             return;
23765         }
23766         this.selections.clear();
23767         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23768             this.selectRow(i, true);
23769         }
23770     },
23771
23772     /**
23773      * Returns True if there is a selection.
23774      * @return {Boolean}
23775      */
23776     hasSelection : function(){
23777         return this.selections.length > 0;
23778     },
23779
23780     /**
23781      * Returns True if the specified row is selected.
23782      * @param {Number/Record} record The record or index of the record to check
23783      * @return {Boolean}
23784      */
23785     isSelected : function(index){
23786             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23787         return (r && this.selections.key(r.id) ? true : false);
23788     },
23789
23790     /**
23791      * Returns True if the specified record id is selected.
23792      * @param {String} id The id of record to check
23793      * @return {Boolean}
23794      */
23795     isIdSelected : function(id){
23796         return (this.selections.key(id) ? true : false);
23797     },
23798
23799
23800     // private
23801     handleMouseDBClick : function(e, t){
23802         
23803     },
23804     // private
23805     handleMouseDown : function(e, t)
23806     {
23807             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23808         if(this.isLocked() || rowIndex < 0 ){
23809             return;
23810         };
23811         if(e.shiftKey && this.last !== false){
23812             var last = this.last;
23813             this.selectRange(last, rowIndex, e.ctrlKey);
23814             this.last = last; // reset the last
23815             t.focus();
23816     
23817         }else{
23818             var isSelected = this.isSelected(rowIndex);
23819             //Roo.log("select row:" + rowIndex);
23820             if(isSelected){
23821                 this.deselectRow(rowIndex);
23822             } else {
23823                         this.selectRow(rowIndex, true);
23824             }
23825     
23826             /*
23827                 if(e.button !== 0 && isSelected){
23828                 alert('rowIndex 2: ' + rowIndex);
23829                     view.focusRow(rowIndex);
23830                 }else if(e.ctrlKey && isSelected){
23831                     this.deselectRow(rowIndex);
23832                 }else if(!isSelected){
23833                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23834                     view.focusRow(rowIndex);
23835                 }
23836             */
23837         }
23838         this.fireEvent("afterselectionchange", this);
23839     },
23840     // private
23841     handleDragableRowClick :  function(grid, rowIndex, e) 
23842     {
23843         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23844             this.selectRow(rowIndex, false);
23845             grid.view.focusRow(rowIndex);
23846              this.fireEvent("afterselectionchange", this);
23847         }
23848     },
23849     
23850     /**
23851      * Selects multiple rows.
23852      * @param {Array} rows Array of the indexes of the row to select
23853      * @param {Boolean} keepExisting (optional) True to keep existing selections
23854      */
23855     selectRows : function(rows, keepExisting){
23856         if(!keepExisting){
23857             this.clearSelections();
23858         }
23859         for(var i = 0, len = rows.length; i < len; i++){
23860             this.selectRow(rows[i], true);
23861         }
23862     },
23863
23864     /**
23865      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23866      * @param {Number} startRow The index of the first row in the range
23867      * @param {Number} endRow The index of the last row in the range
23868      * @param {Boolean} keepExisting (optional) True to retain existing selections
23869      */
23870     selectRange : function(startRow, endRow, keepExisting){
23871         if(this.locked) {
23872             return;
23873         }
23874         if(!keepExisting){
23875             this.clearSelections();
23876         }
23877         if(startRow <= endRow){
23878             for(var i = startRow; i <= endRow; i++){
23879                 this.selectRow(i, true);
23880             }
23881         }else{
23882             for(var i = startRow; i >= endRow; i--){
23883                 this.selectRow(i, true);
23884             }
23885         }
23886     },
23887
23888     /**
23889      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23890      * @param {Number} startRow The index of the first row in the range
23891      * @param {Number} endRow The index of the last row in the range
23892      */
23893     deselectRange : function(startRow, endRow, preventViewNotify){
23894         if(this.locked) {
23895             return;
23896         }
23897         for(var i = startRow; i <= endRow; i++){
23898             this.deselectRow(i, preventViewNotify);
23899         }
23900     },
23901
23902     /**
23903      * Selects a row.
23904      * @param {Number} row The index of the row to select
23905      * @param {Boolean} keepExisting (optional) True to keep existing selections
23906      */
23907     selectRow : function(index, keepExisting, preventViewNotify)
23908     {
23909             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23910             return;
23911         }
23912         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23913             if(!keepExisting || this.singleSelect){
23914                 this.clearSelections();
23915             }
23916             
23917             var r = this.grid.store.getAt(index);
23918             //console.log('selectRow - record id :' + r.id);
23919             
23920             this.selections.add(r);
23921             this.last = this.lastActive = index;
23922             if(!preventViewNotify){
23923                 var proxy = new Roo.Element(
23924                                 this.grid.getRowDom(index)
23925                 );
23926                 proxy.addClass('bg-info info');
23927             }
23928             this.fireEvent("rowselect", this, index, r);
23929             this.fireEvent("selectionchange", this);
23930         }
23931     },
23932
23933     /**
23934      * Deselects a row.
23935      * @param {Number} row The index of the row to deselect
23936      */
23937     deselectRow : function(index, preventViewNotify)
23938     {
23939         if(this.locked) {
23940             return;
23941         }
23942         if(this.last == index){
23943             this.last = false;
23944         }
23945         if(this.lastActive == index){
23946             this.lastActive = false;
23947         }
23948         
23949         var r = this.grid.store.getAt(index);
23950         if (!r) {
23951             return;
23952         }
23953         
23954         this.selections.remove(r);
23955         //.console.log('deselectRow - record id :' + r.id);
23956         if(!preventViewNotify){
23957         
23958             var proxy = new Roo.Element(
23959                 this.grid.getRowDom(index)
23960             );
23961             proxy.removeClass('bg-info info');
23962         }
23963         this.fireEvent("rowdeselect", this, index);
23964         this.fireEvent("selectionchange", this);
23965     },
23966
23967     // private
23968     restoreLast : function(){
23969         if(this._last){
23970             this.last = this._last;
23971         }
23972     },
23973
23974     // private
23975     acceptsNav : function(row, col, cm){
23976         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23977     },
23978
23979     // private
23980     onEditorKey : function(field, e){
23981         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23982         if(k == e.TAB){
23983             e.stopEvent();
23984             ed.completeEdit();
23985             if(e.shiftKey){
23986                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23987             }else{
23988                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23989             }
23990         }else if(k == e.ENTER && !e.ctrlKey){
23991             e.stopEvent();
23992             ed.completeEdit();
23993             if(e.shiftKey){
23994                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23995             }else{
23996                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23997             }
23998         }else if(k == e.ESC){
23999             ed.cancelEdit();
24000         }
24001         if(newCell){
24002             g.startEditing(newCell[0], newCell[1]);
24003         }
24004     }
24005 });
24006 /*
24007  * Based on:
24008  * Ext JS Library 1.1.1
24009  * Copyright(c) 2006-2007, Ext JS, LLC.
24010  *
24011  * Originally Released Under LGPL - original licence link has changed is not relivant.
24012  *
24013  * Fork - LGPL
24014  * <script type="text/javascript">
24015  */
24016  
24017 /**
24018  * @class Roo.bootstrap.PagingToolbar
24019  * @extends Roo.bootstrap.NavSimplebar
24020  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24021  * @constructor
24022  * Create a new PagingToolbar
24023  * @param {Object} config The config object
24024  * @param {Roo.data.Store} store
24025  */
24026 Roo.bootstrap.PagingToolbar = function(config)
24027 {
24028     // old args format still supported... - xtype is prefered..
24029         // created from xtype...
24030     
24031     this.ds = config.dataSource;
24032     
24033     if (config.store && !this.ds) {
24034         this.store= Roo.factory(config.store, Roo.data);
24035         this.ds = this.store;
24036         this.ds.xmodule = this.xmodule || false;
24037     }
24038     
24039     this.toolbarItems = [];
24040     if (config.items) {
24041         this.toolbarItems = config.items;
24042     }
24043     
24044     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24045     
24046     this.cursor = 0;
24047     
24048     if (this.ds) { 
24049         this.bind(this.ds);
24050     }
24051     
24052     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24053     
24054 };
24055
24056 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24057     /**
24058      * @cfg {Roo.data.Store} dataSource
24059      * The underlying data store providing the paged data
24060      */
24061     /**
24062      * @cfg {String/HTMLElement/Element} container
24063      * container The id or element that will contain the toolbar
24064      */
24065     /**
24066      * @cfg {Boolean} displayInfo
24067      * True to display the displayMsg (defaults to false)
24068      */
24069     /**
24070      * @cfg {Number} pageSize
24071      * The number of records to display per page (defaults to 20)
24072      */
24073     pageSize: 20,
24074     /**
24075      * @cfg {String} displayMsg
24076      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24077      */
24078     displayMsg : 'Displaying {0} - {1} of {2}',
24079     /**
24080      * @cfg {String} emptyMsg
24081      * The message to display when no records are found (defaults to "No data to display")
24082      */
24083     emptyMsg : 'No data to display',
24084     /**
24085      * Customizable piece of the default paging text (defaults to "Page")
24086      * @type String
24087      */
24088     beforePageText : "Page",
24089     /**
24090      * Customizable piece of the default paging text (defaults to "of %0")
24091      * @type String
24092      */
24093     afterPageText : "of {0}",
24094     /**
24095      * Customizable piece of the default paging text (defaults to "First Page")
24096      * @type String
24097      */
24098     firstText : "First Page",
24099     /**
24100      * Customizable piece of the default paging text (defaults to "Previous Page")
24101      * @type String
24102      */
24103     prevText : "Previous Page",
24104     /**
24105      * Customizable piece of the default paging text (defaults to "Next Page")
24106      * @type String
24107      */
24108     nextText : "Next Page",
24109     /**
24110      * Customizable piece of the default paging text (defaults to "Last Page")
24111      * @type String
24112      */
24113     lastText : "Last Page",
24114     /**
24115      * Customizable piece of the default paging text (defaults to "Refresh")
24116      * @type String
24117      */
24118     refreshText : "Refresh",
24119
24120     buttons : false,
24121     // private
24122     onRender : function(ct, position) 
24123     {
24124         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24125         this.navgroup.parentId = this.id;
24126         this.navgroup.onRender(this.el, null);
24127         // add the buttons to the navgroup
24128         
24129         if(this.displayInfo){
24130             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24131             this.displayEl = this.el.select('.x-paging-info', true).first();
24132 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24133 //            this.displayEl = navel.el.select('span',true).first();
24134         }
24135         
24136         var _this = this;
24137         
24138         if(this.buttons){
24139             Roo.each(_this.buttons, function(e){ // this might need to use render????
24140                Roo.factory(e).onRender(_this.el, null);
24141             });
24142         }
24143             
24144         Roo.each(_this.toolbarItems, function(e) {
24145             _this.navgroup.addItem(e);
24146         });
24147         
24148         
24149         this.first = this.navgroup.addItem({
24150             tooltip: this.firstText,
24151             cls: "prev",
24152             icon : 'fa fa-backward',
24153             disabled: true,
24154             preventDefault: true,
24155             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24156         });
24157         
24158         this.prev =  this.navgroup.addItem({
24159             tooltip: this.prevText,
24160             cls: "prev",
24161             icon : 'fa fa-step-backward',
24162             disabled: true,
24163             preventDefault: true,
24164             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24165         });
24166     //this.addSeparator();
24167         
24168         
24169         var field = this.navgroup.addItem( {
24170             tagtype : 'span',
24171             cls : 'x-paging-position',
24172             
24173             html : this.beforePageText  +
24174                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24175                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24176          } ); //?? escaped?
24177         
24178         this.field = field.el.select('input', true).first();
24179         this.field.on("keydown", this.onPagingKeydown, this);
24180         this.field.on("focus", function(){this.dom.select();});
24181     
24182     
24183         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24184         //this.field.setHeight(18);
24185         //this.addSeparator();
24186         this.next = this.navgroup.addItem({
24187             tooltip: this.nextText,
24188             cls: "next",
24189             html : ' <i class="fa fa-step-forward">',
24190             disabled: true,
24191             preventDefault: true,
24192             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24193         });
24194         this.last = this.navgroup.addItem({
24195             tooltip: this.lastText,
24196             icon : 'fa fa-forward',
24197             cls: "next",
24198             disabled: true,
24199             preventDefault: true,
24200             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24201         });
24202     //this.addSeparator();
24203         this.loading = this.navgroup.addItem({
24204             tooltip: this.refreshText,
24205             icon: 'fa fa-refresh',
24206             preventDefault: true,
24207             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24208         });
24209         
24210     },
24211
24212     // private
24213     updateInfo : function(){
24214         if(this.displayEl){
24215             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24216             var msg = count == 0 ?
24217                 this.emptyMsg :
24218                 String.format(
24219                     this.displayMsg,
24220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24221                 );
24222             this.displayEl.update(msg);
24223         }
24224     },
24225
24226     // private
24227     onLoad : function(ds, r, o)
24228     {
24229         this.cursor = o.params ? o.params.start : 0;
24230         var d = this.getPageData(),
24231             ap = d.activePage,
24232             ps = d.pages;
24233         
24234         
24235         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24236         this.field.dom.value = ap;
24237         this.first.setDisabled(ap == 1);
24238         this.prev.setDisabled(ap == 1);
24239         this.next.setDisabled(ap == ps);
24240         this.last.setDisabled(ap == ps);
24241         this.loading.enable();
24242         this.updateInfo();
24243     },
24244
24245     // private
24246     getPageData : function(){
24247         var total = this.ds.getTotalCount();
24248         return {
24249             total : total,
24250             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24251             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24252         };
24253     },
24254
24255     // private
24256     onLoadError : function(){
24257         this.loading.enable();
24258     },
24259
24260     // private
24261     onPagingKeydown : function(e){
24262         var k = e.getKey();
24263         var d = this.getPageData();
24264         if(k == e.RETURN){
24265             var v = this.field.dom.value, pageNum;
24266             if(!v || isNaN(pageNum = parseInt(v, 10))){
24267                 this.field.dom.value = d.activePage;
24268                 return;
24269             }
24270             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24271             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24272             e.stopEvent();
24273         }
24274         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))
24275         {
24276           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24277           this.field.dom.value = pageNum;
24278           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24279           e.stopEvent();
24280         }
24281         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24282         {
24283           var v = this.field.dom.value, pageNum; 
24284           var increment = (e.shiftKey) ? 10 : 1;
24285           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24286                 increment *= -1;
24287           }
24288           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24289             this.field.dom.value = d.activePage;
24290             return;
24291           }
24292           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24293           {
24294             this.field.dom.value = parseInt(v, 10) + increment;
24295             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24296             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24297           }
24298           e.stopEvent();
24299         }
24300     },
24301
24302     // private
24303     beforeLoad : function(){
24304         if(this.loading){
24305             this.loading.disable();
24306         }
24307     },
24308
24309     // private
24310     onClick : function(which){
24311         
24312         var ds = this.ds;
24313         if (!ds) {
24314             return;
24315         }
24316         
24317         switch(which){
24318             case "first":
24319                 ds.load({params:{start: 0, limit: this.pageSize}});
24320             break;
24321             case "prev":
24322                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24323             break;
24324             case "next":
24325                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24326             break;
24327             case "last":
24328                 var total = ds.getTotalCount();
24329                 var extra = total % this.pageSize;
24330                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24331                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24332             break;
24333             case "refresh":
24334                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24335             break;
24336         }
24337     },
24338
24339     /**
24340      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24341      * @param {Roo.data.Store} store The data store to unbind
24342      */
24343     unbind : function(ds){
24344         ds.un("beforeload", this.beforeLoad, this);
24345         ds.un("load", this.onLoad, this);
24346         ds.un("loadexception", this.onLoadError, this);
24347         ds.un("remove", this.updateInfo, this);
24348         ds.un("add", this.updateInfo, this);
24349         this.ds = undefined;
24350     },
24351
24352     /**
24353      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24354      * @param {Roo.data.Store} store The data store to bind
24355      */
24356     bind : function(ds){
24357         ds.on("beforeload", this.beforeLoad, this);
24358         ds.on("load", this.onLoad, this);
24359         ds.on("loadexception", this.onLoadError, this);
24360         ds.on("remove", this.updateInfo, this);
24361         ds.on("add", this.updateInfo, this);
24362         this.ds = ds;
24363     }
24364 });/*
24365  * - LGPL
24366  *
24367  * element
24368  * 
24369  */
24370
24371 /**
24372  * @class Roo.bootstrap.MessageBar
24373  * @extends Roo.bootstrap.Component
24374  * Bootstrap MessageBar class
24375  * @cfg {String} html contents of the MessageBar
24376  * @cfg {String} weight (info | success | warning | danger) default info
24377  * @cfg {String} beforeClass insert the bar before the given class
24378  * @cfg {Boolean} closable (true | false) default false
24379  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24380  * 
24381  * @constructor
24382  * Create a new Element
24383  * @param {Object} config The config object
24384  */
24385
24386 Roo.bootstrap.MessageBar = function(config){
24387     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24388 };
24389
24390 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24391     
24392     html: '',
24393     weight: 'info',
24394     closable: false,
24395     fixed: false,
24396     beforeClass: 'bootstrap-sticky-wrap',
24397     
24398     getAutoCreate : function(){
24399         
24400         var cfg = {
24401             tag: 'div',
24402             cls: 'alert alert-dismissable alert-' + this.weight,
24403             cn: [
24404                 {
24405                     tag: 'span',
24406                     cls: 'message',
24407                     html: this.html || ''
24408                 }
24409             ]
24410         };
24411         
24412         if(this.fixed){
24413             cfg.cls += ' alert-messages-fixed';
24414         }
24415         
24416         if(this.closable){
24417             cfg.cn.push({
24418                 tag: 'button',
24419                 cls: 'close',
24420                 html: 'x'
24421             });
24422         }
24423         
24424         return cfg;
24425     },
24426     
24427     onRender : function(ct, position)
24428     {
24429         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24430         
24431         if(!this.el){
24432             var cfg = Roo.apply({},  this.getAutoCreate());
24433             cfg.id = Roo.id();
24434             
24435             if (this.cls) {
24436                 cfg.cls += ' ' + this.cls;
24437             }
24438             if (this.style) {
24439                 cfg.style = this.style;
24440             }
24441             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24442             
24443             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24444         }
24445         
24446         this.el.select('>button.close').on('click', this.hide, this);
24447         
24448     },
24449     
24450     show : function()
24451     {
24452         if (!this.rendered) {
24453             this.render();
24454         }
24455         
24456         this.el.show();
24457         
24458         this.fireEvent('show', this);
24459         
24460     },
24461     
24462     hide : function()
24463     {
24464         if (!this.rendered) {
24465             this.render();
24466         }
24467         
24468         this.el.hide();
24469         
24470         this.fireEvent('hide', this);
24471     },
24472     
24473     update : function()
24474     {
24475 //        var e = this.el.dom.firstChild;
24476 //        
24477 //        if(this.closable){
24478 //            e = e.nextSibling;
24479 //        }
24480 //        
24481 //        e.data = this.html || '';
24482
24483         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24484     }
24485    
24486 });
24487
24488  
24489
24490      /*
24491  * - LGPL
24492  *
24493  * Graph
24494  * 
24495  */
24496
24497
24498 /**
24499  * @class Roo.bootstrap.Graph
24500  * @extends Roo.bootstrap.Component
24501  * Bootstrap Graph class
24502 > Prameters
24503  -sm {number} sm 4
24504  -md {number} md 5
24505  @cfg {String} graphtype  bar | vbar | pie
24506  @cfg {number} g_x coodinator | centre x (pie)
24507  @cfg {number} g_y coodinator | centre y (pie)
24508  @cfg {number} g_r radius (pie)
24509  @cfg {number} g_height height of the chart (respected by all elements in the set)
24510  @cfg {number} g_width width of the chart (respected by all elements in the set)
24511  @cfg {Object} title The title of the chart
24512     
24513  -{Array}  values
24514  -opts (object) options for the chart 
24515      o {
24516      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24517      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24518      o vgutter (number)
24519      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.
24520      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24521      o to
24522      o stretch (boolean)
24523      o }
24524  -opts (object) options for the pie
24525      o{
24526      o cut
24527      o startAngle (number)
24528      o endAngle (number)
24529      } 
24530  *
24531  * @constructor
24532  * Create a new Input
24533  * @param {Object} config The config object
24534  */
24535
24536 Roo.bootstrap.Graph = function(config){
24537     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24538     
24539     this.addEvents({
24540         // img events
24541         /**
24542          * @event click
24543          * The img click event for the img.
24544          * @param {Roo.EventObject} e
24545          */
24546         "click" : true
24547     });
24548 };
24549
24550 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24551     
24552     sm: 4,
24553     md: 5,
24554     graphtype: 'bar',
24555     g_height: 250,
24556     g_width: 400,
24557     g_x: 50,
24558     g_y: 50,
24559     g_r: 30,
24560     opts:{
24561         //g_colors: this.colors,
24562         g_type: 'soft',
24563         g_gutter: '20%'
24564
24565     },
24566     title : false,
24567
24568     getAutoCreate : function(){
24569         
24570         var cfg = {
24571             tag: 'div',
24572             html : null
24573         };
24574         
24575         
24576         return  cfg;
24577     },
24578
24579     onRender : function(ct,position){
24580         
24581         
24582         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24583         
24584         if (typeof(Raphael) == 'undefined') {
24585             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24586             return;
24587         }
24588         
24589         this.raphael = Raphael(this.el.dom);
24590         
24591                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24592                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24593                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24594                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24595                 /*
24596                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24597                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24598                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24599                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24600                 
24601                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24602                 r.barchart(330, 10, 300, 220, data1);
24603                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24604                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24605                 */
24606                 
24607                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24608                 // r.barchart(30, 30, 560, 250,  xdata, {
24609                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24610                 //     axis : "0 0 1 1",
24611                 //     axisxlabels :  xdata
24612                 //     //yvalues : cols,
24613                    
24614                 // });
24615 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24616 //        
24617 //        this.load(null,xdata,{
24618 //                axis : "0 0 1 1",
24619 //                axisxlabels :  xdata
24620 //                });
24621
24622     },
24623
24624     load : function(graphtype,xdata,opts)
24625     {
24626         this.raphael.clear();
24627         if(!graphtype) {
24628             graphtype = this.graphtype;
24629         }
24630         if(!opts){
24631             opts = this.opts;
24632         }
24633         var r = this.raphael,
24634             fin = function () {
24635                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24636             },
24637             fout = function () {
24638                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24639             },
24640             pfin = function() {
24641                 this.sector.stop();
24642                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24643
24644                 if (this.label) {
24645                     this.label[0].stop();
24646                     this.label[0].attr({ r: 7.5 });
24647                     this.label[1].attr({ "font-weight": 800 });
24648                 }
24649             },
24650             pfout = function() {
24651                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24652
24653                 if (this.label) {
24654                     this.label[0].animate({ r: 5 }, 500, "bounce");
24655                     this.label[1].attr({ "font-weight": 400 });
24656                 }
24657             };
24658
24659         switch(graphtype){
24660             case 'bar':
24661                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24662                 break;
24663             case 'hbar':
24664                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24665                 break;
24666             case 'pie':
24667 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24668 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24669 //            
24670                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24671                 
24672                 break;
24673
24674         }
24675         
24676         if(this.title){
24677             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24678         }
24679         
24680     },
24681     
24682     setTitle: function(o)
24683     {
24684         this.title = o;
24685     },
24686     
24687     initEvents: function() {
24688         
24689         if(!this.href){
24690             this.el.on('click', this.onClick, this);
24691         }
24692     },
24693     
24694     onClick : function(e)
24695     {
24696         Roo.log('img onclick');
24697         this.fireEvent('click', this, e);
24698     }
24699    
24700 });
24701
24702  
24703 /*
24704  * - LGPL
24705  *
24706  * numberBox
24707  * 
24708  */
24709 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24710
24711 /**
24712  * @class Roo.bootstrap.dash.NumberBox
24713  * @extends Roo.bootstrap.Component
24714  * Bootstrap NumberBox class
24715  * @cfg {String} headline Box headline
24716  * @cfg {String} content Box content
24717  * @cfg {String} icon Box icon
24718  * @cfg {String} footer Footer text
24719  * @cfg {String} fhref Footer href
24720  * 
24721  * @constructor
24722  * Create a new NumberBox
24723  * @param {Object} config The config object
24724  */
24725
24726
24727 Roo.bootstrap.dash.NumberBox = function(config){
24728     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24729     
24730 };
24731
24732 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24733     
24734     headline : '',
24735     content : '',
24736     icon : '',
24737     footer : '',
24738     fhref : '',
24739     ficon : '',
24740     
24741     getAutoCreate : function(){
24742         
24743         var cfg = {
24744             tag : 'div',
24745             cls : 'small-box ',
24746             cn : [
24747                 {
24748                     tag : 'div',
24749                     cls : 'inner',
24750                     cn :[
24751                         {
24752                             tag : 'h3',
24753                             cls : 'roo-headline',
24754                             html : this.headline
24755                         },
24756                         {
24757                             tag : 'p',
24758                             cls : 'roo-content',
24759                             html : this.content
24760                         }
24761                     ]
24762                 }
24763             ]
24764         };
24765         
24766         if(this.icon){
24767             cfg.cn.push({
24768                 tag : 'div',
24769                 cls : 'icon',
24770                 cn :[
24771                     {
24772                         tag : 'i',
24773                         cls : 'ion ' + this.icon
24774                     }
24775                 ]
24776             });
24777         }
24778         
24779         if(this.footer){
24780             var footer = {
24781                 tag : 'a',
24782                 cls : 'small-box-footer',
24783                 href : this.fhref || '#',
24784                 html : this.footer
24785             };
24786             
24787             cfg.cn.push(footer);
24788             
24789         }
24790         
24791         return  cfg;
24792     },
24793
24794     onRender : function(ct,position){
24795         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24796
24797
24798        
24799                 
24800     },
24801
24802     setHeadline: function (value)
24803     {
24804         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24805     },
24806     
24807     setFooter: function (value, href)
24808     {
24809         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24810         
24811         if(href){
24812             this.el.select('a.small-box-footer',true).first().attr('href', href);
24813         }
24814         
24815     },
24816
24817     setContent: function (value)
24818     {
24819         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24820     },
24821
24822     initEvents: function() 
24823     {   
24824         
24825     }
24826     
24827 });
24828
24829  
24830 /*
24831  * - LGPL
24832  *
24833  * TabBox
24834  * 
24835  */
24836 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24837
24838 /**
24839  * @class Roo.bootstrap.dash.TabBox
24840  * @extends Roo.bootstrap.Component
24841  * Bootstrap TabBox class
24842  * @cfg {String} title Title of the TabBox
24843  * @cfg {String} icon Icon of the TabBox
24844  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24845  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24846  * 
24847  * @constructor
24848  * Create a new TabBox
24849  * @param {Object} config The config object
24850  */
24851
24852
24853 Roo.bootstrap.dash.TabBox = function(config){
24854     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24855     this.addEvents({
24856         // raw events
24857         /**
24858          * @event addpane
24859          * When a pane is added
24860          * @param {Roo.bootstrap.dash.TabPane} pane
24861          */
24862         "addpane" : true,
24863         /**
24864          * @event activatepane
24865          * When a pane is activated
24866          * @param {Roo.bootstrap.dash.TabPane} pane
24867          */
24868         "activatepane" : true
24869         
24870          
24871     });
24872     
24873     this.panes = [];
24874 };
24875
24876 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24877
24878     title : '',
24879     icon : false,
24880     showtabs : true,
24881     tabScrollable : false,
24882     
24883     getChildContainer : function()
24884     {
24885         return this.el.select('.tab-content', true).first();
24886     },
24887     
24888     getAutoCreate : function(){
24889         
24890         var header = {
24891             tag: 'li',
24892             cls: 'pull-left header',
24893             html: this.title,
24894             cn : []
24895         };
24896         
24897         if(this.icon){
24898             header.cn.push({
24899                 tag: 'i',
24900                 cls: 'fa ' + this.icon
24901             });
24902         }
24903         
24904         var h = {
24905             tag: 'ul',
24906             cls: 'nav nav-tabs pull-right',
24907             cn: [
24908                 header
24909             ]
24910         };
24911         
24912         if(this.tabScrollable){
24913             h = {
24914                 tag: 'div',
24915                 cls: 'tab-header',
24916                 cn: [
24917                     {
24918                         tag: 'ul',
24919                         cls: 'nav nav-tabs pull-right',
24920                         cn: [
24921                             header
24922                         ]
24923                     }
24924                 ]
24925             };
24926         }
24927         
24928         var cfg = {
24929             tag: 'div',
24930             cls: 'nav-tabs-custom',
24931             cn: [
24932                 h,
24933                 {
24934                     tag: 'div',
24935                     cls: 'tab-content no-padding',
24936                     cn: []
24937                 }
24938             ]
24939         };
24940
24941         return  cfg;
24942     },
24943     initEvents : function()
24944     {
24945         //Roo.log('add add pane handler');
24946         this.on('addpane', this.onAddPane, this);
24947     },
24948      /**
24949      * Updates the box title
24950      * @param {String} html to set the title to.
24951      */
24952     setTitle : function(value)
24953     {
24954         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24955     },
24956     onAddPane : function(pane)
24957     {
24958         this.panes.push(pane);
24959         //Roo.log('addpane');
24960         //Roo.log(pane);
24961         // tabs are rendere left to right..
24962         if(!this.showtabs){
24963             return;
24964         }
24965         
24966         var ctr = this.el.select('.nav-tabs', true).first();
24967          
24968          
24969         var existing = ctr.select('.nav-tab',true);
24970         var qty = existing.getCount();;
24971         
24972         
24973         var tab = ctr.createChild({
24974             tag : 'li',
24975             cls : 'nav-tab' + (qty ? '' : ' active'),
24976             cn : [
24977                 {
24978                     tag : 'a',
24979                     href:'#',
24980                     html : pane.title
24981                 }
24982             ]
24983         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24984         pane.tab = tab;
24985         
24986         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24987         if (!qty) {
24988             pane.el.addClass('active');
24989         }
24990         
24991                 
24992     },
24993     onTabClick : function(ev,un,ob,pane)
24994     {
24995         //Roo.log('tab - prev default');
24996         ev.preventDefault();
24997         
24998         
24999         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25000         pane.tab.addClass('active');
25001         //Roo.log(pane.title);
25002         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25003         // technically we should have a deactivate event.. but maybe add later.
25004         // and it should not de-activate the selected tab...
25005         this.fireEvent('activatepane', pane);
25006         pane.el.addClass('active');
25007         pane.fireEvent('activate');
25008         
25009         
25010     },
25011     
25012     getActivePane : function()
25013     {
25014         var r = false;
25015         Roo.each(this.panes, function(p) {
25016             if(p.el.hasClass('active')){
25017                 r = p;
25018                 return false;
25019             }
25020             
25021             return;
25022         });
25023         
25024         return r;
25025     }
25026     
25027     
25028 });
25029
25030  
25031 /*
25032  * - LGPL
25033  *
25034  * Tab pane
25035  * 
25036  */
25037 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25038 /**
25039  * @class Roo.bootstrap.TabPane
25040  * @extends Roo.bootstrap.Component
25041  * Bootstrap TabPane class
25042  * @cfg {Boolean} active (false | true) Default false
25043  * @cfg {String} title title of panel
25044
25045  * 
25046  * @constructor
25047  * Create a new TabPane
25048  * @param {Object} config The config object
25049  */
25050
25051 Roo.bootstrap.dash.TabPane = function(config){
25052     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25053     
25054     this.addEvents({
25055         // raw events
25056         /**
25057          * @event activate
25058          * When a pane is activated
25059          * @param {Roo.bootstrap.dash.TabPane} pane
25060          */
25061         "activate" : true
25062          
25063     });
25064 };
25065
25066 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25067     
25068     active : false,
25069     title : '',
25070     
25071     // the tabBox that this is attached to.
25072     tab : false,
25073      
25074     getAutoCreate : function() 
25075     {
25076         var cfg = {
25077             tag: 'div',
25078             cls: 'tab-pane'
25079         };
25080         
25081         if(this.active){
25082             cfg.cls += ' active';
25083         }
25084         
25085         return cfg;
25086     },
25087     initEvents  : function()
25088     {
25089         //Roo.log('trigger add pane handler');
25090         this.parent().fireEvent('addpane', this)
25091     },
25092     
25093      /**
25094      * Updates the tab title 
25095      * @param {String} html to set the title to.
25096      */
25097     setTitle: function(str)
25098     {
25099         if (!this.tab) {
25100             return;
25101         }
25102         this.title = str;
25103         this.tab.select('a', true).first().dom.innerHTML = str;
25104         
25105     }
25106     
25107     
25108     
25109 });
25110
25111  
25112
25113
25114  /*
25115  * - LGPL
25116  *
25117  * menu
25118  * 
25119  */
25120 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25121
25122 /**
25123  * @class Roo.bootstrap.menu.Menu
25124  * @extends Roo.bootstrap.Component
25125  * Bootstrap Menu class - container for Menu
25126  * @cfg {String} html Text of the menu
25127  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25128  * @cfg {String} icon Font awesome icon
25129  * @cfg {String} pos Menu align to (top | bottom) default bottom
25130  * 
25131  * 
25132  * @constructor
25133  * Create a new Menu
25134  * @param {Object} config The config object
25135  */
25136
25137
25138 Roo.bootstrap.menu.Menu = function(config){
25139     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25140     
25141     this.addEvents({
25142         /**
25143          * @event beforeshow
25144          * Fires before this menu is displayed
25145          * @param {Roo.bootstrap.menu.Menu} this
25146          */
25147         beforeshow : true,
25148         /**
25149          * @event beforehide
25150          * Fires before this menu is hidden
25151          * @param {Roo.bootstrap.menu.Menu} this
25152          */
25153         beforehide : true,
25154         /**
25155          * @event show
25156          * Fires after this menu is displayed
25157          * @param {Roo.bootstrap.menu.Menu} this
25158          */
25159         show : true,
25160         /**
25161          * @event hide
25162          * Fires after this menu is hidden
25163          * @param {Roo.bootstrap.menu.Menu} this
25164          */
25165         hide : true,
25166         /**
25167          * @event click
25168          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25169          * @param {Roo.bootstrap.menu.Menu} this
25170          * @param {Roo.EventObject} e
25171          */
25172         click : true
25173     });
25174     
25175 };
25176
25177 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25178     
25179     submenu : false,
25180     html : '',
25181     weight : 'default',
25182     icon : false,
25183     pos : 'bottom',
25184     
25185     
25186     getChildContainer : function() {
25187         if(this.isSubMenu){
25188             return this.el;
25189         }
25190         
25191         return this.el.select('ul.dropdown-menu', true).first();  
25192     },
25193     
25194     getAutoCreate : function()
25195     {
25196         var text = [
25197             {
25198                 tag : 'span',
25199                 cls : 'roo-menu-text',
25200                 html : this.html
25201             }
25202         ];
25203         
25204         if(this.icon){
25205             text.unshift({
25206                 tag : 'i',
25207                 cls : 'fa ' + this.icon
25208             })
25209         }
25210         
25211         
25212         var cfg = {
25213             tag : 'div',
25214             cls : 'btn-group',
25215             cn : [
25216                 {
25217                     tag : 'button',
25218                     cls : 'dropdown-button btn btn-' + this.weight,
25219                     cn : text
25220                 },
25221                 {
25222                     tag : 'button',
25223                     cls : 'dropdown-toggle btn btn-' + this.weight,
25224                     cn : [
25225                         {
25226                             tag : 'span',
25227                             cls : 'caret'
25228                         }
25229                     ]
25230                 },
25231                 {
25232                     tag : 'ul',
25233                     cls : 'dropdown-menu'
25234                 }
25235             ]
25236             
25237         };
25238         
25239         if(this.pos == 'top'){
25240             cfg.cls += ' dropup';
25241         }
25242         
25243         if(this.isSubMenu){
25244             cfg = {
25245                 tag : 'ul',
25246                 cls : 'dropdown-menu'
25247             }
25248         }
25249         
25250         return cfg;
25251     },
25252     
25253     onRender : function(ct, position)
25254     {
25255         this.isSubMenu = ct.hasClass('dropdown-submenu');
25256         
25257         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25258     },
25259     
25260     initEvents : function() 
25261     {
25262         if(this.isSubMenu){
25263             return;
25264         }
25265         
25266         this.hidden = true;
25267         
25268         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25269         this.triggerEl.on('click', this.onTriggerPress, this);
25270         
25271         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25272         this.buttonEl.on('click', this.onClick, this);
25273         
25274     },
25275     
25276     list : function()
25277     {
25278         if(this.isSubMenu){
25279             return this.el;
25280         }
25281         
25282         return this.el.select('ul.dropdown-menu', true).first();
25283     },
25284     
25285     onClick : function(e)
25286     {
25287         this.fireEvent("click", this, e);
25288     },
25289     
25290     onTriggerPress  : function(e)
25291     {   
25292         if (this.isVisible()) {
25293             this.hide();
25294         } else {
25295             this.show();
25296         }
25297     },
25298     
25299     isVisible : function(){
25300         return !this.hidden;
25301     },
25302     
25303     show : function()
25304     {
25305         this.fireEvent("beforeshow", this);
25306         
25307         this.hidden = false;
25308         this.el.addClass('open');
25309         
25310         Roo.get(document).on("mouseup", this.onMouseUp, this);
25311         
25312         this.fireEvent("show", this);
25313         
25314         
25315     },
25316     
25317     hide : function()
25318     {
25319         this.fireEvent("beforehide", this);
25320         
25321         this.hidden = true;
25322         this.el.removeClass('open');
25323         
25324         Roo.get(document).un("mouseup", this.onMouseUp);
25325         
25326         this.fireEvent("hide", this);
25327     },
25328     
25329     onMouseUp : function()
25330     {
25331         this.hide();
25332     }
25333     
25334 });
25335
25336  
25337  /*
25338  * - LGPL
25339  *
25340  * menu item
25341  * 
25342  */
25343 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25344
25345 /**
25346  * @class Roo.bootstrap.menu.Item
25347  * @extends Roo.bootstrap.Component
25348  * Bootstrap MenuItem class
25349  * @cfg {Boolean} submenu (true | false) default false
25350  * @cfg {String} html text of the item
25351  * @cfg {String} href the link
25352  * @cfg {Boolean} disable (true | false) default false
25353  * @cfg {Boolean} preventDefault (true | false) default true
25354  * @cfg {String} icon Font awesome icon
25355  * @cfg {String} pos Submenu align to (left | right) default right 
25356  * 
25357  * 
25358  * @constructor
25359  * Create a new Item
25360  * @param {Object} config The config object
25361  */
25362
25363
25364 Roo.bootstrap.menu.Item = function(config){
25365     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25366     this.addEvents({
25367         /**
25368          * @event mouseover
25369          * Fires when the mouse is hovering over this menu
25370          * @param {Roo.bootstrap.menu.Item} this
25371          * @param {Roo.EventObject} e
25372          */
25373         mouseover : true,
25374         /**
25375          * @event mouseout
25376          * Fires when the mouse exits this menu
25377          * @param {Roo.bootstrap.menu.Item} this
25378          * @param {Roo.EventObject} e
25379          */
25380         mouseout : true,
25381         // raw events
25382         /**
25383          * @event click
25384          * The raw click event for the entire grid.
25385          * @param {Roo.EventObject} e
25386          */
25387         click : true
25388     });
25389 };
25390
25391 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25392     
25393     submenu : false,
25394     href : '',
25395     html : '',
25396     preventDefault: true,
25397     disable : false,
25398     icon : false,
25399     pos : 'right',
25400     
25401     getAutoCreate : function()
25402     {
25403         var text = [
25404             {
25405                 tag : 'span',
25406                 cls : 'roo-menu-item-text',
25407                 html : this.html
25408             }
25409         ];
25410         
25411         if(this.icon){
25412             text.unshift({
25413                 tag : 'i',
25414                 cls : 'fa ' + this.icon
25415             })
25416         }
25417         
25418         var cfg = {
25419             tag : 'li',
25420             cn : [
25421                 {
25422                     tag : 'a',
25423                     href : this.href || '#',
25424                     cn : text
25425                 }
25426             ]
25427         };
25428         
25429         if(this.disable){
25430             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25431         }
25432         
25433         if(this.submenu){
25434             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25435             
25436             if(this.pos == 'left'){
25437                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25438             }
25439         }
25440         
25441         return cfg;
25442     },
25443     
25444     initEvents : function() 
25445     {
25446         this.el.on('mouseover', this.onMouseOver, this);
25447         this.el.on('mouseout', this.onMouseOut, this);
25448         
25449         this.el.select('a', true).first().on('click', this.onClick, this);
25450         
25451     },
25452     
25453     onClick : function(e)
25454     {
25455         if(this.preventDefault){
25456             e.preventDefault();
25457         }
25458         
25459         this.fireEvent("click", this, e);
25460     },
25461     
25462     onMouseOver : function(e)
25463     {
25464         if(this.submenu && this.pos == 'left'){
25465             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25466         }
25467         
25468         this.fireEvent("mouseover", this, e);
25469     },
25470     
25471     onMouseOut : function(e)
25472     {
25473         this.fireEvent("mouseout", this, e);
25474     }
25475 });
25476
25477  
25478
25479  /*
25480  * - LGPL
25481  *
25482  * menu separator
25483  * 
25484  */
25485 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25486
25487 /**
25488  * @class Roo.bootstrap.menu.Separator
25489  * @extends Roo.bootstrap.Component
25490  * Bootstrap Separator class
25491  * 
25492  * @constructor
25493  * Create a new Separator
25494  * @param {Object} config The config object
25495  */
25496
25497
25498 Roo.bootstrap.menu.Separator = function(config){
25499     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25500 };
25501
25502 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25503     
25504     getAutoCreate : function(){
25505         var cfg = {
25506             tag : 'li',
25507             cls: 'divider'
25508         };
25509         
25510         return cfg;
25511     }
25512    
25513 });
25514
25515  
25516
25517  /*
25518  * - LGPL
25519  *
25520  * Tooltip
25521  * 
25522  */
25523
25524 /**
25525  * @class Roo.bootstrap.Tooltip
25526  * Bootstrap Tooltip class
25527  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25528  * to determine which dom element triggers the tooltip.
25529  * 
25530  * It needs to add support for additional attributes like tooltip-position
25531  * 
25532  * @constructor
25533  * Create a new Toolti
25534  * @param {Object} config The config object
25535  */
25536
25537 Roo.bootstrap.Tooltip = function(config){
25538     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25539     
25540     this.alignment = Roo.bootstrap.Tooltip.alignment;
25541     
25542     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25543         this.alignment = config.alignment;
25544     }
25545     
25546 };
25547
25548 Roo.apply(Roo.bootstrap.Tooltip, {
25549     /**
25550      * @function init initialize tooltip monitoring.
25551      * @static
25552      */
25553     currentEl : false,
25554     currentTip : false,
25555     currentRegion : false,
25556     
25557     //  init : delay?
25558     
25559     init : function()
25560     {
25561         Roo.get(document).on('mouseover', this.enter ,this);
25562         Roo.get(document).on('mouseout', this.leave, this);
25563          
25564         
25565         this.currentTip = new Roo.bootstrap.Tooltip();
25566     },
25567     
25568     enter : function(ev)
25569     {
25570         var dom = ev.getTarget();
25571         
25572         //Roo.log(['enter',dom]);
25573         var el = Roo.fly(dom);
25574         if (this.currentEl) {
25575             //Roo.log(dom);
25576             //Roo.log(this.currentEl);
25577             //Roo.log(this.currentEl.contains(dom));
25578             if (this.currentEl == el) {
25579                 return;
25580             }
25581             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25582                 return;
25583             }
25584
25585         }
25586         
25587         if (this.currentTip.el) {
25588             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25589         }    
25590         //Roo.log(ev);
25591         
25592         if(!el || el.dom == document){
25593             return;
25594         }
25595         
25596         var bindEl = el;
25597         
25598         // you can not look for children, as if el is the body.. then everythign is the child..
25599         if (!el.attr('tooltip')) { //
25600             if (!el.select("[tooltip]").elements.length) {
25601                 return;
25602             }
25603             // is the mouse over this child...?
25604             bindEl = el.select("[tooltip]").first();
25605             var xy = ev.getXY();
25606             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25607                 //Roo.log("not in region.");
25608                 return;
25609             }
25610             //Roo.log("child element over..");
25611             
25612         }
25613         this.currentEl = bindEl;
25614         this.currentTip.bind(bindEl);
25615         this.currentRegion = Roo.lib.Region.getRegion(dom);
25616         this.currentTip.enter();
25617         
25618     },
25619     leave : function(ev)
25620     {
25621         var dom = ev.getTarget();
25622         //Roo.log(['leave',dom]);
25623         if (!this.currentEl) {
25624             return;
25625         }
25626         
25627         
25628         if (dom != this.currentEl.dom) {
25629             return;
25630         }
25631         var xy = ev.getXY();
25632         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25633             return;
25634         }
25635         // only activate leave if mouse cursor is outside... bounding box..
25636         
25637         
25638         
25639         
25640         if (this.currentTip) {
25641             this.currentTip.leave();
25642         }
25643         //Roo.log('clear currentEl');
25644         this.currentEl = false;
25645         
25646         
25647     },
25648     alignment : {
25649         'left' : ['r-l', [-2,0], 'right'],
25650         'right' : ['l-r', [2,0], 'left'],
25651         'bottom' : ['t-b', [0,2], 'top'],
25652         'top' : [ 'b-t', [0,-2], 'bottom']
25653     }
25654     
25655 });
25656
25657
25658 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25659     
25660     
25661     bindEl : false,
25662     
25663     delay : null, // can be { show : 300 , hide: 500}
25664     
25665     timeout : null,
25666     
25667     hoverState : null, //???
25668     
25669     placement : 'bottom', 
25670     
25671     alignment : false,
25672     
25673     getAutoCreate : function(){
25674     
25675         var cfg = {
25676            cls : 'tooltip',
25677            role : 'tooltip',
25678            cn : [
25679                 {
25680                     cls : 'tooltip-arrow'
25681                 },
25682                 {
25683                     cls : 'tooltip-inner'
25684                 }
25685            ]
25686         };
25687         
25688         return cfg;
25689     },
25690     bind : function(el)
25691     {
25692         this.bindEl = el;
25693     },
25694       
25695     
25696     enter : function () {
25697        
25698         if (this.timeout != null) {
25699             clearTimeout(this.timeout);
25700         }
25701         
25702         this.hoverState = 'in';
25703          //Roo.log("enter - show");
25704         if (!this.delay || !this.delay.show) {
25705             this.show();
25706             return;
25707         }
25708         var _t = this;
25709         this.timeout = setTimeout(function () {
25710             if (_t.hoverState == 'in') {
25711                 _t.show();
25712             }
25713         }, this.delay.show);
25714     },
25715     leave : function()
25716     {
25717         clearTimeout(this.timeout);
25718     
25719         this.hoverState = 'out';
25720          if (!this.delay || !this.delay.hide) {
25721             this.hide();
25722             return;
25723         }
25724        
25725         var _t = this;
25726         this.timeout = setTimeout(function () {
25727             //Roo.log("leave - timeout");
25728             
25729             if (_t.hoverState == 'out') {
25730                 _t.hide();
25731                 Roo.bootstrap.Tooltip.currentEl = false;
25732             }
25733         }, delay);
25734     },
25735     
25736     show : function (msg)
25737     {
25738         if (!this.el) {
25739             this.render(document.body);
25740         }
25741         // set content.
25742         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25743         
25744         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25745         
25746         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25747         
25748         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25749         
25750         var placement = typeof this.placement == 'function' ?
25751             this.placement.call(this, this.el, on_el) :
25752             this.placement;
25753             
25754         var autoToken = /\s?auto?\s?/i;
25755         var autoPlace = autoToken.test(placement);
25756         if (autoPlace) {
25757             placement = placement.replace(autoToken, '') || 'top';
25758         }
25759         
25760         //this.el.detach()
25761         //this.el.setXY([0,0]);
25762         this.el.show();
25763         //this.el.dom.style.display='block';
25764         
25765         //this.el.appendTo(on_el);
25766         
25767         var p = this.getPosition();
25768         var box = this.el.getBox();
25769         
25770         if (autoPlace) {
25771             // fixme..
25772         }
25773         
25774         var align = this.alignment[placement];
25775         
25776         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25777         
25778         if(placement == 'top' || placement == 'bottom'){
25779             if(xy[0] < 0){
25780                 placement = 'right';
25781             }
25782             
25783             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25784                 placement = 'left';
25785             }
25786             
25787             var scroll = Roo.select('body', true).first().getScroll();
25788             
25789             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25790                 placement = 'top';
25791             }
25792             
25793         }
25794         
25795         this.el.alignTo(this.bindEl, align[0],align[1]);
25796         //var arrow = this.el.select('.arrow',true).first();
25797         //arrow.set(align[2], 
25798         
25799         this.el.addClass(placement);
25800         
25801         this.el.addClass('in fade');
25802         
25803         this.hoverState = null;
25804         
25805         if (this.el.hasClass('fade')) {
25806             // fade it?
25807         }
25808         
25809     },
25810     hide : function()
25811     {
25812          
25813         if (!this.el) {
25814             return;
25815         }
25816         //this.el.setXY([0,0]);
25817         this.el.removeClass('in');
25818         //this.el.hide();
25819         
25820     }
25821     
25822 });
25823  
25824
25825  /*
25826  * - LGPL
25827  *
25828  * Location Picker
25829  * 
25830  */
25831
25832 /**
25833  * @class Roo.bootstrap.LocationPicker
25834  * @extends Roo.bootstrap.Component
25835  * Bootstrap LocationPicker class
25836  * @cfg {Number} latitude Position when init default 0
25837  * @cfg {Number} longitude Position when init default 0
25838  * @cfg {Number} zoom default 15
25839  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25840  * @cfg {Boolean} mapTypeControl default false
25841  * @cfg {Boolean} disableDoubleClickZoom default false
25842  * @cfg {Boolean} scrollwheel default true
25843  * @cfg {Boolean} streetViewControl default false
25844  * @cfg {Number} radius default 0
25845  * @cfg {String} locationName
25846  * @cfg {Boolean} draggable default true
25847  * @cfg {Boolean} enableAutocomplete default false
25848  * @cfg {Boolean} enableReverseGeocode default true
25849  * @cfg {String} markerTitle
25850  * 
25851  * @constructor
25852  * Create a new LocationPicker
25853  * @param {Object} config The config object
25854  */
25855
25856
25857 Roo.bootstrap.LocationPicker = function(config){
25858     
25859     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25860     
25861     this.addEvents({
25862         /**
25863          * @event initial
25864          * Fires when the picker initialized.
25865          * @param {Roo.bootstrap.LocationPicker} this
25866          * @param {Google Location} location
25867          */
25868         initial : true,
25869         /**
25870          * @event positionchanged
25871          * Fires when the picker position changed.
25872          * @param {Roo.bootstrap.LocationPicker} this
25873          * @param {Google Location} location
25874          */
25875         positionchanged : true,
25876         /**
25877          * @event resize
25878          * Fires when the map resize.
25879          * @param {Roo.bootstrap.LocationPicker} this
25880          */
25881         resize : true,
25882         /**
25883          * @event show
25884          * Fires when the map show.
25885          * @param {Roo.bootstrap.LocationPicker} this
25886          */
25887         show : true,
25888         /**
25889          * @event hide
25890          * Fires when the map hide.
25891          * @param {Roo.bootstrap.LocationPicker} this
25892          */
25893         hide : true,
25894         /**
25895          * @event mapClick
25896          * Fires when click the map.
25897          * @param {Roo.bootstrap.LocationPicker} this
25898          * @param {Map event} e
25899          */
25900         mapClick : true,
25901         /**
25902          * @event mapRightClick
25903          * Fires when right click the map.
25904          * @param {Roo.bootstrap.LocationPicker} this
25905          * @param {Map event} e
25906          */
25907         mapRightClick : true,
25908         /**
25909          * @event markerClick
25910          * Fires when click the marker.
25911          * @param {Roo.bootstrap.LocationPicker} this
25912          * @param {Map event} e
25913          */
25914         markerClick : true,
25915         /**
25916          * @event markerRightClick
25917          * Fires when right click the marker.
25918          * @param {Roo.bootstrap.LocationPicker} this
25919          * @param {Map event} e
25920          */
25921         markerRightClick : true,
25922         /**
25923          * @event OverlayViewDraw
25924          * Fires when OverlayView Draw
25925          * @param {Roo.bootstrap.LocationPicker} this
25926          */
25927         OverlayViewDraw : true,
25928         /**
25929          * @event OverlayViewOnAdd
25930          * Fires when OverlayView Draw
25931          * @param {Roo.bootstrap.LocationPicker} this
25932          */
25933         OverlayViewOnAdd : true,
25934         /**
25935          * @event OverlayViewOnRemove
25936          * Fires when OverlayView Draw
25937          * @param {Roo.bootstrap.LocationPicker} this
25938          */
25939         OverlayViewOnRemove : true,
25940         /**
25941          * @event OverlayViewShow
25942          * Fires when OverlayView Draw
25943          * @param {Roo.bootstrap.LocationPicker} this
25944          * @param {Pixel} cpx
25945          */
25946         OverlayViewShow : true,
25947         /**
25948          * @event OverlayViewHide
25949          * Fires when OverlayView Draw
25950          * @param {Roo.bootstrap.LocationPicker} this
25951          */
25952         OverlayViewHide : true,
25953         /**
25954          * @event loadexception
25955          * Fires when load google lib failed.
25956          * @param {Roo.bootstrap.LocationPicker} this
25957          */
25958         loadexception : true
25959     });
25960         
25961 };
25962
25963 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25964     
25965     gMapContext: false,
25966     
25967     latitude: 0,
25968     longitude: 0,
25969     zoom: 15,
25970     mapTypeId: false,
25971     mapTypeControl: false,
25972     disableDoubleClickZoom: false,
25973     scrollwheel: true,
25974     streetViewControl: false,
25975     radius: 0,
25976     locationName: '',
25977     draggable: true,
25978     enableAutocomplete: false,
25979     enableReverseGeocode: true,
25980     markerTitle: '',
25981     
25982     getAutoCreate: function()
25983     {
25984
25985         var cfg = {
25986             tag: 'div',
25987             cls: 'roo-location-picker'
25988         };
25989         
25990         return cfg
25991     },
25992     
25993     initEvents: function(ct, position)
25994     {       
25995         if(!this.el.getWidth() || this.isApplied()){
25996             return;
25997         }
25998         
25999         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26000         
26001         this.initial();
26002     },
26003     
26004     initial: function()
26005     {
26006         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26007             this.fireEvent('loadexception', this);
26008             return;
26009         }
26010         
26011         if(!this.mapTypeId){
26012             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26013         }
26014         
26015         this.gMapContext = this.GMapContext();
26016         
26017         this.initOverlayView();
26018         
26019         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26020         
26021         var _this = this;
26022                 
26023         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26024             _this.setPosition(_this.gMapContext.marker.position);
26025         });
26026         
26027         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26028             _this.fireEvent('mapClick', this, event);
26029             
26030         });
26031
26032         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26033             _this.fireEvent('mapRightClick', this, event);
26034             
26035         });
26036         
26037         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26038             _this.fireEvent('markerClick', this, event);
26039             
26040         });
26041
26042         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26043             _this.fireEvent('markerRightClick', this, event);
26044             
26045         });
26046         
26047         this.setPosition(this.gMapContext.location);
26048         
26049         this.fireEvent('initial', this, this.gMapContext.location);
26050     },
26051     
26052     initOverlayView: function()
26053     {
26054         var _this = this;
26055         
26056         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26057             
26058             draw: function()
26059             {
26060                 _this.fireEvent('OverlayViewDraw', _this);
26061             },
26062             
26063             onAdd: function()
26064             {
26065                 _this.fireEvent('OverlayViewOnAdd', _this);
26066             },
26067             
26068             onRemove: function()
26069             {
26070                 _this.fireEvent('OverlayViewOnRemove', _this);
26071             },
26072             
26073             show: function(cpx)
26074             {
26075                 _this.fireEvent('OverlayViewShow', _this, cpx);
26076             },
26077             
26078             hide: function()
26079             {
26080                 _this.fireEvent('OverlayViewHide', _this);
26081             }
26082             
26083         });
26084     },
26085     
26086     fromLatLngToContainerPixel: function(event)
26087     {
26088         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26089     },
26090     
26091     isApplied: function() 
26092     {
26093         return this.getGmapContext() == false ? false : true;
26094     },
26095     
26096     getGmapContext: function() 
26097     {
26098         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26099     },
26100     
26101     GMapContext: function() 
26102     {
26103         var position = new google.maps.LatLng(this.latitude, this.longitude);
26104         
26105         var _map = new google.maps.Map(this.el.dom, {
26106             center: position,
26107             zoom: this.zoom,
26108             mapTypeId: this.mapTypeId,
26109             mapTypeControl: this.mapTypeControl,
26110             disableDoubleClickZoom: this.disableDoubleClickZoom,
26111             scrollwheel: this.scrollwheel,
26112             streetViewControl: this.streetViewControl,
26113             locationName: this.locationName,
26114             draggable: this.draggable,
26115             enableAutocomplete: this.enableAutocomplete,
26116             enableReverseGeocode: this.enableReverseGeocode
26117         });
26118         
26119         var _marker = new google.maps.Marker({
26120             position: position,
26121             map: _map,
26122             title: this.markerTitle,
26123             draggable: this.draggable
26124         });
26125         
26126         return {
26127             map: _map,
26128             marker: _marker,
26129             circle: null,
26130             location: position,
26131             radius: this.radius,
26132             locationName: this.locationName,
26133             addressComponents: {
26134                 formatted_address: null,
26135                 addressLine1: null,
26136                 addressLine2: null,
26137                 streetName: null,
26138                 streetNumber: null,
26139                 city: null,
26140                 district: null,
26141                 state: null,
26142                 stateOrProvince: null
26143             },
26144             settings: this,
26145             domContainer: this.el.dom,
26146             geodecoder: new google.maps.Geocoder()
26147         };
26148     },
26149     
26150     drawCircle: function(center, radius, options) 
26151     {
26152         if (this.gMapContext.circle != null) {
26153             this.gMapContext.circle.setMap(null);
26154         }
26155         if (radius > 0) {
26156             radius *= 1;
26157             options = Roo.apply({}, options, {
26158                 strokeColor: "#0000FF",
26159                 strokeOpacity: .35,
26160                 strokeWeight: 2,
26161                 fillColor: "#0000FF",
26162                 fillOpacity: .2
26163             });
26164             
26165             options.map = this.gMapContext.map;
26166             options.radius = radius;
26167             options.center = center;
26168             this.gMapContext.circle = new google.maps.Circle(options);
26169             return this.gMapContext.circle;
26170         }
26171         
26172         return null;
26173     },
26174     
26175     setPosition: function(location) 
26176     {
26177         this.gMapContext.location = location;
26178         this.gMapContext.marker.setPosition(location);
26179         this.gMapContext.map.panTo(location);
26180         this.drawCircle(location, this.gMapContext.radius, {});
26181         
26182         var _this = this;
26183         
26184         if (this.gMapContext.settings.enableReverseGeocode) {
26185             this.gMapContext.geodecoder.geocode({
26186                 latLng: this.gMapContext.location
26187             }, function(results, status) {
26188                 
26189                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26190                     _this.gMapContext.locationName = results[0].formatted_address;
26191                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26192                     
26193                     _this.fireEvent('positionchanged', this, location);
26194                 }
26195             });
26196             
26197             return;
26198         }
26199         
26200         this.fireEvent('positionchanged', this, location);
26201     },
26202     
26203     resize: function()
26204     {
26205         google.maps.event.trigger(this.gMapContext.map, "resize");
26206         
26207         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26208         
26209         this.fireEvent('resize', this);
26210     },
26211     
26212     setPositionByLatLng: function(latitude, longitude)
26213     {
26214         this.setPosition(new google.maps.LatLng(latitude, longitude));
26215     },
26216     
26217     getCurrentPosition: function() 
26218     {
26219         return {
26220             latitude: this.gMapContext.location.lat(),
26221             longitude: this.gMapContext.location.lng()
26222         };
26223     },
26224     
26225     getAddressName: function() 
26226     {
26227         return this.gMapContext.locationName;
26228     },
26229     
26230     getAddressComponents: function() 
26231     {
26232         return this.gMapContext.addressComponents;
26233     },
26234     
26235     address_component_from_google_geocode: function(address_components) 
26236     {
26237         var result = {};
26238         
26239         for (var i = 0; i < address_components.length; i++) {
26240             var component = address_components[i];
26241             if (component.types.indexOf("postal_code") >= 0) {
26242                 result.postalCode = component.short_name;
26243             } else if (component.types.indexOf("street_number") >= 0) {
26244                 result.streetNumber = component.short_name;
26245             } else if (component.types.indexOf("route") >= 0) {
26246                 result.streetName = component.short_name;
26247             } else if (component.types.indexOf("neighborhood") >= 0) {
26248                 result.city = component.short_name;
26249             } else if (component.types.indexOf("locality") >= 0) {
26250                 result.city = component.short_name;
26251             } else if (component.types.indexOf("sublocality") >= 0) {
26252                 result.district = component.short_name;
26253             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26254                 result.stateOrProvince = component.short_name;
26255             } else if (component.types.indexOf("country") >= 0) {
26256                 result.country = component.short_name;
26257             }
26258         }
26259         
26260         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26261         result.addressLine2 = "";
26262         return result;
26263     },
26264     
26265     setZoomLevel: function(zoom)
26266     {
26267         this.gMapContext.map.setZoom(zoom);
26268     },
26269     
26270     show: function()
26271     {
26272         if(!this.el){
26273             return;
26274         }
26275         
26276         this.el.show();
26277         
26278         this.resize();
26279         
26280         this.fireEvent('show', this);
26281     },
26282     
26283     hide: function()
26284     {
26285         if(!this.el){
26286             return;
26287         }
26288         
26289         this.el.hide();
26290         
26291         this.fireEvent('hide', this);
26292     }
26293     
26294 });
26295
26296 Roo.apply(Roo.bootstrap.LocationPicker, {
26297     
26298     OverlayView : function(map, options)
26299     {
26300         options = options || {};
26301         
26302         this.setMap(map);
26303     }
26304     
26305     
26306 });/*
26307  * - LGPL
26308  *
26309  * Alert
26310  * 
26311  */
26312
26313 /**
26314  * @class Roo.bootstrap.Alert
26315  * @extends Roo.bootstrap.Component
26316  * Bootstrap Alert class
26317  * @cfg {String} title The title of alert
26318  * @cfg {String} html The content of alert
26319  * @cfg {String} weight (  success | info | warning | danger )
26320  * @cfg {String} faicon font-awesomeicon
26321  * 
26322  * @constructor
26323  * Create a new alert
26324  * @param {Object} config The config object
26325  */
26326
26327
26328 Roo.bootstrap.Alert = function(config){
26329     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26330     
26331 };
26332
26333 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26334     
26335     title: '',
26336     html: '',
26337     weight: false,
26338     faicon: false,
26339     
26340     getAutoCreate : function()
26341     {
26342         
26343         var cfg = {
26344             tag : 'div',
26345             cls : 'alert',
26346             cn : [
26347                 {
26348                     tag : 'i',
26349                     cls : 'roo-alert-icon'
26350                     
26351                 },
26352                 {
26353                     tag : 'b',
26354                     cls : 'roo-alert-title',
26355                     html : this.title
26356                 },
26357                 {
26358                     tag : 'span',
26359                     cls : 'roo-alert-text',
26360                     html : this.html
26361                 }
26362             ]
26363         };
26364         
26365         if(this.faicon){
26366             cfg.cn[0].cls += ' fa ' + this.faicon;
26367         }
26368         
26369         if(this.weight){
26370             cfg.cls += ' alert-' + this.weight;
26371         }
26372         
26373         return cfg;
26374     },
26375     
26376     initEvents: function() 
26377     {
26378         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26379     },
26380     
26381     setTitle : function(str)
26382     {
26383         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26384     },
26385     
26386     setText : function(str)
26387     {
26388         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26389     },
26390     
26391     setWeight : function(weight)
26392     {
26393         if(this.weight){
26394             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26395         }
26396         
26397         this.weight = weight;
26398         
26399         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26400     },
26401     
26402     setIcon : function(icon)
26403     {
26404         if(this.faicon){
26405             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26406         }
26407         
26408         this.faicon = icon;
26409         
26410         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26411     },
26412     
26413     hide: function() 
26414     {
26415         this.el.hide();   
26416     },
26417     
26418     show: function() 
26419     {  
26420         this.el.show();   
26421     }
26422     
26423 });
26424
26425  
26426 /*
26427 * Licence: LGPL
26428 */
26429
26430 /**
26431  * @class Roo.bootstrap.UploadCropbox
26432  * @extends Roo.bootstrap.Component
26433  * Bootstrap UploadCropbox class
26434  * @cfg {String} emptyText show when image has been loaded
26435  * @cfg {String} rotateNotify show when image too small to rotate
26436  * @cfg {Number} errorTimeout default 3000
26437  * @cfg {Number} minWidth default 300
26438  * @cfg {Number} minHeight default 300
26439  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26440  * @cfg {Boolean} isDocument (true|false) default false
26441  * @cfg {String} url action url
26442  * @cfg {String} paramName default 'imageUpload'
26443  * @cfg {String} method default POST
26444  * @cfg {Boolean} loadMask (true|false) default true
26445  * @cfg {Boolean} loadingText default 'Loading...'
26446  * 
26447  * @constructor
26448  * Create a new UploadCropbox
26449  * @param {Object} config The config object
26450  */
26451
26452 Roo.bootstrap.UploadCropbox = function(config){
26453     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26454     
26455     this.addEvents({
26456         /**
26457          * @event beforeselectfile
26458          * Fire before select file
26459          * @param {Roo.bootstrap.UploadCropbox} this
26460          */
26461         "beforeselectfile" : true,
26462         /**
26463          * @event initial
26464          * Fire after initEvent
26465          * @param {Roo.bootstrap.UploadCropbox} this
26466          */
26467         "initial" : true,
26468         /**
26469          * @event crop
26470          * Fire after initEvent
26471          * @param {Roo.bootstrap.UploadCropbox} this
26472          * @param {String} data
26473          */
26474         "crop" : true,
26475         /**
26476          * @event prepare
26477          * Fire when preparing the file data
26478          * @param {Roo.bootstrap.UploadCropbox} this
26479          * @param {Object} file
26480          */
26481         "prepare" : true,
26482         /**
26483          * @event exception
26484          * Fire when get exception
26485          * @param {Roo.bootstrap.UploadCropbox} this
26486          * @param {XMLHttpRequest} xhr
26487          */
26488         "exception" : true,
26489         /**
26490          * @event beforeloadcanvas
26491          * Fire before load the canvas
26492          * @param {Roo.bootstrap.UploadCropbox} this
26493          * @param {String} src
26494          */
26495         "beforeloadcanvas" : true,
26496         /**
26497          * @event trash
26498          * Fire when trash image
26499          * @param {Roo.bootstrap.UploadCropbox} this
26500          */
26501         "trash" : true,
26502         /**
26503          * @event download
26504          * Fire when download the image
26505          * @param {Roo.bootstrap.UploadCropbox} this
26506          */
26507         "download" : true,
26508         /**
26509          * @event footerbuttonclick
26510          * Fire when footerbuttonclick
26511          * @param {Roo.bootstrap.UploadCropbox} this
26512          * @param {String} type
26513          */
26514         "footerbuttonclick" : true,
26515         /**
26516          * @event resize
26517          * Fire when resize
26518          * @param {Roo.bootstrap.UploadCropbox} this
26519          */
26520         "resize" : true,
26521         /**
26522          * @event rotate
26523          * Fire when rotate the image
26524          * @param {Roo.bootstrap.UploadCropbox} this
26525          * @param {String} pos
26526          */
26527         "rotate" : true,
26528         /**
26529          * @event inspect
26530          * Fire when inspect the file
26531          * @param {Roo.bootstrap.UploadCropbox} this
26532          * @param {Object} file
26533          */
26534         "inspect" : true,
26535         /**
26536          * @event upload
26537          * Fire when xhr upload the file
26538          * @param {Roo.bootstrap.UploadCropbox} this
26539          * @param {Object} data
26540          */
26541         "upload" : true,
26542         /**
26543          * @event arrange
26544          * Fire when arrange the file data
26545          * @param {Roo.bootstrap.UploadCropbox} this
26546          * @param {Object} formData
26547          */
26548         "arrange" : true
26549     });
26550     
26551     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26552 };
26553
26554 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26555     
26556     emptyText : 'Click to upload image',
26557     rotateNotify : 'Image is too small to rotate',
26558     errorTimeout : 3000,
26559     scale : 0,
26560     baseScale : 1,
26561     rotate : 0,
26562     dragable : false,
26563     pinching : false,
26564     mouseX : 0,
26565     mouseY : 0,
26566     cropData : false,
26567     minWidth : 300,
26568     minHeight : 300,
26569     file : false,
26570     exif : {},
26571     baseRotate : 1,
26572     cropType : 'image/jpeg',
26573     buttons : false,
26574     canvasLoaded : false,
26575     isDocument : false,
26576     method : 'POST',
26577     paramName : 'imageUpload',
26578     loadMask : true,
26579     loadingText : 'Loading...',
26580     maskEl : false,
26581     
26582     getAutoCreate : function()
26583     {
26584         var cfg = {
26585             tag : 'div',
26586             cls : 'roo-upload-cropbox',
26587             cn : [
26588                 {
26589                     tag : 'input',
26590                     cls : 'roo-upload-cropbox-selector',
26591                     type : 'file'
26592                 },
26593                 {
26594                     tag : 'div',
26595                     cls : 'roo-upload-cropbox-body',
26596                     style : 'cursor:pointer',
26597                     cn : [
26598                         {
26599                             tag : 'div',
26600                             cls : 'roo-upload-cropbox-preview'
26601                         },
26602                         {
26603                             tag : 'div',
26604                             cls : 'roo-upload-cropbox-thumb'
26605                         },
26606                         {
26607                             tag : 'div',
26608                             cls : 'roo-upload-cropbox-empty-notify',
26609                             html : this.emptyText
26610                         },
26611                         {
26612                             tag : 'div',
26613                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26614                             html : this.rotateNotify
26615                         }
26616                     ]
26617                 },
26618                 {
26619                     tag : 'div',
26620                     cls : 'roo-upload-cropbox-footer',
26621                     cn : {
26622                         tag : 'div',
26623                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26624                         cn : []
26625                     }
26626                 }
26627             ]
26628         };
26629         
26630         return cfg;
26631     },
26632     
26633     onRender : function(ct, position)
26634     {
26635         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26636         
26637         if (this.buttons.length) {
26638             
26639             Roo.each(this.buttons, function(bb) {
26640                 
26641                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26642                 
26643                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26644                 
26645             }, this);
26646         }
26647         
26648         if(this.loadMask){
26649             this.maskEl = this.el;
26650         }
26651     },
26652     
26653     initEvents : function()
26654     {
26655         this.urlAPI = (window.createObjectURL && window) || 
26656                                 (window.URL && URL.revokeObjectURL && URL) || 
26657                                 (window.webkitURL && webkitURL);
26658                         
26659         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26660         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26661         
26662         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26663         this.selectorEl.hide();
26664         
26665         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26666         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26667         
26668         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26669         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26670         this.thumbEl.hide();
26671         
26672         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26673         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26674         
26675         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26676         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26677         this.errorEl.hide();
26678         
26679         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26680         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26681         this.footerEl.hide();
26682         
26683         this.setThumbBoxSize();
26684         
26685         this.bind();
26686         
26687         this.resize();
26688         
26689         this.fireEvent('initial', this);
26690     },
26691
26692     bind : function()
26693     {
26694         var _this = this;
26695         
26696         window.addEventListener("resize", function() { _this.resize(); } );
26697         
26698         this.bodyEl.on('click', this.beforeSelectFile, this);
26699         
26700         if(Roo.isTouch){
26701             this.bodyEl.on('touchstart', this.onTouchStart, this);
26702             this.bodyEl.on('touchmove', this.onTouchMove, this);
26703             this.bodyEl.on('touchend', this.onTouchEnd, this);
26704         }
26705         
26706         if(!Roo.isTouch){
26707             this.bodyEl.on('mousedown', this.onMouseDown, this);
26708             this.bodyEl.on('mousemove', this.onMouseMove, this);
26709             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26710             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26711             Roo.get(document).on('mouseup', this.onMouseUp, this);
26712         }
26713         
26714         this.selectorEl.on('change', this.onFileSelected, this);
26715     },
26716     
26717     reset : function()
26718     {    
26719         this.scale = 0;
26720         this.baseScale = 1;
26721         this.rotate = 0;
26722         this.baseRotate = 1;
26723         this.dragable = false;
26724         this.pinching = false;
26725         this.mouseX = 0;
26726         this.mouseY = 0;
26727         this.cropData = false;
26728         this.notifyEl.dom.innerHTML = this.emptyText;
26729         
26730         this.selectorEl.dom.value = '';
26731         
26732     },
26733     
26734     resize : function()
26735     {
26736         if(this.fireEvent('resize', this) != false){
26737             this.setThumbBoxPosition();
26738             this.setCanvasPosition();
26739         }
26740     },
26741     
26742     onFooterButtonClick : function(e, el, o, type)
26743     {
26744         switch (type) {
26745             case 'rotate-left' :
26746                 this.onRotateLeft(e);
26747                 break;
26748             case 'rotate-right' :
26749                 this.onRotateRight(e);
26750                 break;
26751             case 'picture' :
26752                 this.beforeSelectFile(e);
26753                 break;
26754             case 'trash' :
26755                 this.trash(e);
26756                 break;
26757             case 'crop' :
26758                 this.crop(e);
26759                 break;
26760             case 'download' :
26761                 this.download(e);
26762                 break;
26763             default :
26764                 break;
26765         }
26766         
26767         this.fireEvent('footerbuttonclick', this, type);
26768     },
26769     
26770     beforeSelectFile : function(e)
26771     {
26772         e.preventDefault();
26773         
26774         if(this.fireEvent('beforeselectfile', this) != false){
26775             this.selectorEl.dom.click();
26776         }
26777     },
26778     
26779     onFileSelected : function(e)
26780     {
26781         e.preventDefault();
26782         
26783         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26784             return;
26785         }
26786         
26787         var file = this.selectorEl.dom.files[0];
26788         
26789         if(this.fireEvent('inspect', this, file) != false){
26790             this.prepare(file);
26791         }
26792         
26793     },
26794     
26795     trash : function(e)
26796     {
26797         this.fireEvent('trash', this);
26798     },
26799     
26800     download : function(e)
26801     {
26802         this.fireEvent('download', this);
26803     },
26804     
26805     loadCanvas : function(src)
26806     {   
26807         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26808             
26809             this.reset();
26810             
26811             this.imageEl = document.createElement('img');
26812             
26813             var _this = this;
26814             
26815             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26816             
26817             this.imageEl.src = src;
26818         }
26819     },
26820     
26821     onLoadCanvas : function()
26822     {   
26823         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26824         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26825         
26826         this.bodyEl.un('click', this.beforeSelectFile, this);
26827         
26828         this.notifyEl.hide();
26829         this.thumbEl.show();
26830         this.footerEl.show();
26831         
26832         this.baseRotateLevel();
26833         
26834         if(this.isDocument){
26835             this.setThumbBoxSize();
26836         }
26837         
26838         this.setThumbBoxPosition();
26839         
26840         this.baseScaleLevel();
26841         
26842         this.draw();
26843         
26844         this.resize();
26845         
26846         this.canvasLoaded = true;
26847         
26848         if(this.loadMask){
26849             this.maskEl.unmask();
26850         }
26851         
26852     },
26853     
26854     setCanvasPosition : function()
26855     {   
26856         if(!this.canvasEl){
26857             return;
26858         }
26859         
26860         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26861         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26862         
26863         this.previewEl.setLeft(pw);
26864         this.previewEl.setTop(ph);
26865         
26866     },
26867     
26868     onMouseDown : function(e)
26869     {   
26870         e.stopEvent();
26871         
26872         this.dragable = true;
26873         this.pinching = false;
26874         
26875         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26876             this.dragable = false;
26877             return;
26878         }
26879         
26880         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26881         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26882         
26883     },
26884     
26885     onMouseMove : function(e)
26886     {   
26887         e.stopEvent();
26888         
26889         if(!this.canvasLoaded){
26890             return;
26891         }
26892         
26893         if (!this.dragable){
26894             return;
26895         }
26896         
26897         var minX = Math.ceil(this.thumbEl.getLeft(true));
26898         var minY = Math.ceil(this.thumbEl.getTop(true));
26899         
26900         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26901         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26902         
26903         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26904         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26905         
26906         x = x - this.mouseX;
26907         y = y - this.mouseY;
26908         
26909         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26910         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26911         
26912         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26913         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26914         
26915         this.previewEl.setLeft(bgX);
26916         this.previewEl.setTop(bgY);
26917         
26918         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26919         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26920     },
26921     
26922     onMouseUp : function(e)
26923     {   
26924         e.stopEvent();
26925         
26926         this.dragable = false;
26927     },
26928     
26929     onMouseWheel : function(e)
26930     {   
26931         e.stopEvent();
26932         
26933         this.startScale = this.scale;
26934         
26935         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26936         
26937         if(!this.zoomable()){
26938             this.scale = this.startScale;
26939             return;
26940         }
26941         
26942         this.draw();
26943         
26944         return;
26945     },
26946     
26947     zoomable : function()
26948     {
26949         var minScale = this.thumbEl.getWidth() / this.minWidth;
26950         
26951         if(this.minWidth < this.minHeight){
26952             minScale = this.thumbEl.getHeight() / this.minHeight;
26953         }
26954         
26955         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26956         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26957         
26958         if(
26959                 this.isDocument &&
26960                 (this.rotate == 0 || this.rotate == 180) && 
26961                 (
26962                     width > this.imageEl.OriginWidth || 
26963                     height > this.imageEl.OriginHeight ||
26964                     (width < this.minWidth && height < this.minHeight)
26965                 )
26966         ){
26967             return false;
26968         }
26969         
26970         if(
26971                 this.isDocument &&
26972                 (this.rotate == 90 || this.rotate == 270) && 
26973                 (
26974                     width > this.imageEl.OriginWidth || 
26975                     height > this.imageEl.OriginHeight ||
26976                     (width < this.minHeight && height < this.minWidth)
26977                 )
26978         ){
26979             return false;
26980         }
26981         
26982         if(
26983                 !this.isDocument &&
26984                 (this.rotate == 0 || this.rotate == 180) && 
26985                 (
26986                     width < this.minWidth || 
26987                     width > this.imageEl.OriginWidth || 
26988                     height < this.minHeight || 
26989                     height > this.imageEl.OriginHeight
26990                 )
26991         ){
26992             return false;
26993         }
26994         
26995         if(
26996                 !this.isDocument &&
26997                 (this.rotate == 90 || this.rotate == 270) && 
26998                 (
26999                     width < this.minHeight || 
27000                     width > this.imageEl.OriginWidth || 
27001                     height < this.minWidth || 
27002                     height > this.imageEl.OriginHeight
27003                 )
27004         ){
27005             return false;
27006         }
27007         
27008         return true;
27009         
27010     },
27011     
27012     onRotateLeft : function(e)
27013     {   
27014         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27015             
27016             var minScale = this.thumbEl.getWidth() / this.minWidth;
27017             
27018             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27019             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27020             
27021             this.startScale = this.scale;
27022             
27023             while (this.getScaleLevel() < minScale){
27024             
27025                 this.scale = this.scale + 1;
27026                 
27027                 if(!this.zoomable()){
27028                     break;
27029                 }
27030                 
27031                 if(
27032                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27033                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27034                 ){
27035                     continue;
27036                 }
27037                 
27038                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27039
27040                 this.draw();
27041                 
27042                 return;
27043             }
27044             
27045             this.scale = this.startScale;
27046             
27047             this.onRotateFail();
27048             
27049             return false;
27050         }
27051         
27052         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27053
27054         if(this.isDocument){
27055             this.setThumbBoxSize();
27056             this.setThumbBoxPosition();
27057             this.setCanvasPosition();
27058         }
27059         
27060         this.draw();
27061         
27062         this.fireEvent('rotate', this, 'left');
27063         
27064     },
27065     
27066     onRotateRight : function(e)
27067     {
27068         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27069             
27070             var minScale = this.thumbEl.getWidth() / this.minWidth;
27071         
27072             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27073             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27074             
27075             this.startScale = this.scale;
27076             
27077             while (this.getScaleLevel() < minScale){
27078             
27079                 this.scale = this.scale + 1;
27080                 
27081                 if(!this.zoomable()){
27082                     break;
27083                 }
27084                 
27085                 if(
27086                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27087                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27088                 ){
27089                     continue;
27090                 }
27091                 
27092                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27093
27094                 this.draw();
27095                 
27096                 return;
27097             }
27098             
27099             this.scale = this.startScale;
27100             
27101             this.onRotateFail();
27102             
27103             return false;
27104         }
27105         
27106         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27107
27108         if(this.isDocument){
27109             this.setThumbBoxSize();
27110             this.setThumbBoxPosition();
27111             this.setCanvasPosition();
27112         }
27113         
27114         this.draw();
27115         
27116         this.fireEvent('rotate', this, 'right');
27117     },
27118     
27119     onRotateFail : function()
27120     {
27121         this.errorEl.show(true);
27122         
27123         var _this = this;
27124         
27125         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27126     },
27127     
27128     draw : function()
27129     {
27130         this.previewEl.dom.innerHTML = '';
27131         
27132         var canvasEl = document.createElement("canvas");
27133         
27134         var contextEl = canvasEl.getContext("2d");
27135         
27136         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27137         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27138         var center = this.imageEl.OriginWidth / 2;
27139         
27140         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27141             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27142             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27143             center = this.imageEl.OriginHeight / 2;
27144         }
27145         
27146         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27147         
27148         contextEl.translate(center, center);
27149         contextEl.rotate(this.rotate * Math.PI / 180);
27150
27151         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27152         
27153         this.canvasEl = document.createElement("canvas");
27154         
27155         this.contextEl = this.canvasEl.getContext("2d");
27156         
27157         switch (this.rotate) {
27158             case 0 :
27159                 
27160                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27161                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27162                 
27163                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27164                 
27165                 break;
27166             case 90 : 
27167                 
27168                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27169                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27170                 
27171                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27172                     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);
27173                     break;
27174                 }
27175                 
27176                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27177                 
27178                 break;
27179             case 180 :
27180                 
27181                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27182                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27183                 
27184                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27185                     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);
27186                     break;
27187                 }
27188                 
27189                 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);
27190                 
27191                 break;
27192             case 270 :
27193                 
27194                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27195                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27196         
27197                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27198                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27199                     break;
27200                 }
27201                 
27202                 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);
27203                 
27204                 break;
27205             default : 
27206                 break;
27207         }
27208         
27209         this.previewEl.appendChild(this.canvasEl);
27210         
27211         this.setCanvasPosition();
27212     },
27213     
27214     crop : function()
27215     {
27216         if(!this.canvasLoaded){
27217             return;
27218         }
27219         
27220         var imageCanvas = document.createElement("canvas");
27221         
27222         var imageContext = imageCanvas.getContext("2d");
27223         
27224         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27225         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27226         
27227         var center = imageCanvas.width / 2;
27228         
27229         imageContext.translate(center, center);
27230         
27231         imageContext.rotate(this.rotate * Math.PI / 180);
27232         
27233         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27234         
27235         var canvas = document.createElement("canvas");
27236         
27237         var context = canvas.getContext("2d");
27238                 
27239         canvas.width = this.minWidth;
27240         canvas.height = this.minHeight;
27241
27242         switch (this.rotate) {
27243             case 0 :
27244                 
27245                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27246                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27247                 
27248                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27249                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27250                 
27251                 var targetWidth = this.minWidth - 2 * x;
27252                 var targetHeight = this.minHeight - 2 * y;
27253                 
27254                 var scale = 1;
27255                 
27256                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27257                     scale = targetWidth / width;
27258                 }
27259                 
27260                 if(x > 0 && y == 0){
27261                     scale = targetHeight / height;
27262                 }
27263                 
27264                 if(x > 0 && y > 0){
27265                     scale = targetWidth / width;
27266                     
27267                     if(width < height){
27268                         scale = targetHeight / height;
27269                     }
27270                 }
27271                 
27272                 context.scale(scale, scale);
27273                 
27274                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27275                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27276
27277                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27278                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27279
27280                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27281                 
27282                 break;
27283             case 90 : 
27284                 
27285                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27286                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27287                 
27288                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27289                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27290                 
27291                 var targetWidth = this.minWidth - 2 * x;
27292                 var targetHeight = this.minHeight - 2 * y;
27293                 
27294                 var scale = 1;
27295                 
27296                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27297                     scale = targetWidth / width;
27298                 }
27299                 
27300                 if(x > 0 && y == 0){
27301                     scale = targetHeight / height;
27302                 }
27303                 
27304                 if(x > 0 && y > 0){
27305                     scale = targetWidth / width;
27306                     
27307                     if(width < height){
27308                         scale = targetHeight / height;
27309                     }
27310                 }
27311                 
27312                 context.scale(scale, scale);
27313                 
27314                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27315                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27316
27317                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27318                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27319                 
27320                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27321                 
27322                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27323                 
27324                 break;
27325             case 180 :
27326                 
27327                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27328                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27329                 
27330                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27331                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27332                 
27333                 var targetWidth = this.minWidth - 2 * x;
27334                 var targetHeight = this.minHeight - 2 * y;
27335                 
27336                 var scale = 1;
27337                 
27338                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27339                     scale = targetWidth / width;
27340                 }
27341                 
27342                 if(x > 0 && y == 0){
27343                     scale = targetHeight / height;
27344                 }
27345                 
27346                 if(x > 0 && y > 0){
27347                     scale = targetWidth / width;
27348                     
27349                     if(width < height){
27350                         scale = targetHeight / height;
27351                     }
27352                 }
27353                 
27354                 context.scale(scale, scale);
27355                 
27356                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27357                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27358
27359                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27360                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27361
27362                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27363                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27364                 
27365                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27366                 
27367                 break;
27368             case 270 :
27369                 
27370                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27371                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27372                 
27373                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27374                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27375                 
27376                 var targetWidth = this.minWidth - 2 * x;
27377                 var targetHeight = this.minHeight - 2 * y;
27378                 
27379                 var scale = 1;
27380                 
27381                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27382                     scale = targetWidth / width;
27383                 }
27384                 
27385                 if(x > 0 && y == 0){
27386                     scale = targetHeight / height;
27387                 }
27388                 
27389                 if(x > 0 && y > 0){
27390                     scale = targetWidth / width;
27391                     
27392                     if(width < height){
27393                         scale = targetHeight / height;
27394                     }
27395                 }
27396                 
27397                 context.scale(scale, scale);
27398                 
27399                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27400                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27401
27402                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27403                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27404                 
27405                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27406                 
27407                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27408                 
27409                 break;
27410             default : 
27411                 break;
27412         }
27413         
27414         this.cropData = canvas.toDataURL(this.cropType);
27415         
27416         if(this.fireEvent('crop', this, this.cropData) !== false){
27417             this.process(this.file, this.cropData);
27418         }
27419         
27420         return;
27421         
27422     },
27423     
27424     setThumbBoxSize : function()
27425     {
27426         var width, height;
27427         
27428         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27429             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27430             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27431             
27432             this.minWidth = width;
27433             this.minHeight = height;
27434             
27435             if(this.rotate == 90 || this.rotate == 270){
27436                 this.minWidth = height;
27437                 this.minHeight = width;
27438             }
27439         }
27440         
27441         height = 300;
27442         width = Math.ceil(this.minWidth * height / this.minHeight);
27443         
27444         if(this.minWidth > this.minHeight){
27445             width = 300;
27446             height = Math.ceil(this.minHeight * width / this.minWidth);
27447         }
27448         
27449         this.thumbEl.setStyle({
27450             width : width + 'px',
27451             height : height + 'px'
27452         });
27453
27454         return;
27455             
27456     },
27457     
27458     setThumbBoxPosition : function()
27459     {
27460         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27461         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27462         
27463         this.thumbEl.setLeft(x);
27464         this.thumbEl.setTop(y);
27465         
27466     },
27467     
27468     baseRotateLevel : function()
27469     {
27470         this.baseRotate = 1;
27471         
27472         if(
27473                 typeof(this.exif) != 'undefined' &&
27474                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27475                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27476         ){
27477             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27478         }
27479         
27480         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27481         
27482     },
27483     
27484     baseScaleLevel : function()
27485     {
27486         var width, height;
27487         
27488         if(this.isDocument){
27489             
27490             if(this.baseRotate == 6 || this.baseRotate == 8){
27491             
27492                 height = this.thumbEl.getHeight();
27493                 this.baseScale = height / this.imageEl.OriginWidth;
27494
27495                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27496                     width = this.thumbEl.getWidth();
27497                     this.baseScale = width / this.imageEl.OriginHeight;
27498                 }
27499
27500                 return;
27501             }
27502
27503             height = this.thumbEl.getHeight();
27504             this.baseScale = height / this.imageEl.OriginHeight;
27505
27506             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27507                 width = this.thumbEl.getWidth();
27508                 this.baseScale = width / this.imageEl.OriginWidth;
27509             }
27510
27511             return;
27512         }
27513         
27514         if(this.baseRotate == 6 || this.baseRotate == 8){
27515             
27516             width = this.thumbEl.getHeight();
27517             this.baseScale = width / this.imageEl.OriginHeight;
27518             
27519             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27520                 height = this.thumbEl.getWidth();
27521                 this.baseScale = height / this.imageEl.OriginHeight;
27522             }
27523             
27524             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27525                 height = this.thumbEl.getWidth();
27526                 this.baseScale = height / this.imageEl.OriginHeight;
27527                 
27528                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27529                     width = this.thumbEl.getHeight();
27530                     this.baseScale = width / this.imageEl.OriginWidth;
27531                 }
27532             }
27533             
27534             return;
27535         }
27536         
27537         width = this.thumbEl.getWidth();
27538         this.baseScale = width / this.imageEl.OriginWidth;
27539         
27540         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27541             height = this.thumbEl.getHeight();
27542             this.baseScale = height / this.imageEl.OriginHeight;
27543         }
27544         
27545         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27546             
27547             height = this.thumbEl.getHeight();
27548             this.baseScale = height / this.imageEl.OriginHeight;
27549             
27550             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27551                 width = this.thumbEl.getWidth();
27552                 this.baseScale = width / this.imageEl.OriginWidth;
27553             }
27554             
27555         }
27556         
27557         return;
27558     },
27559     
27560     getScaleLevel : function()
27561     {
27562         return this.baseScale * Math.pow(1.1, this.scale);
27563     },
27564     
27565     onTouchStart : function(e)
27566     {
27567         if(!this.canvasLoaded){
27568             this.beforeSelectFile(e);
27569             return;
27570         }
27571         
27572         var touches = e.browserEvent.touches;
27573         
27574         if(!touches){
27575             return;
27576         }
27577         
27578         if(touches.length == 1){
27579             this.onMouseDown(e);
27580             return;
27581         }
27582         
27583         if(touches.length != 2){
27584             return;
27585         }
27586         
27587         var coords = [];
27588         
27589         for(var i = 0, finger; finger = touches[i]; i++){
27590             coords.push(finger.pageX, finger.pageY);
27591         }
27592         
27593         var x = Math.pow(coords[0] - coords[2], 2);
27594         var y = Math.pow(coords[1] - coords[3], 2);
27595         
27596         this.startDistance = Math.sqrt(x + y);
27597         
27598         this.startScale = this.scale;
27599         
27600         this.pinching = true;
27601         this.dragable = false;
27602         
27603     },
27604     
27605     onTouchMove : function(e)
27606     {
27607         if(!this.pinching && !this.dragable){
27608             return;
27609         }
27610         
27611         var touches = e.browserEvent.touches;
27612         
27613         if(!touches){
27614             return;
27615         }
27616         
27617         if(this.dragable){
27618             this.onMouseMove(e);
27619             return;
27620         }
27621         
27622         var coords = [];
27623         
27624         for(var i = 0, finger; finger = touches[i]; i++){
27625             coords.push(finger.pageX, finger.pageY);
27626         }
27627         
27628         var x = Math.pow(coords[0] - coords[2], 2);
27629         var y = Math.pow(coords[1] - coords[3], 2);
27630         
27631         this.endDistance = Math.sqrt(x + y);
27632         
27633         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27634         
27635         if(!this.zoomable()){
27636             this.scale = this.startScale;
27637             return;
27638         }
27639         
27640         this.draw();
27641         
27642     },
27643     
27644     onTouchEnd : function(e)
27645     {
27646         this.pinching = false;
27647         this.dragable = false;
27648         
27649     },
27650     
27651     process : function(file, crop)
27652     {
27653         if(this.loadMask){
27654             this.maskEl.mask(this.loadingText);
27655         }
27656         
27657         this.xhr = new XMLHttpRequest();
27658         
27659         file.xhr = this.xhr;
27660
27661         this.xhr.open(this.method, this.url, true);
27662         
27663         var headers = {
27664             "Accept": "application/json",
27665             "Cache-Control": "no-cache",
27666             "X-Requested-With": "XMLHttpRequest"
27667         };
27668         
27669         for (var headerName in headers) {
27670             var headerValue = headers[headerName];
27671             if (headerValue) {
27672                 this.xhr.setRequestHeader(headerName, headerValue);
27673             }
27674         }
27675         
27676         var _this = this;
27677         
27678         this.xhr.onload = function()
27679         {
27680             _this.xhrOnLoad(_this.xhr);
27681         }
27682         
27683         this.xhr.onerror = function()
27684         {
27685             _this.xhrOnError(_this.xhr);
27686         }
27687         
27688         var formData = new FormData();
27689
27690         formData.append('returnHTML', 'NO');
27691         
27692         if(crop){
27693             formData.append('crop', crop);
27694         }
27695         
27696         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27697             formData.append(this.paramName, file, file.name);
27698         }
27699         
27700         if(typeof(file.filename) != 'undefined'){
27701             formData.append('filename', file.filename);
27702         }
27703         
27704         if(typeof(file.mimetype) != 'undefined'){
27705             formData.append('mimetype', file.mimetype);
27706         }
27707         
27708         if(this.fireEvent('arrange', this, formData) != false){
27709             this.xhr.send(formData);
27710         };
27711     },
27712     
27713     xhrOnLoad : function(xhr)
27714     {
27715         if(this.loadMask){
27716             this.maskEl.unmask();
27717         }
27718         
27719         if (xhr.readyState !== 4) {
27720             this.fireEvent('exception', this, xhr);
27721             return;
27722         }
27723
27724         var response = Roo.decode(xhr.responseText);
27725         
27726         if(!response.success){
27727             this.fireEvent('exception', this, xhr);
27728             return;
27729         }
27730         
27731         var response = Roo.decode(xhr.responseText);
27732         
27733         this.fireEvent('upload', this, response);
27734         
27735     },
27736     
27737     xhrOnError : function()
27738     {
27739         if(this.loadMask){
27740             this.maskEl.unmask();
27741         }
27742         
27743         Roo.log('xhr on error');
27744         
27745         var response = Roo.decode(xhr.responseText);
27746           
27747         Roo.log(response);
27748         
27749     },
27750     
27751     prepare : function(file)
27752     {   
27753         if(this.loadMask){
27754             this.maskEl.mask(this.loadingText);
27755         }
27756         
27757         this.file = false;
27758         this.exif = {};
27759         
27760         if(typeof(file) === 'string'){
27761             this.loadCanvas(file);
27762             return;
27763         }
27764         
27765         if(!file || !this.urlAPI){
27766             return;
27767         }
27768         
27769         this.file = file;
27770         this.cropType = file.type;
27771         
27772         var _this = this;
27773         
27774         if(this.fireEvent('prepare', this, this.file) != false){
27775             
27776             var reader = new FileReader();
27777             
27778             reader.onload = function (e) {
27779                 if (e.target.error) {
27780                     Roo.log(e.target.error);
27781                     return;
27782                 }
27783                 
27784                 var buffer = e.target.result,
27785                     dataView = new DataView(buffer),
27786                     offset = 2,
27787                     maxOffset = dataView.byteLength - 4,
27788                     markerBytes,
27789                     markerLength;
27790                 
27791                 if (dataView.getUint16(0) === 0xffd8) {
27792                     while (offset < maxOffset) {
27793                         markerBytes = dataView.getUint16(offset);
27794                         
27795                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27796                             markerLength = dataView.getUint16(offset + 2) + 2;
27797                             if (offset + markerLength > dataView.byteLength) {
27798                                 Roo.log('Invalid meta data: Invalid segment size.');
27799                                 break;
27800                             }
27801                             
27802                             if(markerBytes == 0xffe1){
27803                                 _this.parseExifData(
27804                                     dataView,
27805                                     offset,
27806                                     markerLength
27807                                 );
27808                             }
27809                             
27810                             offset += markerLength;
27811                             
27812                             continue;
27813                         }
27814                         
27815                         break;
27816                     }
27817                     
27818                 }
27819                 
27820                 var url = _this.urlAPI.createObjectURL(_this.file);
27821                 
27822                 _this.loadCanvas(url);
27823                 
27824                 return;
27825             }
27826             
27827             reader.readAsArrayBuffer(this.file);
27828             
27829         }
27830         
27831     },
27832     
27833     parseExifData : function(dataView, offset, length)
27834     {
27835         var tiffOffset = offset + 10,
27836             littleEndian,
27837             dirOffset;
27838     
27839         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27840             // No Exif data, might be XMP data instead
27841             return;
27842         }
27843         
27844         // Check for the ASCII code for "Exif" (0x45786966):
27845         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27846             // No Exif data, might be XMP data instead
27847             return;
27848         }
27849         if (tiffOffset + 8 > dataView.byteLength) {
27850             Roo.log('Invalid Exif data: Invalid segment size.');
27851             return;
27852         }
27853         // Check for the two null bytes:
27854         if (dataView.getUint16(offset + 8) !== 0x0000) {
27855             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27856             return;
27857         }
27858         // Check the byte alignment:
27859         switch (dataView.getUint16(tiffOffset)) {
27860         case 0x4949:
27861             littleEndian = true;
27862             break;
27863         case 0x4D4D:
27864             littleEndian = false;
27865             break;
27866         default:
27867             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27868             return;
27869         }
27870         // Check for the TIFF tag marker (0x002A):
27871         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27872             Roo.log('Invalid Exif data: Missing TIFF marker.');
27873             return;
27874         }
27875         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27876         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27877         
27878         this.parseExifTags(
27879             dataView,
27880             tiffOffset,
27881             tiffOffset + dirOffset,
27882             littleEndian
27883         );
27884     },
27885     
27886     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27887     {
27888         var tagsNumber,
27889             dirEndOffset,
27890             i;
27891         if (dirOffset + 6 > dataView.byteLength) {
27892             Roo.log('Invalid Exif data: Invalid directory offset.');
27893             return;
27894         }
27895         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27896         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27897         if (dirEndOffset + 4 > dataView.byteLength) {
27898             Roo.log('Invalid Exif data: Invalid directory size.');
27899             return;
27900         }
27901         for (i = 0; i < tagsNumber; i += 1) {
27902             this.parseExifTag(
27903                 dataView,
27904                 tiffOffset,
27905                 dirOffset + 2 + 12 * i, // tag offset
27906                 littleEndian
27907             );
27908         }
27909         // Return the offset to the next directory:
27910         return dataView.getUint32(dirEndOffset, littleEndian);
27911     },
27912     
27913     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27914     {
27915         var tag = dataView.getUint16(offset, littleEndian);
27916         
27917         this.exif[tag] = this.getExifValue(
27918             dataView,
27919             tiffOffset,
27920             offset,
27921             dataView.getUint16(offset + 2, littleEndian), // tag type
27922             dataView.getUint32(offset + 4, littleEndian), // tag length
27923             littleEndian
27924         );
27925     },
27926     
27927     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27928     {
27929         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27930             tagSize,
27931             dataOffset,
27932             values,
27933             i,
27934             str,
27935             c;
27936     
27937         if (!tagType) {
27938             Roo.log('Invalid Exif data: Invalid tag type.');
27939             return;
27940         }
27941         
27942         tagSize = tagType.size * length;
27943         // Determine if the value is contained in the dataOffset bytes,
27944         // or if the value at the dataOffset is a pointer to the actual data:
27945         dataOffset = tagSize > 4 ?
27946                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27947         if (dataOffset + tagSize > dataView.byteLength) {
27948             Roo.log('Invalid Exif data: Invalid data offset.');
27949             return;
27950         }
27951         if (length === 1) {
27952             return tagType.getValue(dataView, dataOffset, littleEndian);
27953         }
27954         values = [];
27955         for (i = 0; i < length; i += 1) {
27956             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27957         }
27958         
27959         if (tagType.ascii) {
27960             str = '';
27961             // Concatenate the chars:
27962             for (i = 0; i < values.length; i += 1) {
27963                 c = values[i];
27964                 // Ignore the terminating NULL byte(s):
27965                 if (c === '\u0000') {
27966                     break;
27967                 }
27968                 str += c;
27969             }
27970             return str;
27971         }
27972         return values;
27973     }
27974     
27975 });
27976
27977 Roo.apply(Roo.bootstrap.UploadCropbox, {
27978     tags : {
27979         'Orientation': 0x0112
27980     },
27981     
27982     Orientation: {
27983             1: 0, //'top-left',
27984 //            2: 'top-right',
27985             3: 180, //'bottom-right',
27986 //            4: 'bottom-left',
27987 //            5: 'left-top',
27988             6: 90, //'right-top',
27989 //            7: 'right-bottom',
27990             8: 270 //'left-bottom'
27991     },
27992     
27993     exifTagTypes : {
27994         // byte, 8-bit unsigned int:
27995         1: {
27996             getValue: function (dataView, dataOffset) {
27997                 return dataView.getUint8(dataOffset);
27998             },
27999             size: 1
28000         },
28001         // ascii, 8-bit byte:
28002         2: {
28003             getValue: function (dataView, dataOffset) {
28004                 return String.fromCharCode(dataView.getUint8(dataOffset));
28005             },
28006             size: 1,
28007             ascii: true
28008         },
28009         // short, 16 bit int:
28010         3: {
28011             getValue: function (dataView, dataOffset, littleEndian) {
28012                 return dataView.getUint16(dataOffset, littleEndian);
28013             },
28014             size: 2
28015         },
28016         // long, 32 bit int:
28017         4: {
28018             getValue: function (dataView, dataOffset, littleEndian) {
28019                 return dataView.getUint32(dataOffset, littleEndian);
28020             },
28021             size: 4
28022         },
28023         // rational = two long values, first is numerator, second is denominator:
28024         5: {
28025             getValue: function (dataView, dataOffset, littleEndian) {
28026                 return dataView.getUint32(dataOffset, littleEndian) /
28027                     dataView.getUint32(dataOffset + 4, littleEndian);
28028             },
28029             size: 8
28030         },
28031         // slong, 32 bit signed int:
28032         9: {
28033             getValue: function (dataView, dataOffset, littleEndian) {
28034                 return dataView.getInt32(dataOffset, littleEndian);
28035             },
28036             size: 4
28037         },
28038         // srational, two slongs, first is numerator, second is denominator:
28039         10: {
28040             getValue: function (dataView, dataOffset, littleEndian) {
28041                 return dataView.getInt32(dataOffset, littleEndian) /
28042                     dataView.getInt32(dataOffset + 4, littleEndian);
28043             },
28044             size: 8
28045         }
28046     },
28047     
28048     footer : {
28049         STANDARD : [
28050             {
28051                 tag : 'div',
28052                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28053                 action : 'rotate-left',
28054                 cn : [
28055                     {
28056                         tag : 'button',
28057                         cls : 'btn btn-default',
28058                         html : '<i class="fa fa-undo"></i>'
28059                     }
28060                 ]
28061             },
28062             {
28063                 tag : 'div',
28064                 cls : 'btn-group roo-upload-cropbox-picture',
28065                 action : 'picture',
28066                 cn : [
28067                     {
28068                         tag : 'button',
28069                         cls : 'btn btn-default',
28070                         html : '<i class="fa fa-picture-o"></i>'
28071                     }
28072                 ]
28073             },
28074             {
28075                 tag : 'div',
28076                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28077                 action : 'rotate-right',
28078                 cn : [
28079                     {
28080                         tag : 'button',
28081                         cls : 'btn btn-default',
28082                         html : '<i class="fa fa-repeat"></i>'
28083                     }
28084                 ]
28085             }
28086         ],
28087         DOCUMENT : [
28088             {
28089                 tag : 'div',
28090                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28091                 action : 'rotate-left',
28092                 cn : [
28093                     {
28094                         tag : 'button',
28095                         cls : 'btn btn-default',
28096                         html : '<i class="fa fa-undo"></i>'
28097                     }
28098                 ]
28099             },
28100             {
28101                 tag : 'div',
28102                 cls : 'btn-group roo-upload-cropbox-download',
28103                 action : 'download',
28104                 cn : [
28105                     {
28106                         tag : 'button',
28107                         cls : 'btn btn-default',
28108                         html : '<i class="fa fa-download"></i>'
28109                     }
28110                 ]
28111             },
28112             {
28113                 tag : 'div',
28114                 cls : 'btn-group roo-upload-cropbox-crop',
28115                 action : 'crop',
28116                 cn : [
28117                     {
28118                         tag : 'button',
28119                         cls : 'btn btn-default',
28120                         html : '<i class="fa fa-crop"></i>'
28121                     }
28122                 ]
28123             },
28124             {
28125                 tag : 'div',
28126                 cls : 'btn-group roo-upload-cropbox-trash',
28127                 action : 'trash',
28128                 cn : [
28129                     {
28130                         tag : 'button',
28131                         cls : 'btn btn-default',
28132                         html : '<i class="fa fa-trash"></i>'
28133                     }
28134                 ]
28135             },
28136             {
28137                 tag : 'div',
28138                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28139                 action : 'rotate-right',
28140                 cn : [
28141                     {
28142                         tag : 'button',
28143                         cls : 'btn btn-default',
28144                         html : '<i class="fa fa-repeat"></i>'
28145                     }
28146                 ]
28147             }
28148         ],
28149         ROTATOR : [
28150             {
28151                 tag : 'div',
28152                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28153                 action : 'rotate-left',
28154                 cn : [
28155                     {
28156                         tag : 'button',
28157                         cls : 'btn btn-default',
28158                         html : '<i class="fa fa-undo"></i>'
28159                     }
28160                 ]
28161             },
28162             {
28163                 tag : 'div',
28164                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28165                 action : 'rotate-right',
28166                 cn : [
28167                     {
28168                         tag : 'button',
28169                         cls : 'btn btn-default',
28170                         html : '<i class="fa fa-repeat"></i>'
28171                     }
28172                 ]
28173             }
28174         ]
28175     }
28176 });
28177
28178 /*
28179 * Licence: LGPL
28180 */
28181
28182 /**
28183  * @class Roo.bootstrap.DocumentManager
28184  * @extends Roo.bootstrap.Component
28185  * Bootstrap DocumentManager class
28186  * @cfg {String} paramName default 'imageUpload'
28187  * @cfg {String} toolTipName default 'filename'
28188  * @cfg {String} method default POST
28189  * @cfg {String} url action url
28190  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28191  * @cfg {Boolean} multiple multiple upload default true
28192  * @cfg {Number} thumbSize default 300
28193  * @cfg {String} fieldLabel
28194  * @cfg {Number} labelWidth default 4
28195  * @cfg {String} labelAlign (left|top) default left
28196  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28197 * @cfg {Number} labellg set the width of label (1-12)
28198  * @cfg {Number} labelmd set the width of label (1-12)
28199  * @cfg {Number} labelsm set the width of label (1-12)
28200  * @cfg {Number} labelxs set the width of label (1-12)
28201  * 
28202  * @constructor
28203  * Create a new DocumentManager
28204  * @param {Object} config The config object
28205  */
28206
28207 Roo.bootstrap.DocumentManager = function(config){
28208     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28209     
28210     this.files = [];
28211     this.delegates = [];
28212     
28213     this.addEvents({
28214         /**
28215          * @event initial
28216          * Fire when initial the DocumentManager
28217          * @param {Roo.bootstrap.DocumentManager} this
28218          */
28219         "initial" : true,
28220         /**
28221          * @event inspect
28222          * inspect selected file
28223          * @param {Roo.bootstrap.DocumentManager} this
28224          * @param {File} file
28225          */
28226         "inspect" : true,
28227         /**
28228          * @event exception
28229          * Fire when xhr load exception
28230          * @param {Roo.bootstrap.DocumentManager} this
28231          * @param {XMLHttpRequest} xhr
28232          */
28233         "exception" : true,
28234         /**
28235          * @event afterupload
28236          * Fire when xhr load exception
28237          * @param {Roo.bootstrap.DocumentManager} this
28238          * @param {XMLHttpRequest} xhr
28239          */
28240         "afterupload" : true,
28241         /**
28242          * @event prepare
28243          * prepare the form data
28244          * @param {Roo.bootstrap.DocumentManager} this
28245          * @param {Object} formData
28246          */
28247         "prepare" : true,
28248         /**
28249          * @event remove
28250          * Fire when remove the file
28251          * @param {Roo.bootstrap.DocumentManager} this
28252          * @param {Object} file
28253          */
28254         "remove" : true,
28255         /**
28256          * @event refresh
28257          * Fire after refresh the file
28258          * @param {Roo.bootstrap.DocumentManager} this
28259          */
28260         "refresh" : true,
28261         /**
28262          * @event click
28263          * Fire after click the image
28264          * @param {Roo.bootstrap.DocumentManager} this
28265          * @param {Object} file
28266          */
28267         "click" : true,
28268         /**
28269          * @event edit
28270          * Fire when upload a image and editable set to true
28271          * @param {Roo.bootstrap.DocumentManager} this
28272          * @param {Object} file
28273          */
28274         "edit" : true,
28275         /**
28276          * @event beforeselectfile
28277          * Fire before select file
28278          * @param {Roo.bootstrap.DocumentManager} this
28279          */
28280         "beforeselectfile" : true,
28281         /**
28282          * @event process
28283          * Fire before process file
28284          * @param {Roo.bootstrap.DocumentManager} this
28285          * @param {Object} file
28286          */
28287         "process" : true
28288         
28289     });
28290 };
28291
28292 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28293     
28294     boxes : 0,
28295     inputName : '',
28296     thumbSize : 300,
28297     multiple : true,
28298     files : false,
28299     method : 'POST',
28300     url : '',
28301     paramName : 'imageUpload',
28302     toolTipName : 'filename',
28303     fieldLabel : '',
28304     labelWidth : 4,
28305     labelAlign : 'left',
28306     editable : true,
28307     delegates : false,
28308     xhr : false, 
28309     
28310     labellg : 0,
28311     labelmd : 0,
28312     labelsm : 0,
28313     labelxs : 0,
28314     
28315     getAutoCreate : function()
28316     {   
28317         var managerWidget = {
28318             tag : 'div',
28319             cls : 'roo-document-manager',
28320             cn : [
28321                 {
28322                     tag : 'input',
28323                     cls : 'roo-document-manager-selector',
28324                     type : 'file'
28325                 },
28326                 {
28327                     tag : 'div',
28328                     cls : 'roo-document-manager-uploader',
28329                     cn : [
28330                         {
28331                             tag : 'div',
28332                             cls : 'roo-document-manager-upload-btn',
28333                             html : '<i class="fa fa-plus"></i>'
28334                         }
28335                     ]
28336                     
28337                 }
28338             ]
28339         };
28340         
28341         var content = [
28342             {
28343                 tag : 'div',
28344                 cls : 'column col-md-12',
28345                 cn : managerWidget
28346             }
28347         ];
28348         
28349         if(this.fieldLabel.length){
28350             
28351             content = [
28352                 {
28353                     tag : 'div',
28354                     cls : 'column col-md-12',
28355                     html : this.fieldLabel
28356                 },
28357                 {
28358                     tag : 'div',
28359                     cls : 'column col-md-12',
28360                     cn : managerWidget
28361                 }
28362             ];
28363
28364             if(this.labelAlign == 'left'){
28365                 content = [
28366                     {
28367                         tag : 'div',
28368                         cls : 'column',
28369                         html : this.fieldLabel
28370                     },
28371                     {
28372                         tag : 'div',
28373                         cls : 'column',
28374                         cn : managerWidget
28375                     }
28376                 ];
28377                 
28378                 if(this.labelWidth > 12){
28379                     content[0].style = "width: " + this.labelWidth + 'px';
28380                 }
28381
28382                 if(this.labelWidth < 13 && this.labelmd == 0){
28383                     this.labelmd = this.labelWidth;
28384                 }
28385
28386                 if(this.labellg > 0){
28387                     content[0].cls += ' col-lg-' + this.labellg;
28388                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28389                 }
28390
28391                 if(this.labelmd > 0){
28392                     content[0].cls += ' col-md-' + this.labelmd;
28393                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28394                 }
28395
28396                 if(this.labelsm > 0){
28397                     content[0].cls += ' col-sm-' + this.labelsm;
28398                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28399                 }
28400
28401                 if(this.labelxs > 0){
28402                     content[0].cls += ' col-xs-' + this.labelxs;
28403                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28404                 }
28405                 
28406             }
28407         }
28408         
28409         var cfg = {
28410             tag : 'div',
28411             cls : 'row clearfix',
28412             cn : content
28413         };
28414         
28415         return cfg;
28416         
28417     },
28418     
28419     initEvents : function()
28420     {
28421         this.managerEl = this.el.select('.roo-document-manager', true).first();
28422         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28423         
28424         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28425         this.selectorEl.hide();
28426         
28427         if(this.multiple){
28428             this.selectorEl.attr('multiple', 'multiple');
28429         }
28430         
28431         this.selectorEl.on('change', this.onFileSelected, this);
28432         
28433         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28434         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28435         
28436         this.uploader.on('click', this.onUploaderClick, this);
28437         
28438         this.renderProgressDialog();
28439         
28440         var _this = this;
28441         
28442         window.addEventListener("resize", function() { _this.refresh(); } );
28443         
28444         this.fireEvent('initial', this);
28445     },
28446     
28447     renderProgressDialog : function()
28448     {
28449         var _this = this;
28450         
28451         this.progressDialog = new Roo.bootstrap.Modal({
28452             cls : 'roo-document-manager-progress-dialog',
28453             allow_close : false,
28454             title : '',
28455             buttons : [
28456                 {
28457                     name  :'cancel',
28458                     weight : 'danger',
28459                     html : 'Cancel'
28460                 }
28461             ], 
28462             listeners : { 
28463                 btnclick : function() {
28464                     _this.uploadCancel();
28465                     this.hide();
28466                 }
28467             }
28468         });
28469          
28470         this.progressDialog.render(Roo.get(document.body));
28471          
28472         this.progress = new Roo.bootstrap.Progress({
28473             cls : 'roo-document-manager-progress',
28474             active : true,
28475             striped : true
28476         });
28477         
28478         this.progress.render(this.progressDialog.getChildContainer());
28479         
28480         this.progressBar = new Roo.bootstrap.ProgressBar({
28481             cls : 'roo-document-manager-progress-bar',
28482             aria_valuenow : 0,
28483             aria_valuemin : 0,
28484             aria_valuemax : 12,
28485             panel : 'success'
28486         });
28487         
28488         this.progressBar.render(this.progress.getChildContainer());
28489     },
28490     
28491     onUploaderClick : function(e)
28492     {
28493         e.preventDefault();
28494      
28495         if(this.fireEvent('beforeselectfile', this) != false){
28496             this.selectorEl.dom.click();
28497         }
28498         
28499     },
28500     
28501     onFileSelected : function(e)
28502     {
28503         e.preventDefault();
28504         
28505         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28506             return;
28507         }
28508         
28509         Roo.each(this.selectorEl.dom.files, function(file){
28510             if(this.fireEvent('inspect', this, file) != false){
28511                 this.files.push(file);
28512             }
28513         }, this);
28514         
28515         this.queue();
28516         
28517     },
28518     
28519     queue : function()
28520     {
28521         this.selectorEl.dom.value = '';
28522         
28523         if(!this.files.length){
28524             return;
28525         }
28526         
28527         if(this.boxes > 0 && this.files.length > this.boxes){
28528             this.files = this.files.slice(0, this.boxes);
28529         }
28530         
28531         this.uploader.show();
28532         
28533         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28534             this.uploader.hide();
28535         }
28536         
28537         var _this = this;
28538         
28539         var files = [];
28540         
28541         var docs = [];
28542         
28543         Roo.each(this.files, function(file){
28544             
28545             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28546                 var f = this.renderPreview(file);
28547                 files.push(f);
28548                 return;
28549             }
28550             
28551             if(file.type.indexOf('image') != -1){
28552                 this.delegates.push(
28553                     (function(){
28554                         _this.process(file);
28555                     }).createDelegate(this)
28556                 );
28557         
28558                 return;
28559             }
28560             
28561             docs.push(
28562                 (function(){
28563                     _this.process(file);
28564                 }).createDelegate(this)
28565             );
28566             
28567         }, this);
28568         
28569         this.files = files;
28570         
28571         this.delegates = this.delegates.concat(docs);
28572         
28573         if(!this.delegates.length){
28574             this.refresh();
28575             return;
28576         }
28577         
28578         this.progressBar.aria_valuemax = this.delegates.length;
28579         
28580         this.arrange();
28581         
28582         return;
28583     },
28584     
28585     arrange : function()
28586     {
28587         if(!this.delegates.length){
28588             this.progressDialog.hide();
28589             this.refresh();
28590             return;
28591         }
28592         
28593         var delegate = this.delegates.shift();
28594         
28595         this.progressDialog.show();
28596         
28597         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28598         
28599         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28600         
28601         delegate();
28602     },
28603     
28604     refresh : function()
28605     {
28606         this.uploader.show();
28607         
28608         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28609             this.uploader.hide();
28610         }
28611         
28612         Roo.isTouch ? this.closable(false) : this.closable(true);
28613         
28614         this.fireEvent('refresh', this);
28615     },
28616     
28617     onRemove : function(e, el, o)
28618     {
28619         e.preventDefault();
28620         
28621         this.fireEvent('remove', this, o);
28622         
28623     },
28624     
28625     remove : function(o)
28626     {
28627         var files = [];
28628         
28629         Roo.each(this.files, function(file){
28630             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28631                 files.push(file);
28632                 return;
28633             }
28634
28635             o.target.remove();
28636
28637         }, this);
28638         
28639         this.files = files;
28640         
28641         this.refresh();
28642     },
28643     
28644     clear : function()
28645     {
28646         Roo.each(this.files, function(file){
28647             if(!file.target){
28648                 return;
28649             }
28650             
28651             file.target.remove();
28652
28653         }, this);
28654         
28655         this.files = [];
28656         
28657         this.refresh();
28658     },
28659     
28660     onClick : function(e, el, o)
28661     {
28662         e.preventDefault();
28663         
28664         this.fireEvent('click', this, o);
28665         
28666     },
28667     
28668     closable : function(closable)
28669     {
28670         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28671             
28672             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28673             
28674             if(closable){
28675                 el.show();
28676                 return;
28677             }
28678             
28679             el.hide();
28680             
28681         }, this);
28682     },
28683     
28684     xhrOnLoad : function(xhr)
28685     {
28686         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28687             el.remove();
28688         }, this);
28689         
28690         if (xhr.readyState !== 4) {
28691             this.arrange();
28692             this.fireEvent('exception', this, xhr);
28693             return;
28694         }
28695
28696         var response = Roo.decode(xhr.responseText);
28697         
28698         if(!response.success){
28699             this.arrange();
28700             this.fireEvent('exception', this, xhr);
28701             return;
28702         }
28703         
28704         var file = this.renderPreview(response.data);
28705         
28706         this.files.push(file);
28707         
28708         this.arrange();
28709         
28710         this.fireEvent('afterupload', this, xhr);
28711         
28712     },
28713     
28714     xhrOnError : function(xhr)
28715     {
28716         Roo.log('xhr on error');
28717         
28718         var response = Roo.decode(xhr.responseText);
28719           
28720         Roo.log(response);
28721         
28722         this.arrange();
28723     },
28724     
28725     process : function(file)
28726     {
28727         if(this.fireEvent('process', this, file) !== false){
28728             if(this.editable && file.type.indexOf('image') != -1){
28729                 this.fireEvent('edit', this, file);
28730                 return;
28731             }
28732
28733             this.uploadStart(file, false);
28734
28735             return;
28736         }
28737         
28738     },
28739     
28740     uploadStart : function(file, crop)
28741     {
28742         this.xhr = new XMLHttpRequest();
28743         
28744         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28745             this.arrange();
28746             return;
28747         }
28748         
28749         file.xhr = this.xhr;
28750             
28751         this.managerEl.createChild({
28752             tag : 'div',
28753             cls : 'roo-document-manager-loading',
28754             cn : [
28755                 {
28756                     tag : 'div',
28757                     tooltip : file.name,
28758                     cls : 'roo-document-manager-thumb',
28759                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28760                 }
28761             ]
28762
28763         });
28764
28765         this.xhr.open(this.method, this.url, true);
28766         
28767         var headers = {
28768             "Accept": "application/json",
28769             "Cache-Control": "no-cache",
28770             "X-Requested-With": "XMLHttpRequest"
28771         };
28772         
28773         for (var headerName in headers) {
28774             var headerValue = headers[headerName];
28775             if (headerValue) {
28776                 this.xhr.setRequestHeader(headerName, headerValue);
28777             }
28778         }
28779         
28780         var _this = this;
28781         
28782         this.xhr.onload = function()
28783         {
28784             _this.xhrOnLoad(_this.xhr);
28785         }
28786         
28787         this.xhr.onerror = function()
28788         {
28789             _this.xhrOnError(_this.xhr);
28790         }
28791         
28792         var formData = new FormData();
28793
28794         formData.append('returnHTML', 'NO');
28795         
28796         if(crop){
28797             formData.append('crop', crop);
28798         }
28799         
28800         formData.append(this.paramName, file, file.name);
28801         
28802         var options = {
28803             file : file, 
28804             manually : false
28805         };
28806         
28807         if(this.fireEvent('prepare', this, formData, options) != false){
28808             
28809             if(options.manually){
28810                 return;
28811             }
28812             
28813             this.xhr.send(formData);
28814             return;
28815         };
28816         
28817         this.uploadCancel();
28818     },
28819     
28820     uploadCancel : function()
28821     {
28822         if (this.xhr) {
28823             this.xhr.abort();
28824         }
28825         
28826         this.delegates = [];
28827         
28828         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28829             el.remove();
28830         }, this);
28831         
28832         this.arrange();
28833     },
28834     
28835     renderPreview : function(file)
28836     {
28837         if(typeof(file.target) != 'undefined' && file.target){
28838             return file;
28839         }
28840         
28841         var previewEl = this.managerEl.createChild({
28842             tag : 'div',
28843             cls : 'roo-document-manager-preview',
28844             cn : [
28845                 {
28846                     tag : 'div',
28847                     tooltip : file[this.toolTipName],
28848                     cls : 'roo-document-manager-thumb',
28849                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28850                 },
28851                 {
28852                     tag : 'button',
28853                     cls : 'close',
28854                     html : '<i class="fa fa-times-circle"></i>'
28855                 }
28856             ]
28857         });
28858
28859         var close = previewEl.select('button.close', true).first();
28860
28861         close.on('click', this.onRemove, this, file);
28862
28863         file.target = previewEl;
28864
28865         var image = previewEl.select('img', true).first();
28866         
28867         var _this = this;
28868         
28869         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28870         
28871         image.on('click', this.onClick, this, file);
28872         
28873         return file;
28874         
28875     },
28876     
28877     onPreviewLoad : function(file, image)
28878     {
28879         if(typeof(file.target) == 'undefined' || !file.target){
28880             return;
28881         }
28882         
28883         var width = image.dom.naturalWidth || image.dom.width;
28884         var height = image.dom.naturalHeight || image.dom.height;
28885         
28886         if(width > height){
28887             file.target.addClass('wide');
28888             return;
28889         }
28890         
28891         file.target.addClass('tall');
28892         return;
28893         
28894     },
28895     
28896     uploadFromSource : function(file, crop)
28897     {
28898         this.xhr = new XMLHttpRequest();
28899         
28900         this.managerEl.createChild({
28901             tag : 'div',
28902             cls : 'roo-document-manager-loading',
28903             cn : [
28904                 {
28905                     tag : 'div',
28906                     tooltip : file.name,
28907                     cls : 'roo-document-manager-thumb',
28908                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28909                 }
28910             ]
28911
28912         });
28913
28914         this.xhr.open(this.method, this.url, true);
28915         
28916         var headers = {
28917             "Accept": "application/json",
28918             "Cache-Control": "no-cache",
28919             "X-Requested-With": "XMLHttpRequest"
28920         };
28921         
28922         for (var headerName in headers) {
28923             var headerValue = headers[headerName];
28924             if (headerValue) {
28925                 this.xhr.setRequestHeader(headerName, headerValue);
28926             }
28927         }
28928         
28929         var _this = this;
28930         
28931         this.xhr.onload = function()
28932         {
28933             _this.xhrOnLoad(_this.xhr);
28934         }
28935         
28936         this.xhr.onerror = function()
28937         {
28938             _this.xhrOnError(_this.xhr);
28939         }
28940         
28941         var formData = new FormData();
28942
28943         formData.append('returnHTML', 'NO');
28944         
28945         formData.append('crop', crop);
28946         
28947         if(typeof(file.filename) != 'undefined'){
28948             formData.append('filename', file.filename);
28949         }
28950         
28951         if(typeof(file.mimetype) != 'undefined'){
28952             formData.append('mimetype', file.mimetype);
28953         }
28954         
28955         Roo.log(formData);
28956         
28957         if(this.fireEvent('prepare', this, formData) != false){
28958             this.xhr.send(formData);
28959         };
28960     }
28961 });
28962
28963 /*
28964 * Licence: LGPL
28965 */
28966
28967 /**
28968  * @class Roo.bootstrap.DocumentViewer
28969  * @extends Roo.bootstrap.Component
28970  * Bootstrap DocumentViewer class
28971  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28972  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28973  * 
28974  * @constructor
28975  * Create a new DocumentViewer
28976  * @param {Object} config The config object
28977  */
28978
28979 Roo.bootstrap.DocumentViewer = function(config){
28980     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28981     
28982     this.addEvents({
28983         /**
28984          * @event initial
28985          * Fire after initEvent
28986          * @param {Roo.bootstrap.DocumentViewer} this
28987          */
28988         "initial" : true,
28989         /**
28990          * @event click
28991          * Fire after click
28992          * @param {Roo.bootstrap.DocumentViewer} this
28993          */
28994         "click" : true,
28995         /**
28996          * @event download
28997          * Fire after download button
28998          * @param {Roo.bootstrap.DocumentViewer} this
28999          */
29000         "download" : true,
29001         /**
29002          * @event trash
29003          * Fire after trash button
29004          * @param {Roo.bootstrap.DocumentViewer} this
29005          */
29006         "trash" : true
29007         
29008     });
29009 };
29010
29011 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29012     
29013     showDownload : true,
29014     
29015     showTrash : true,
29016     
29017     getAutoCreate : function()
29018     {
29019         var cfg = {
29020             tag : 'div',
29021             cls : 'roo-document-viewer',
29022             cn : [
29023                 {
29024                     tag : 'div',
29025                     cls : 'roo-document-viewer-body',
29026                     cn : [
29027                         {
29028                             tag : 'div',
29029                             cls : 'roo-document-viewer-thumb',
29030                             cn : [
29031                                 {
29032                                     tag : 'img',
29033                                     cls : 'roo-document-viewer-image'
29034                                 }
29035                             ]
29036                         }
29037                     ]
29038                 },
29039                 {
29040                     tag : 'div',
29041                     cls : 'roo-document-viewer-footer',
29042                     cn : {
29043                         tag : 'div',
29044                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29045                         cn : [
29046                             {
29047                                 tag : 'div',
29048                                 cls : 'btn-group roo-document-viewer-download',
29049                                 cn : [
29050                                     {
29051                                         tag : 'button',
29052                                         cls : 'btn btn-default',
29053                                         html : '<i class="fa fa-download"></i>'
29054                                     }
29055                                 ]
29056                             },
29057                             {
29058                                 tag : 'div',
29059                                 cls : 'btn-group roo-document-viewer-trash',
29060                                 cn : [
29061                                     {
29062                                         tag : 'button',
29063                                         cls : 'btn btn-default',
29064                                         html : '<i class="fa fa-trash"></i>'
29065                                     }
29066                                 ]
29067                             }
29068                         ]
29069                     }
29070                 }
29071             ]
29072         };
29073         
29074         return cfg;
29075     },
29076     
29077     initEvents : function()
29078     {
29079         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29080         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29081         
29082         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29083         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29084         
29085         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29086         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29087         
29088         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29089         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29090         
29091         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29092         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29093         
29094         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29095         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29096         
29097         this.bodyEl.on('click', this.onClick, this);
29098         this.downloadBtn.on('click', this.onDownload, this);
29099         this.trashBtn.on('click', this.onTrash, this);
29100         
29101         this.downloadBtn.hide();
29102         this.trashBtn.hide();
29103         
29104         if(this.showDownload){
29105             this.downloadBtn.show();
29106         }
29107         
29108         if(this.showTrash){
29109             this.trashBtn.show();
29110         }
29111         
29112         if(!this.showDownload && !this.showTrash) {
29113             this.footerEl.hide();
29114         }
29115         
29116     },
29117     
29118     initial : function()
29119     {
29120         this.fireEvent('initial', this);
29121         
29122     },
29123     
29124     onClick : function(e)
29125     {
29126         e.preventDefault();
29127         
29128         this.fireEvent('click', this);
29129     },
29130     
29131     onDownload : function(e)
29132     {
29133         e.preventDefault();
29134         
29135         this.fireEvent('download', this);
29136     },
29137     
29138     onTrash : function(e)
29139     {
29140         e.preventDefault();
29141         
29142         this.fireEvent('trash', this);
29143     }
29144     
29145 });
29146 /*
29147  * - LGPL
29148  *
29149  * nav progress bar
29150  * 
29151  */
29152
29153 /**
29154  * @class Roo.bootstrap.NavProgressBar
29155  * @extends Roo.bootstrap.Component
29156  * Bootstrap NavProgressBar class
29157  * 
29158  * @constructor
29159  * Create a new nav progress bar
29160  * @param {Object} config The config object
29161  */
29162
29163 Roo.bootstrap.NavProgressBar = function(config){
29164     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29165
29166     this.bullets = this.bullets || [];
29167    
29168 //    Roo.bootstrap.NavProgressBar.register(this);
29169      this.addEvents({
29170         /**
29171              * @event changed
29172              * Fires when the active item changes
29173              * @param {Roo.bootstrap.NavProgressBar} this
29174              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29175              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29176          */
29177         'changed': true
29178      });
29179     
29180 };
29181
29182 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29183     
29184     bullets : [],
29185     barItems : [],
29186     
29187     getAutoCreate : function()
29188     {
29189         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29190         
29191         cfg = {
29192             tag : 'div',
29193             cls : 'roo-navigation-bar-group',
29194             cn : [
29195                 {
29196                     tag : 'div',
29197                     cls : 'roo-navigation-top-bar'
29198                 },
29199                 {
29200                     tag : 'div',
29201                     cls : 'roo-navigation-bullets-bar',
29202                     cn : [
29203                         {
29204                             tag : 'ul',
29205                             cls : 'roo-navigation-bar'
29206                         }
29207                     ]
29208                 },
29209                 
29210                 {
29211                     tag : 'div',
29212                     cls : 'roo-navigation-bottom-bar'
29213                 }
29214             ]
29215             
29216         };
29217         
29218         return cfg;
29219         
29220     },
29221     
29222     initEvents: function() 
29223     {
29224         
29225     },
29226     
29227     onRender : function(ct, position) 
29228     {
29229         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29230         
29231         if(this.bullets.length){
29232             Roo.each(this.bullets, function(b){
29233                this.addItem(b);
29234             }, this);
29235         }
29236         
29237         this.format();
29238         
29239     },
29240     
29241     addItem : function(cfg)
29242     {
29243         var item = new Roo.bootstrap.NavProgressItem(cfg);
29244         
29245         item.parentId = this.id;
29246         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29247         
29248         if(cfg.html){
29249             var top = new Roo.bootstrap.Element({
29250                 tag : 'div',
29251                 cls : 'roo-navigation-bar-text'
29252             });
29253             
29254             var bottom = new Roo.bootstrap.Element({
29255                 tag : 'div',
29256                 cls : 'roo-navigation-bar-text'
29257             });
29258             
29259             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29260             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29261             
29262             var topText = new Roo.bootstrap.Element({
29263                 tag : 'span',
29264                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29265             });
29266             
29267             var bottomText = new Roo.bootstrap.Element({
29268                 tag : 'span',
29269                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29270             });
29271             
29272             topText.onRender(top.el, null);
29273             bottomText.onRender(bottom.el, null);
29274             
29275             item.topEl = top;
29276             item.bottomEl = bottom;
29277         }
29278         
29279         this.barItems.push(item);
29280         
29281         return item;
29282     },
29283     
29284     getActive : function()
29285     {
29286         var active = false;
29287         
29288         Roo.each(this.barItems, function(v){
29289             
29290             if (!v.isActive()) {
29291                 return;
29292             }
29293             
29294             active = v;
29295             return false;
29296             
29297         });
29298         
29299         return active;
29300     },
29301     
29302     setActiveItem : function(item)
29303     {
29304         var prev = false;
29305         
29306         Roo.each(this.barItems, function(v){
29307             if (v.rid == item.rid) {
29308                 return ;
29309             }
29310             
29311             if (v.isActive()) {
29312                 v.setActive(false);
29313                 prev = v;
29314             }
29315         });
29316
29317         item.setActive(true);
29318         
29319         this.fireEvent('changed', this, item, prev);
29320     },
29321     
29322     getBarItem: function(rid)
29323     {
29324         var ret = false;
29325         
29326         Roo.each(this.barItems, function(e) {
29327             if (e.rid != rid) {
29328                 return;
29329             }
29330             
29331             ret =  e;
29332             return false;
29333         });
29334         
29335         return ret;
29336     },
29337     
29338     indexOfItem : function(item)
29339     {
29340         var index = false;
29341         
29342         Roo.each(this.barItems, function(v, i){
29343             
29344             if (v.rid != item.rid) {
29345                 return;
29346             }
29347             
29348             index = i;
29349             return false
29350         });
29351         
29352         return index;
29353     },
29354     
29355     setActiveNext : function()
29356     {
29357         var i = this.indexOfItem(this.getActive());
29358         
29359         if (i > this.barItems.length) {
29360             return;
29361         }
29362         
29363         this.setActiveItem(this.barItems[i+1]);
29364     },
29365     
29366     setActivePrev : function()
29367     {
29368         var i = this.indexOfItem(this.getActive());
29369         
29370         if (i  < 1) {
29371             return;
29372         }
29373         
29374         this.setActiveItem(this.barItems[i-1]);
29375     },
29376     
29377     format : function()
29378     {
29379         if(!this.barItems.length){
29380             return;
29381         }
29382      
29383         var width = 100 / this.barItems.length;
29384         
29385         Roo.each(this.barItems, function(i){
29386             i.el.setStyle('width', width + '%');
29387             i.topEl.el.setStyle('width', width + '%');
29388             i.bottomEl.el.setStyle('width', width + '%');
29389         }, this);
29390         
29391     }
29392     
29393 });
29394 /*
29395  * - LGPL
29396  *
29397  * Nav Progress Item
29398  * 
29399  */
29400
29401 /**
29402  * @class Roo.bootstrap.NavProgressItem
29403  * @extends Roo.bootstrap.Component
29404  * Bootstrap NavProgressItem class
29405  * @cfg {String} rid the reference id
29406  * @cfg {Boolean} active (true|false) Is item active default false
29407  * @cfg {Boolean} disabled (true|false) Is item active default false
29408  * @cfg {String} html
29409  * @cfg {String} position (top|bottom) text position default bottom
29410  * @cfg {String} icon show icon instead of number
29411  * 
29412  * @constructor
29413  * Create a new NavProgressItem
29414  * @param {Object} config The config object
29415  */
29416 Roo.bootstrap.NavProgressItem = function(config){
29417     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29418     this.addEvents({
29419         // raw events
29420         /**
29421          * @event click
29422          * The raw click event for the entire grid.
29423          * @param {Roo.bootstrap.NavProgressItem} this
29424          * @param {Roo.EventObject} e
29425          */
29426         "click" : true
29427     });
29428    
29429 };
29430
29431 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29432     
29433     rid : '',
29434     active : false,
29435     disabled : false,
29436     html : '',
29437     position : 'bottom',
29438     icon : false,
29439     
29440     getAutoCreate : function()
29441     {
29442         var iconCls = 'roo-navigation-bar-item-icon';
29443         
29444         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29445         
29446         var cfg = {
29447             tag: 'li',
29448             cls: 'roo-navigation-bar-item',
29449             cn : [
29450                 {
29451                     tag : 'i',
29452                     cls : iconCls
29453                 }
29454             ]
29455         };
29456         
29457         if(this.active){
29458             cfg.cls += ' active';
29459         }
29460         if(this.disabled){
29461             cfg.cls += ' disabled';
29462         }
29463         
29464         return cfg;
29465     },
29466     
29467     disable : function()
29468     {
29469         this.setDisabled(true);
29470     },
29471     
29472     enable : function()
29473     {
29474         this.setDisabled(false);
29475     },
29476     
29477     initEvents: function() 
29478     {
29479         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29480         
29481         this.iconEl.on('click', this.onClick, this);
29482     },
29483     
29484     onClick : function(e)
29485     {
29486         e.preventDefault();
29487         
29488         if(this.disabled){
29489             return;
29490         }
29491         
29492         if(this.fireEvent('click', this, e) === false){
29493             return;
29494         };
29495         
29496         this.parent().setActiveItem(this);
29497     },
29498     
29499     isActive: function () 
29500     {
29501         return this.active;
29502     },
29503     
29504     setActive : function(state)
29505     {
29506         if(this.active == state){
29507             return;
29508         }
29509         
29510         this.active = state;
29511         
29512         if (state) {
29513             this.el.addClass('active');
29514             return;
29515         }
29516         
29517         this.el.removeClass('active');
29518         
29519         return;
29520     },
29521     
29522     setDisabled : function(state)
29523     {
29524         if(this.disabled == state){
29525             return;
29526         }
29527         
29528         this.disabled = state;
29529         
29530         if (state) {
29531             this.el.addClass('disabled');
29532             return;
29533         }
29534         
29535         this.el.removeClass('disabled');
29536     },
29537     
29538     tooltipEl : function()
29539     {
29540         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29541     }
29542 });
29543  
29544
29545  /*
29546  * - LGPL
29547  *
29548  * FieldLabel
29549  * 
29550  */
29551
29552 /**
29553  * @class Roo.bootstrap.FieldLabel
29554  * @extends Roo.bootstrap.Component
29555  * Bootstrap FieldLabel class
29556  * @cfg {String} html contents of the element
29557  * @cfg {String} tag tag of the element default label
29558  * @cfg {String} cls class of the element
29559  * @cfg {String} target label target 
29560  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29561  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29562  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29563  * @cfg {String} iconTooltip default "This field is required"
29564  * 
29565  * @constructor
29566  * Create a new FieldLabel
29567  * @param {Object} config The config object
29568  */
29569
29570 Roo.bootstrap.FieldLabel = function(config){
29571     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29572     
29573     this.addEvents({
29574             /**
29575              * @event invalid
29576              * Fires after the field has been marked as invalid.
29577              * @param {Roo.form.FieldLabel} this
29578              * @param {String} msg The validation message
29579              */
29580             invalid : true,
29581             /**
29582              * @event valid
29583              * Fires after the field has been validated with no errors.
29584              * @param {Roo.form.FieldLabel} this
29585              */
29586             valid : true
29587         });
29588 };
29589
29590 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29591     
29592     tag: 'label',
29593     cls: '',
29594     html: '',
29595     target: '',
29596     allowBlank : true,
29597     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29598     validClass : 'text-success fa fa-lg fa-check',
29599     iconTooltip : 'This field is required',
29600     
29601     getAutoCreate : function(){
29602         
29603         var cfg = {
29604             tag : this.tag,
29605             cls : 'roo-bootstrap-field-label ' + this.cls,
29606             for : this.target,
29607             cn : [
29608                 {
29609                     tag : 'i',
29610                     cls : '',
29611                     tooltip : this.iconTooltip
29612                 },
29613                 {
29614                     tag : 'span',
29615                     html : this.html
29616                 }
29617             ] 
29618         };
29619         
29620         return cfg;
29621     },
29622     
29623     initEvents: function() 
29624     {
29625         Roo.bootstrap.Element.superclass.initEvents.call(this);
29626         
29627         this.iconEl = this.el.select('i', true).first();
29628         
29629         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29630         
29631         Roo.bootstrap.FieldLabel.register(this);
29632     },
29633     
29634     /**
29635      * Mark this field as valid
29636      */
29637     markValid : function()
29638     {
29639         this.iconEl.show();
29640         
29641         this.iconEl.removeClass(this.invalidClass);
29642         
29643         this.iconEl.addClass(this.validClass);
29644         
29645         this.fireEvent('valid', this);
29646     },
29647     
29648     /**
29649      * Mark this field as invalid
29650      * @param {String} msg The validation message
29651      */
29652     markInvalid : function(msg)
29653     {
29654         this.iconEl.show();
29655         
29656         this.iconEl.removeClass(this.validClass);
29657         
29658         this.iconEl.addClass(this.invalidClass);
29659         
29660         this.fireEvent('invalid', this, msg);
29661     }
29662     
29663    
29664 });
29665
29666 Roo.apply(Roo.bootstrap.FieldLabel, {
29667     
29668     groups: {},
29669     
29670      /**
29671     * register a FieldLabel Group
29672     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29673     */
29674     register : function(label)
29675     {
29676         if(this.groups.hasOwnProperty(label.target)){
29677             return;
29678         }
29679      
29680         this.groups[label.target] = label;
29681         
29682     },
29683     /**
29684     * fetch a FieldLabel Group based on the target
29685     * @param {string} target
29686     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29687     */
29688     get: function(target) {
29689         if (typeof(this.groups[target]) == 'undefined') {
29690             return false;
29691         }
29692         
29693         return this.groups[target] ;
29694     }
29695 });
29696
29697  
29698
29699  /*
29700  * - LGPL
29701  *
29702  * page DateSplitField.
29703  * 
29704  */
29705
29706
29707 /**
29708  * @class Roo.bootstrap.DateSplitField
29709  * @extends Roo.bootstrap.Component
29710  * Bootstrap DateSplitField class
29711  * @cfg {string} fieldLabel - the label associated
29712  * @cfg {Number} labelWidth set the width of label (0-12)
29713  * @cfg {String} labelAlign (top|left)
29714  * @cfg {Boolean} dayAllowBlank (true|false) default false
29715  * @cfg {Boolean} monthAllowBlank (true|false) default false
29716  * @cfg {Boolean} yearAllowBlank (true|false) default false
29717  * @cfg {string} dayPlaceholder 
29718  * @cfg {string} monthPlaceholder
29719  * @cfg {string} yearPlaceholder
29720  * @cfg {string} dayFormat default 'd'
29721  * @cfg {string} monthFormat default 'm'
29722  * @cfg {string} yearFormat default 'Y'
29723  * @cfg {Number} labellg set the width of label (1-12)
29724  * @cfg {Number} labelmd set the width of label (1-12)
29725  * @cfg {Number} labelsm set the width of label (1-12)
29726  * @cfg {Number} labelxs set the width of label (1-12)
29727
29728  *     
29729  * @constructor
29730  * Create a new DateSplitField
29731  * @param {Object} config The config object
29732  */
29733
29734 Roo.bootstrap.DateSplitField = function(config){
29735     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29736     
29737     this.addEvents({
29738         // raw events
29739          /**
29740          * @event years
29741          * getting the data of years
29742          * @param {Roo.bootstrap.DateSplitField} this
29743          * @param {Object} years
29744          */
29745         "years" : true,
29746         /**
29747          * @event days
29748          * getting the data of days
29749          * @param {Roo.bootstrap.DateSplitField} this
29750          * @param {Object} days
29751          */
29752         "days" : true,
29753         /**
29754          * @event invalid
29755          * Fires after the field has been marked as invalid.
29756          * @param {Roo.form.Field} this
29757          * @param {String} msg The validation message
29758          */
29759         invalid : true,
29760        /**
29761          * @event valid
29762          * Fires after the field has been validated with no errors.
29763          * @param {Roo.form.Field} this
29764          */
29765         valid : true
29766     });
29767 };
29768
29769 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29770     
29771     fieldLabel : '',
29772     labelAlign : 'top',
29773     labelWidth : 3,
29774     dayAllowBlank : false,
29775     monthAllowBlank : false,
29776     yearAllowBlank : false,
29777     dayPlaceholder : '',
29778     monthPlaceholder : '',
29779     yearPlaceholder : '',
29780     dayFormat : 'd',
29781     monthFormat : 'm',
29782     yearFormat : 'Y',
29783     isFormField : true,
29784     labellg : 0,
29785     labelmd : 0,
29786     labelsm : 0,
29787     labelxs : 0,
29788     
29789     getAutoCreate : function()
29790     {
29791         var cfg = {
29792             tag : 'div',
29793             cls : 'row roo-date-split-field-group',
29794             cn : [
29795                 {
29796                     tag : 'input',
29797                     type : 'hidden',
29798                     cls : 'form-hidden-field roo-date-split-field-group-value',
29799                     name : this.name
29800                 }
29801             ]
29802         };
29803         
29804         var labelCls = 'col-md-12';
29805         var contentCls = 'col-md-4';
29806         
29807         if(this.fieldLabel){
29808             
29809             var label = {
29810                 tag : 'div',
29811                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29812                 cn : [
29813                     {
29814                         tag : 'label',
29815                         html : this.fieldLabel
29816                     }
29817                 ]
29818             };
29819             
29820             if(this.labelAlign == 'left'){
29821             
29822                 if(this.labelWidth > 12){
29823                     label.style = "width: " + this.labelWidth + 'px';
29824                 }
29825
29826                 if(this.labelWidth < 13 && this.labelmd == 0){
29827                     this.labelmd = this.labelWidth;
29828                 }
29829
29830                 if(this.labellg > 0){
29831                     labelCls = ' col-lg-' + this.labellg;
29832                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29833                 }
29834
29835                 if(this.labelmd > 0){
29836                     labelCls = ' col-md-' + this.labelmd;
29837                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29838                 }
29839
29840                 if(this.labelsm > 0){
29841                     labelCls = ' col-sm-' + this.labelsm;
29842                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29843                 }
29844
29845                 if(this.labelxs > 0){
29846                     labelCls = ' col-xs-' + this.labelxs;
29847                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29848                 }
29849             }
29850             
29851             label.cls += ' ' + labelCls;
29852             
29853             cfg.cn.push(label);
29854         }
29855         
29856         Roo.each(['day', 'month', 'year'], function(t){
29857             cfg.cn.push({
29858                 tag : 'div',
29859                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29860             });
29861         }, this);
29862         
29863         return cfg;
29864     },
29865     
29866     inputEl: function ()
29867     {
29868         return this.el.select('.roo-date-split-field-group-value', true).first();
29869     },
29870     
29871     onRender : function(ct, position) 
29872     {
29873         var _this = this;
29874         
29875         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29876         
29877         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29878         
29879         this.dayField = new Roo.bootstrap.ComboBox({
29880             allowBlank : this.dayAllowBlank,
29881             alwaysQuery : true,
29882             displayField : 'value',
29883             editable : false,
29884             fieldLabel : '',
29885             forceSelection : true,
29886             mode : 'local',
29887             placeholder : this.dayPlaceholder,
29888             selectOnFocus : true,
29889             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29890             triggerAction : 'all',
29891             typeAhead : true,
29892             valueField : 'value',
29893             store : new Roo.data.SimpleStore({
29894                 data : (function() {    
29895                     var days = [];
29896                     _this.fireEvent('days', _this, days);
29897                     return days;
29898                 })(),
29899                 fields : [ 'value' ]
29900             }),
29901             listeners : {
29902                 select : function (_self, record, index)
29903                 {
29904                     _this.setValue(_this.getValue());
29905                 }
29906             }
29907         });
29908
29909         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29910         
29911         this.monthField = new Roo.bootstrap.MonthField({
29912             after : '<i class=\"fa fa-calendar\"></i>',
29913             allowBlank : this.monthAllowBlank,
29914             placeholder : this.monthPlaceholder,
29915             readOnly : true,
29916             listeners : {
29917                 render : function (_self)
29918                 {
29919                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29920                         e.preventDefault();
29921                         _self.focus();
29922                     });
29923                 },
29924                 select : function (_self, oldvalue, newvalue)
29925                 {
29926                     _this.setValue(_this.getValue());
29927                 }
29928             }
29929         });
29930         
29931         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29932         
29933         this.yearField = new Roo.bootstrap.ComboBox({
29934             allowBlank : this.yearAllowBlank,
29935             alwaysQuery : true,
29936             displayField : 'value',
29937             editable : false,
29938             fieldLabel : '',
29939             forceSelection : true,
29940             mode : 'local',
29941             placeholder : this.yearPlaceholder,
29942             selectOnFocus : true,
29943             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29944             triggerAction : 'all',
29945             typeAhead : true,
29946             valueField : 'value',
29947             store : new Roo.data.SimpleStore({
29948                 data : (function() {
29949                     var years = [];
29950                     _this.fireEvent('years', _this, years);
29951                     return years;
29952                 })(),
29953                 fields : [ 'value' ]
29954             }),
29955             listeners : {
29956                 select : function (_self, record, index)
29957                 {
29958                     _this.setValue(_this.getValue());
29959                 }
29960             }
29961         });
29962
29963         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29964     },
29965     
29966     setValue : function(v, format)
29967     {
29968         this.inputEl.dom.value = v;
29969         
29970         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29971         
29972         var d = Date.parseDate(v, f);
29973         
29974         if(!d){
29975             this.validate();
29976             return;
29977         }
29978         
29979         this.setDay(d.format(this.dayFormat));
29980         this.setMonth(d.format(this.monthFormat));
29981         this.setYear(d.format(this.yearFormat));
29982         
29983         this.validate();
29984         
29985         return;
29986     },
29987     
29988     setDay : function(v)
29989     {
29990         this.dayField.setValue(v);
29991         this.inputEl.dom.value = this.getValue();
29992         this.validate();
29993         return;
29994     },
29995     
29996     setMonth : function(v)
29997     {
29998         this.monthField.setValue(v, true);
29999         this.inputEl.dom.value = this.getValue();
30000         this.validate();
30001         return;
30002     },
30003     
30004     setYear : function(v)
30005     {
30006         this.yearField.setValue(v);
30007         this.inputEl.dom.value = this.getValue();
30008         this.validate();
30009         return;
30010     },
30011     
30012     getDay : function()
30013     {
30014         return this.dayField.getValue();
30015     },
30016     
30017     getMonth : function()
30018     {
30019         return this.monthField.getValue();
30020     },
30021     
30022     getYear : function()
30023     {
30024         return this.yearField.getValue();
30025     },
30026     
30027     getValue : function()
30028     {
30029         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30030         
30031         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30032         
30033         return date;
30034     },
30035     
30036     reset : function()
30037     {
30038         this.setDay('');
30039         this.setMonth('');
30040         this.setYear('');
30041         this.inputEl.dom.value = '';
30042         this.validate();
30043         return;
30044     },
30045     
30046     validate : function()
30047     {
30048         var d = this.dayField.validate();
30049         var m = this.monthField.validate();
30050         var y = this.yearField.validate();
30051         
30052         var valid = true;
30053         
30054         if(
30055                 (!this.dayAllowBlank && !d) ||
30056                 (!this.monthAllowBlank && !m) ||
30057                 (!this.yearAllowBlank && !y)
30058         ){
30059             valid = false;
30060         }
30061         
30062         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30063             return valid;
30064         }
30065         
30066         if(valid){
30067             this.markValid();
30068             return valid;
30069         }
30070         
30071         this.markInvalid();
30072         
30073         return valid;
30074     },
30075     
30076     markValid : function()
30077     {
30078         
30079         var label = this.el.select('label', true).first();
30080         var icon = this.el.select('i.fa-star', true).first();
30081
30082         if(label && icon){
30083             icon.remove();
30084         }
30085         
30086         this.fireEvent('valid', this);
30087     },
30088     
30089      /**
30090      * Mark this field as invalid
30091      * @param {String} msg The validation message
30092      */
30093     markInvalid : function(msg)
30094     {
30095         
30096         var label = this.el.select('label', true).first();
30097         var icon = this.el.select('i.fa-star', true).first();
30098
30099         if(label && !icon){
30100             this.el.select('.roo-date-split-field-label', true).createChild({
30101                 tag : 'i',
30102                 cls : 'text-danger fa fa-lg fa-star',
30103                 tooltip : 'This field is required',
30104                 style : 'margin-right:5px;'
30105             }, label, true);
30106         }
30107         
30108         this.fireEvent('invalid', this, msg);
30109     },
30110     
30111     clearInvalid : function()
30112     {
30113         var label = this.el.select('label', true).first();
30114         var icon = this.el.select('i.fa-star', true).first();
30115
30116         if(label && icon){
30117             icon.remove();
30118         }
30119         
30120         this.fireEvent('valid', this);
30121     },
30122     
30123     getName: function()
30124     {
30125         return this.name;
30126     }
30127     
30128 });
30129
30130  /**
30131  *
30132  * This is based on 
30133  * http://masonry.desandro.com
30134  *
30135  * The idea is to render all the bricks based on vertical width...
30136  *
30137  * The original code extends 'outlayer' - we might need to use that....
30138  * 
30139  */
30140
30141
30142 /**
30143  * @class Roo.bootstrap.LayoutMasonry
30144  * @extends Roo.bootstrap.Component
30145  * Bootstrap Layout Masonry class
30146  * 
30147  * @constructor
30148  * Create a new Element
30149  * @param {Object} config The config object
30150  */
30151
30152 Roo.bootstrap.LayoutMasonry = function(config){
30153     
30154     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30155     
30156     this.bricks = [];
30157     
30158     Roo.bootstrap.LayoutMasonry.register(this);
30159     
30160     this.addEvents({
30161         // raw events
30162         /**
30163          * @event layout
30164          * Fire after layout the items
30165          * @param {Roo.bootstrap.LayoutMasonry} this
30166          * @param {Roo.EventObject} e
30167          */
30168         "layout" : true
30169     });
30170     
30171 };
30172
30173 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30174     
30175     /**
30176      * @cfg {Boolean} isLayoutInstant = no animation?
30177      */   
30178     isLayoutInstant : false, // needed?
30179    
30180     /**
30181      * @cfg {Number} boxWidth  width of the columns
30182      */   
30183     boxWidth : 450,
30184     
30185       /**
30186      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30187      */   
30188     boxHeight : 0,
30189     
30190     /**
30191      * @cfg {Number} padWidth padding below box..
30192      */   
30193     padWidth : 10, 
30194     
30195     /**
30196      * @cfg {Number} gutter gutter width..
30197      */   
30198     gutter : 10,
30199     
30200      /**
30201      * @cfg {Number} maxCols maximum number of columns
30202      */   
30203     
30204     maxCols: 0,
30205     
30206     /**
30207      * @cfg {Boolean} isAutoInitial defalut true
30208      */   
30209     isAutoInitial : true, 
30210     
30211     containerWidth: 0,
30212     
30213     /**
30214      * @cfg {Boolean} isHorizontal defalut false
30215      */   
30216     isHorizontal : false, 
30217
30218     currentSize : null,
30219     
30220     tag: 'div',
30221     
30222     cls: '',
30223     
30224     bricks: null, //CompositeElement
30225     
30226     cols : 1,
30227     
30228     _isLayoutInited : false,
30229     
30230 //    isAlternative : false, // only use for vertical layout...
30231     
30232     /**
30233      * @cfg {Number} alternativePadWidth padding below box..
30234      */   
30235     alternativePadWidth : 50,
30236     
30237     selectedBrick : [],
30238     
30239     getAutoCreate : function(){
30240         
30241         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30242         
30243         var cfg = {
30244             tag: this.tag,
30245             cls: 'blog-masonary-wrapper ' + this.cls,
30246             cn : {
30247                 cls : 'mas-boxes masonary'
30248             }
30249         };
30250         
30251         return cfg;
30252     },
30253     
30254     getChildContainer: function( )
30255     {
30256         if (this.boxesEl) {
30257             return this.boxesEl;
30258         }
30259         
30260         this.boxesEl = this.el.select('.mas-boxes').first();
30261         
30262         return this.boxesEl;
30263     },
30264     
30265     
30266     initEvents : function()
30267     {
30268         var _this = this;
30269         
30270         if(this.isAutoInitial){
30271             Roo.log('hook children rendered');
30272             this.on('childrenrendered', function() {
30273                 Roo.log('children rendered');
30274                 _this.initial();
30275             } ,this);
30276         }
30277     },
30278     
30279     initial : function()
30280     {
30281         this.selectedBrick = [];
30282         
30283         this.currentSize = this.el.getBox(true);
30284         
30285         Roo.EventManager.onWindowResize(this.resize, this); 
30286
30287         if(!this.isAutoInitial){
30288             this.layout();
30289             return;
30290         }
30291         
30292         this.layout();
30293         
30294         return;
30295         //this.layout.defer(500,this);
30296         
30297     },
30298     
30299     resize : function()
30300     {
30301         var cs = this.el.getBox(true);
30302         
30303         if (
30304                 this.currentSize.width == cs.width && 
30305                 this.currentSize.x == cs.x && 
30306                 this.currentSize.height == cs.height && 
30307                 this.currentSize.y == cs.y 
30308         ) {
30309             Roo.log("no change in with or X or Y");
30310             return;
30311         }
30312         
30313         this.currentSize = cs;
30314         
30315         this.layout();
30316         
30317     },
30318     
30319     layout : function()
30320     {   
30321         this._resetLayout();
30322         
30323         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30324         
30325         this.layoutItems( isInstant );
30326       
30327         this._isLayoutInited = true;
30328         
30329         this.fireEvent('layout', this);
30330         
30331     },
30332     
30333     _resetLayout : function()
30334     {
30335         if(this.isHorizontal){
30336             this.horizontalMeasureColumns();
30337             return;
30338         }
30339         
30340         this.verticalMeasureColumns();
30341         
30342     },
30343     
30344     verticalMeasureColumns : function()
30345     {
30346         this.getContainerWidth();
30347         
30348 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30349 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30350 //            return;
30351 //        }
30352         
30353         var boxWidth = this.boxWidth + this.padWidth;
30354         
30355         if(this.containerWidth < this.boxWidth){
30356             boxWidth = this.containerWidth
30357         }
30358         
30359         var containerWidth = this.containerWidth;
30360         
30361         var cols = Math.floor(containerWidth / boxWidth);
30362         
30363         this.cols = Math.max( cols, 1 );
30364         
30365         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30366         
30367         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30368         
30369         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30370         
30371         this.colWidth = boxWidth + avail - this.padWidth;
30372         
30373         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30374         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30375     },
30376     
30377     horizontalMeasureColumns : function()
30378     {
30379         this.getContainerWidth();
30380         
30381         var boxWidth = this.boxWidth;
30382         
30383         if(this.containerWidth < boxWidth){
30384             boxWidth = this.containerWidth;
30385         }
30386         
30387         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30388         
30389         this.el.setHeight(boxWidth);
30390         
30391     },
30392     
30393     getContainerWidth : function()
30394     {
30395         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30396     },
30397     
30398     layoutItems : function( isInstant )
30399     {
30400         Roo.log(this.bricks);
30401         
30402         var items = Roo.apply([], this.bricks);
30403         
30404         if(this.isHorizontal){
30405             this._horizontalLayoutItems( items , isInstant );
30406             return;
30407         }
30408         
30409 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30410 //            this._verticalAlternativeLayoutItems( items , isInstant );
30411 //            return;
30412 //        }
30413         
30414         this._verticalLayoutItems( items , isInstant );
30415         
30416     },
30417     
30418     _verticalLayoutItems : function ( items , isInstant)
30419     {
30420         if ( !items || !items.length ) {
30421             return;
30422         }
30423         
30424         var standard = [
30425             ['xs', 'xs', 'xs', 'tall'],
30426             ['xs', 'xs', 'tall'],
30427             ['xs', 'xs', 'sm'],
30428             ['xs', 'xs', 'xs'],
30429             ['xs', 'tall'],
30430             ['xs', 'sm'],
30431             ['xs', 'xs'],
30432             ['xs'],
30433             
30434             ['sm', 'xs', 'xs'],
30435             ['sm', 'xs'],
30436             ['sm'],
30437             
30438             ['tall', 'xs', 'xs', 'xs'],
30439             ['tall', 'xs', 'xs'],
30440             ['tall', 'xs'],
30441             ['tall']
30442             
30443         ];
30444         
30445         var queue = [];
30446         
30447         var boxes = [];
30448         
30449         var box = [];
30450         
30451         Roo.each(items, function(item, k){
30452             
30453             switch (item.size) {
30454                 // these layouts take up a full box,
30455                 case 'md' :
30456                 case 'md-left' :
30457                 case 'md-right' :
30458                 case 'wide' :
30459                     
30460                     if(box.length){
30461                         boxes.push(box);
30462                         box = [];
30463                     }
30464                     
30465                     boxes.push([item]);
30466                     
30467                     break;
30468                     
30469                 case 'xs' :
30470                 case 'sm' :
30471                 case 'tall' :
30472                     
30473                     box.push(item);
30474                     
30475                     break;
30476                 default :
30477                     break;
30478                     
30479             }
30480             
30481         }, this);
30482         
30483         if(box.length){
30484             boxes.push(box);
30485             box = [];
30486         }
30487         
30488         var filterPattern = function(box, length)
30489         {
30490             if(!box.length){
30491                 return;
30492             }
30493             
30494             var match = false;
30495             
30496             var pattern = box.slice(0, length);
30497             
30498             var format = [];
30499             
30500             Roo.each(pattern, function(i){
30501                 format.push(i.size);
30502             }, this);
30503             
30504             Roo.each(standard, function(s){
30505                 
30506                 if(String(s) != String(format)){
30507                     return;
30508                 }
30509                 
30510                 match = true;
30511                 return false;
30512                 
30513             }, this);
30514             
30515             if(!match && length == 1){
30516                 return;
30517             }
30518             
30519             if(!match){
30520                 filterPattern(box, length - 1);
30521                 return;
30522             }
30523                 
30524             queue.push(pattern);
30525
30526             box = box.slice(length, box.length);
30527
30528             filterPattern(box, 4);
30529
30530             return;
30531             
30532         }
30533         
30534         Roo.each(boxes, function(box, k){
30535             
30536             if(!box.length){
30537                 return;
30538             }
30539             
30540             if(box.length == 1){
30541                 queue.push(box);
30542                 return;
30543             }
30544             
30545             filterPattern(box, 4);
30546             
30547         }, this);
30548         
30549         this._processVerticalLayoutQueue( queue, isInstant );
30550         
30551     },
30552     
30553 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30554 //    {
30555 //        if ( !items || !items.length ) {
30556 //            return;
30557 //        }
30558 //
30559 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30560 //        
30561 //    },
30562     
30563     _horizontalLayoutItems : function ( items , isInstant)
30564     {
30565         if ( !items || !items.length || items.length < 3) {
30566             return;
30567         }
30568         
30569         items.reverse();
30570         
30571         var eItems = items.slice(0, 3);
30572         
30573         items = items.slice(3, items.length);
30574         
30575         var standard = [
30576             ['xs', 'xs', 'xs', 'wide'],
30577             ['xs', 'xs', 'wide'],
30578             ['xs', 'xs', 'sm'],
30579             ['xs', 'xs', 'xs'],
30580             ['xs', 'wide'],
30581             ['xs', 'sm'],
30582             ['xs', 'xs'],
30583             ['xs'],
30584             
30585             ['sm', 'xs', 'xs'],
30586             ['sm', 'xs'],
30587             ['sm'],
30588             
30589             ['wide', 'xs', 'xs', 'xs'],
30590             ['wide', 'xs', 'xs'],
30591             ['wide', 'xs'],
30592             ['wide'],
30593             
30594             ['wide-thin']
30595         ];
30596         
30597         var queue = [];
30598         
30599         var boxes = [];
30600         
30601         var box = [];
30602         
30603         Roo.each(items, function(item, k){
30604             
30605             switch (item.size) {
30606                 case 'md' :
30607                 case 'md-left' :
30608                 case 'md-right' :
30609                 case 'tall' :
30610                     
30611                     if(box.length){
30612                         boxes.push(box);
30613                         box = [];
30614                     }
30615                     
30616                     boxes.push([item]);
30617                     
30618                     break;
30619                     
30620                 case 'xs' :
30621                 case 'sm' :
30622                 case 'wide' :
30623                 case 'wide-thin' :
30624                     
30625                     box.push(item);
30626                     
30627                     break;
30628                 default :
30629                     break;
30630                     
30631             }
30632             
30633         }, this);
30634         
30635         if(box.length){
30636             boxes.push(box);
30637             box = [];
30638         }
30639         
30640         var filterPattern = function(box, length)
30641         {
30642             if(!box.length){
30643                 return;
30644             }
30645             
30646             var match = false;
30647             
30648             var pattern = box.slice(0, length);
30649             
30650             var format = [];
30651             
30652             Roo.each(pattern, function(i){
30653                 format.push(i.size);
30654             }, this);
30655             
30656             Roo.each(standard, function(s){
30657                 
30658                 if(String(s) != String(format)){
30659                     return;
30660                 }
30661                 
30662                 match = true;
30663                 return false;
30664                 
30665             }, this);
30666             
30667             if(!match && length == 1){
30668                 return;
30669             }
30670             
30671             if(!match){
30672                 filterPattern(box, length - 1);
30673                 return;
30674             }
30675                 
30676             queue.push(pattern);
30677
30678             box = box.slice(length, box.length);
30679
30680             filterPattern(box, 4);
30681
30682             return;
30683             
30684         }
30685         
30686         Roo.each(boxes, function(box, k){
30687             
30688             if(!box.length){
30689                 return;
30690             }
30691             
30692             if(box.length == 1){
30693                 queue.push(box);
30694                 return;
30695             }
30696             
30697             filterPattern(box, 4);
30698             
30699         }, this);
30700         
30701         
30702         var prune = [];
30703         
30704         var pos = this.el.getBox(true);
30705         
30706         var minX = pos.x;
30707         
30708         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30709         
30710         var hit_end = false;
30711         
30712         Roo.each(queue, function(box){
30713             
30714             if(hit_end){
30715                 
30716                 Roo.each(box, function(b){
30717                 
30718                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30719                     b.el.hide();
30720
30721                 }, this);
30722
30723                 return;
30724             }
30725             
30726             var mx = 0;
30727             
30728             Roo.each(box, function(b){
30729                 
30730                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30731                 b.el.show();
30732
30733                 mx = Math.max(mx, b.x);
30734                 
30735             }, this);
30736             
30737             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30738             
30739             if(maxX < minX){
30740                 
30741                 Roo.each(box, function(b){
30742                 
30743                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30744                     b.el.hide();
30745                     
30746                 }, this);
30747                 
30748                 hit_end = true;
30749                 
30750                 return;
30751             }
30752             
30753             prune.push(box);
30754             
30755         }, this);
30756         
30757         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30758     },
30759     
30760     /** Sets position of item in DOM
30761     * @param {Element} item
30762     * @param {Number} x - horizontal position
30763     * @param {Number} y - vertical position
30764     * @param {Boolean} isInstant - disables transitions
30765     */
30766     _processVerticalLayoutQueue : function( queue, isInstant )
30767     {
30768         var pos = this.el.getBox(true);
30769         var x = pos.x;
30770         var y = pos.y;
30771         var maxY = [];
30772         
30773         for (var i = 0; i < this.cols; i++){
30774             maxY[i] = pos.y;
30775         }
30776         
30777         Roo.each(queue, function(box, k){
30778             
30779             var col = k % this.cols;
30780             
30781             Roo.each(box, function(b,kk){
30782                 
30783                 b.el.position('absolute');
30784                 
30785                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30786                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30787                 
30788                 if(b.size == 'md-left' || b.size == 'md-right'){
30789                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30790                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30791                 }
30792                 
30793                 b.el.setWidth(width);
30794                 b.el.setHeight(height);
30795                 // iframe?
30796                 b.el.select('iframe',true).setSize(width,height);
30797                 
30798             }, this);
30799             
30800             for (var i = 0; i < this.cols; i++){
30801                 
30802                 if(maxY[i] < maxY[col]){
30803                     col = i;
30804                     continue;
30805                 }
30806                 
30807                 col = Math.min(col, i);
30808                 
30809             }
30810             
30811             x = pos.x + col * (this.colWidth + this.padWidth);
30812             
30813             y = maxY[col];
30814             
30815             var positions = [];
30816             
30817             switch (box.length){
30818                 case 1 :
30819                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30820                     break;
30821                 case 2 :
30822                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30823                     break;
30824                 case 3 :
30825                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30826                     break;
30827                 case 4 :
30828                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30829                     break;
30830                 default :
30831                     break;
30832             }
30833             
30834             Roo.each(box, function(b,kk){
30835                 
30836                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30837                 
30838                 var sz = b.el.getSize();
30839                 
30840                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30841                 
30842             }, this);
30843             
30844         }, this);
30845         
30846         var mY = 0;
30847         
30848         for (var i = 0; i < this.cols; i++){
30849             mY = Math.max(mY, maxY[i]);
30850         }
30851         
30852         this.el.setHeight(mY - pos.y);
30853         
30854     },
30855     
30856 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30857 //    {
30858 //        var pos = this.el.getBox(true);
30859 //        var x = pos.x;
30860 //        var y = pos.y;
30861 //        var maxX = pos.right;
30862 //        
30863 //        var maxHeight = 0;
30864 //        
30865 //        Roo.each(items, function(item, k){
30866 //            
30867 //            var c = k % 2;
30868 //            
30869 //            item.el.position('absolute');
30870 //                
30871 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30872 //
30873 //            item.el.setWidth(width);
30874 //
30875 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30876 //
30877 //            item.el.setHeight(height);
30878 //            
30879 //            if(c == 0){
30880 //                item.el.setXY([x, y], isInstant ? false : true);
30881 //            } else {
30882 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30883 //            }
30884 //            
30885 //            y = y + height + this.alternativePadWidth;
30886 //            
30887 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30888 //            
30889 //        }, this);
30890 //        
30891 //        this.el.setHeight(maxHeight);
30892 //        
30893 //    },
30894     
30895     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30896     {
30897         var pos = this.el.getBox(true);
30898         
30899         var minX = pos.x;
30900         var minY = pos.y;
30901         
30902         var maxX = pos.right;
30903         
30904         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30905         
30906         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30907         
30908         Roo.each(queue, function(box, k){
30909             
30910             Roo.each(box, function(b, kk){
30911                 
30912                 b.el.position('absolute');
30913                 
30914                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30915                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30916                 
30917                 if(b.size == 'md-left' || b.size == 'md-right'){
30918                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30919                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30920                 }
30921                 
30922                 b.el.setWidth(width);
30923                 b.el.setHeight(height);
30924                 
30925             }, this);
30926             
30927             if(!box.length){
30928                 return;
30929             }
30930             
30931             var positions = [];
30932             
30933             switch (box.length){
30934                 case 1 :
30935                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30936                     break;
30937                 case 2 :
30938                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30939                     break;
30940                 case 3 :
30941                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30942                     break;
30943                 case 4 :
30944                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30945                     break;
30946                 default :
30947                     break;
30948             }
30949             
30950             Roo.each(box, function(b,kk){
30951                 
30952                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30953                 
30954                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30955                 
30956             }, this);
30957             
30958         }, this);
30959         
30960     },
30961     
30962     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30963     {
30964         Roo.each(eItems, function(b,k){
30965             
30966             b.size = (k == 0) ? 'sm' : 'xs';
30967             b.x = (k == 0) ? 2 : 1;
30968             b.y = (k == 0) ? 2 : 1;
30969             
30970             b.el.position('absolute');
30971             
30972             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30973                 
30974             b.el.setWidth(width);
30975             
30976             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30977             
30978             b.el.setHeight(height);
30979             
30980         }, this);
30981
30982         var positions = [];
30983         
30984         positions.push({
30985             x : maxX - this.unitWidth * 2 - this.gutter,
30986             y : minY
30987         });
30988         
30989         positions.push({
30990             x : maxX - this.unitWidth,
30991             y : minY + (this.unitWidth + this.gutter) * 2
30992         });
30993         
30994         positions.push({
30995             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30996             y : minY
30997         });
30998         
30999         Roo.each(eItems, function(b,k){
31000             
31001             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31002
31003         }, this);
31004         
31005     },
31006     
31007     getVerticalOneBoxColPositions : function(x, y, box)
31008     {
31009         var pos = [];
31010         
31011         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31012         
31013         if(box[0].size == 'md-left'){
31014             rand = 0;
31015         }
31016         
31017         if(box[0].size == 'md-right'){
31018             rand = 1;
31019         }
31020         
31021         pos.push({
31022             x : x + (this.unitWidth + this.gutter) * rand,
31023             y : y
31024         });
31025         
31026         return pos;
31027     },
31028     
31029     getVerticalTwoBoxColPositions : function(x, y, box)
31030     {
31031         var pos = [];
31032         
31033         if(box[0].size == 'xs'){
31034             
31035             pos.push({
31036                 x : x,
31037                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31038             });
31039
31040             pos.push({
31041                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31042                 y : y
31043             });
31044             
31045             return pos;
31046             
31047         }
31048         
31049         pos.push({
31050             x : x,
31051             y : y
31052         });
31053
31054         pos.push({
31055             x : x + (this.unitWidth + this.gutter) * 2,
31056             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31057         });
31058         
31059         return pos;
31060         
31061     },
31062     
31063     getVerticalThreeBoxColPositions : function(x, y, box)
31064     {
31065         var pos = [];
31066         
31067         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31068             
31069             pos.push({
31070                 x : x,
31071                 y : y
31072             });
31073
31074             pos.push({
31075                 x : x + (this.unitWidth + this.gutter) * 1,
31076                 y : y
31077             });
31078             
31079             pos.push({
31080                 x : x + (this.unitWidth + this.gutter) * 2,
31081                 y : y
31082             });
31083             
31084             return pos;
31085             
31086         }
31087         
31088         if(box[0].size == 'xs' && box[1].size == 'xs'){
31089             
31090             pos.push({
31091                 x : x,
31092                 y : y
31093             });
31094
31095             pos.push({
31096                 x : x,
31097                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31098             });
31099             
31100             pos.push({
31101                 x : x + (this.unitWidth + this.gutter) * 1,
31102                 y : y
31103             });
31104             
31105             return pos;
31106             
31107         }
31108         
31109         pos.push({
31110             x : x,
31111             y : y
31112         });
31113
31114         pos.push({
31115             x : x + (this.unitWidth + this.gutter) * 2,
31116             y : y
31117         });
31118
31119         pos.push({
31120             x : x + (this.unitWidth + this.gutter) * 2,
31121             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31122         });
31123             
31124         return pos;
31125         
31126     },
31127     
31128     getVerticalFourBoxColPositions : function(x, y, box)
31129     {
31130         var pos = [];
31131         
31132         if(box[0].size == 'xs'){
31133             
31134             pos.push({
31135                 x : x,
31136                 y : y
31137             });
31138
31139             pos.push({
31140                 x : x,
31141                 y : y + (this.unitHeight + this.gutter) * 1
31142             });
31143             
31144             pos.push({
31145                 x : x,
31146                 y : y + (this.unitHeight + this.gutter) * 2
31147             });
31148             
31149             pos.push({
31150                 x : x + (this.unitWidth + this.gutter) * 1,
31151                 y : y
31152             });
31153             
31154             return pos;
31155             
31156         }
31157         
31158         pos.push({
31159             x : x,
31160             y : y
31161         });
31162
31163         pos.push({
31164             x : x + (this.unitWidth + this.gutter) * 2,
31165             y : y
31166         });
31167
31168         pos.push({
31169             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31170             y : y + (this.unitHeight + this.gutter) * 1
31171         });
31172
31173         pos.push({
31174             x : x + (this.unitWidth + this.gutter) * 2,
31175             y : y + (this.unitWidth + this.gutter) * 2
31176         });
31177
31178         return pos;
31179         
31180     },
31181     
31182     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31183     {
31184         var pos = [];
31185         
31186         if(box[0].size == 'md-left'){
31187             pos.push({
31188                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31189                 y : minY
31190             });
31191             
31192             return pos;
31193         }
31194         
31195         if(box[0].size == 'md-right'){
31196             pos.push({
31197                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31198                 y : minY + (this.unitWidth + this.gutter) * 1
31199             });
31200             
31201             return pos;
31202         }
31203         
31204         var rand = Math.floor(Math.random() * (4 - box[0].y));
31205         
31206         pos.push({
31207             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31208             y : minY + (this.unitWidth + this.gutter) * rand
31209         });
31210         
31211         return pos;
31212         
31213     },
31214     
31215     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31216     {
31217         var pos = [];
31218         
31219         if(box[0].size == 'xs'){
31220             
31221             pos.push({
31222                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31223                 y : minY
31224             });
31225
31226             pos.push({
31227                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31228                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31229             });
31230             
31231             return pos;
31232             
31233         }
31234         
31235         pos.push({
31236             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31237             y : minY
31238         });
31239
31240         pos.push({
31241             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31242             y : minY + (this.unitWidth + this.gutter) * 2
31243         });
31244         
31245         return pos;
31246         
31247     },
31248     
31249     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31250     {
31251         var pos = [];
31252         
31253         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31254             
31255             pos.push({
31256                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31257                 y : minY
31258             });
31259
31260             pos.push({
31261                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31262                 y : minY + (this.unitWidth + this.gutter) * 1
31263             });
31264             
31265             pos.push({
31266                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31267                 y : minY + (this.unitWidth + this.gutter) * 2
31268             });
31269             
31270             return pos;
31271             
31272         }
31273         
31274         if(box[0].size == 'xs' && box[1].size == 'xs'){
31275             
31276             pos.push({
31277                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31278                 y : minY
31279             });
31280
31281             pos.push({
31282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31283                 y : minY
31284             });
31285             
31286             pos.push({
31287                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31288                 y : minY + (this.unitWidth + this.gutter) * 1
31289             });
31290             
31291             return pos;
31292             
31293         }
31294         
31295         pos.push({
31296             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31297             y : minY
31298         });
31299
31300         pos.push({
31301             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31302             y : minY + (this.unitWidth + this.gutter) * 2
31303         });
31304
31305         pos.push({
31306             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31307             y : minY + (this.unitWidth + this.gutter) * 2
31308         });
31309             
31310         return pos;
31311         
31312     },
31313     
31314     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31315     {
31316         var pos = [];
31317         
31318         if(box[0].size == 'xs'){
31319             
31320             pos.push({
31321                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31322                 y : minY
31323             });
31324
31325             pos.push({
31326                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31332                 y : minY
31333             });
31334             
31335             pos.push({
31336                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31337                 y : minY + (this.unitWidth + this.gutter) * 1
31338             });
31339             
31340             return pos;
31341             
31342         }
31343         
31344         pos.push({
31345             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31346             y : minY
31347         });
31348         
31349         pos.push({
31350             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31351             y : minY + (this.unitWidth + this.gutter) * 2
31352         });
31353         
31354         pos.push({
31355             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31361             y : minY + (this.unitWidth + this.gutter) * 2
31362         });
31363
31364         return pos;
31365         
31366     },
31367     
31368     /**
31369     * remove a Masonry Brick
31370     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31371     */
31372     removeBrick : function(brick_id)
31373     {
31374         if (!brick_id) {
31375             return;
31376         }
31377         
31378         for (var i = 0; i<this.bricks.length; i++) {
31379             if (this.bricks[i].id == brick_id) {
31380                 this.bricks.splice(i,1);
31381                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31382                 this.initial();
31383             }
31384         }
31385     },
31386     
31387     /**
31388     * adds a Masonry Brick
31389     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31390     */
31391     addBrick : function(cfg)
31392     {
31393         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31394         //this.register(cn);
31395         cn.parentId = this.id;
31396         cn.onRender(this.el, null);
31397         return cn;
31398     },
31399     
31400     /**
31401     * register a Masonry Brick
31402     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31403     */
31404     
31405     register : function(brick)
31406     {
31407         this.bricks.push(brick);
31408         brick.masonryId = this.id;
31409     },
31410     
31411     /**
31412     * clear all the Masonry Brick
31413     */
31414     clearAll : function()
31415     {
31416         this.bricks = [];
31417         //this.getChildContainer().dom.innerHTML = "";
31418         this.el.dom.innerHTML = '';
31419     },
31420     
31421     getSelected : function()
31422     {
31423         if (!this.selectedBrick) {
31424             return false;
31425         }
31426         
31427         return this.selectedBrick;
31428     }
31429 });
31430
31431 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31432     
31433     groups: {},
31434      /**
31435     * register a Masonry Layout
31436     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31437     */
31438     
31439     register : function(layout)
31440     {
31441         this.groups[layout.id] = layout;
31442     },
31443     /**
31444     * fetch a  Masonry Layout based on the masonry layout ID
31445     * @param {string} the masonry layout to add
31446     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31447     */
31448     
31449     get: function(layout_id) {
31450         if (typeof(this.groups[layout_id]) == 'undefined') {
31451             return false;
31452         }
31453         return this.groups[layout_id] ;
31454     }
31455     
31456     
31457     
31458 });
31459
31460  
31461
31462  /**
31463  *
31464  * This is based on 
31465  * http://masonry.desandro.com
31466  *
31467  * The idea is to render all the bricks based on vertical width...
31468  *
31469  * The original code extends 'outlayer' - we might need to use that....
31470  * 
31471  */
31472
31473
31474 /**
31475  * @class Roo.bootstrap.LayoutMasonryAuto
31476  * @extends Roo.bootstrap.Component
31477  * Bootstrap Layout Masonry class
31478  * 
31479  * @constructor
31480  * Create a new Element
31481  * @param {Object} config The config object
31482  */
31483
31484 Roo.bootstrap.LayoutMasonryAuto = function(config){
31485     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31486 };
31487
31488 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31489     
31490       /**
31491      * @cfg {Boolean} isFitWidth  - resize the width..
31492      */   
31493     isFitWidth : false,  // options..
31494     /**
31495      * @cfg {Boolean} isOriginLeft = left align?
31496      */   
31497     isOriginLeft : true,
31498     /**
31499      * @cfg {Boolean} isOriginTop = top align?
31500      */   
31501     isOriginTop : false,
31502     /**
31503      * @cfg {Boolean} isLayoutInstant = no animation?
31504      */   
31505     isLayoutInstant : false, // needed?
31506     /**
31507      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31508      */   
31509     isResizingContainer : true,
31510     /**
31511      * @cfg {Number} columnWidth  width of the columns 
31512      */   
31513     
31514     columnWidth : 0,
31515     
31516     /**
31517      * @cfg {Number} maxCols maximum number of columns
31518      */   
31519     
31520     maxCols: 0,
31521     /**
31522      * @cfg {Number} padHeight padding below box..
31523      */   
31524     
31525     padHeight : 10, 
31526     
31527     /**
31528      * @cfg {Boolean} isAutoInitial defalut true
31529      */   
31530     
31531     isAutoInitial : true, 
31532     
31533     // private?
31534     gutter : 0,
31535     
31536     containerWidth: 0,
31537     initialColumnWidth : 0,
31538     currentSize : null,
31539     
31540     colYs : null, // array.
31541     maxY : 0,
31542     padWidth: 10,
31543     
31544     
31545     tag: 'div',
31546     cls: '',
31547     bricks: null, //CompositeElement
31548     cols : 0, // array?
31549     // element : null, // wrapped now this.el
31550     _isLayoutInited : null, 
31551     
31552     
31553     getAutoCreate : function(){
31554         
31555         var cfg = {
31556             tag: this.tag,
31557             cls: 'blog-masonary-wrapper ' + this.cls,
31558             cn : {
31559                 cls : 'mas-boxes masonary'
31560             }
31561         };
31562         
31563         return cfg;
31564     },
31565     
31566     getChildContainer: function( )
31567     {
31568         if (this.boxesEl) {
31569             return this.boxesEl;
31570         }
31571         
31572         this.boxesEl = this.el.select('.mas-boxes').first();
31573         
31574         return this.boxesEl;
31575     },
31576     
31577     
31578     initEvents : function()
31579     {
31580         var _this = this;
31581         
31582         if(this.isAutoInitial){
31583             Roo.log('hook children rendered');
31584             this.on('childrenrendered', function() {
31585                 Roo.log('children rendered');
31586                 _this.initial();
31587             } ,this);
31588         }
31589         
31590     },
31591     
31592     initial : function()
31593     {
31594         this.reloadItems();
31595
31596         this.currentSize = this.el.getBox(true);
31597
31598         /// was window resize... - let's see if this works..
31599         Roo.EventManager.onWindowResize(this.resize, this); 
31600
31601         if(!this.isAutoInitial){
31602             this.layout();
31603             return;
31604         }
31605         
31606         this.layout.defer(500,this);
31607     },
31608     
31609     reloadItems: function()
31610     {
31611         this.bricks = this.el.select('.masonry-brick', true);
31612         
31613         this.bricks.each(function(b) {
31614             //Roo.log(b.getSize());
31615             if (!b.attr('originalwidth')) {
31616                 b.attr('originalwidth',  b.getSize().width);
31617             }
31618             
31619         });
31620         
31621         Roo.log(this.bricks.elements.length);
31622     },
31623     
31624     resize : function()
31625     {
31626         Roo.log('resize');
31627         var cs = this.el.getBox(true);
31628         
31629         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31630             Roo.log("no change in with or X");
31631             return;
31632         }
31633         this.currentSize = cs;
31634         this.layout();
31635     },
31636     
31637     layout : function()
31638     {
31639          Roo.log('layout');
31640         this._resetLayout();
31641         //this._manageStamps();
31642       
31643         // don't animate first layout
31644         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31645         this.layoutItems( isInstant );
31646       
31647         // flag for initalized
31648         this._isLayoutInited = true;
31649     },
31650     
31651     layoutItems : function( isInstant )
31652     {
31653         //var items = this._getItemsForLayout( this.items );
31654         // original code supports filtering layout items.. we just ignore it..
31655         
31656         this._layoutItems( this.bricks , isInstant );
31657       
31658         this._postLayout();
31659     },
31660     _layoutItems : function ( items , isInstant)
31661     {
31662        //this.fireEvent( 'layout', this, items );
31663     
31664
31665         if ( !items || !items.elements.length ) {
31666           // no items, emit event with empty array
31667             return;
31668         }
31669
31670         var queue = [];
31671         items.each(function(item) {
31672             Roo.log("layout item");
31673             Roo.log(item);
31674             // get x/y object from method
31675             var position = this._getItemLayoutPosition( item );
31676             // enqueue
31677             position.item = item;
31678             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31679             queue.push( position );
31680         }, this);
31681       
31682         this._processLayoutQueue( queue );
31683     },
31684     /** Sets position of item in DOM
31685     * @param {Element} item
31686     * @param {Number} x - horizontal position
31687     * @param {Number} y - vertical position
31688     * @param {Boolean} isInstant - disables transitions
31689     */
31690     _processLayoutQueue : function( queue )
31691     {
31692         for ( var i=0, len = queue.length; i < len; i++ ) {
31693             var obj = queue[i];
31694             obj.item.position('absolute');
31695             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31696         }
31697     },
31698       
31699     
31700     /**
31701     * Any logic you want to do after each layout,
31702     * i.e. size the container
31703     */
31704     _postLayout : function()
31705     {
31706         this.resizeContainer();
31707     },
31708     
31709     resizeContainer : function()
31710     {
31711         if ( !this.isResizingContainer ) {
31712             return;
31713         }
31714         var size = this._getContainerSize();
31715         if ( size ) {
31716             this.el.setSize(size.width,size.height);
31717             this.boxesEl.setSize(size.width,size.height);
31718         }
31719     },
31720     
31721     
31722     
31723     _resetLayout : function()
31724     {
31725         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31726         this.colWidth = this.el.getWidth();
31727         //this.gutter = this.el.getWidth(); 
31728         
31729         this.measureColumns();
31730
31731         // reset column Y
31732         var i = this.cols;
31733         this.colYs = [];
31734         while (i--) {
31735             this.colYs.push( 0 );
31736         }
31737     
31738         this.maxY = 0;
31739     },
31740
31741     measureColumns : function()
31742     {
31743         this.getContainerWidth();
31744       // if columnWidth is 0, default to outerWidth of first item
31745         if ( !this.columnWidth ) {
31746             var firstItem = this.bricks.first();
31747             Roo.log(firstItem);
31748             this.columnWidth  = this.containerWidth;
31749             if (firstItem && firstItem.attr('originalwidth') ) {
31750                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31751             }
31752             // columnWidth fall back to item of first element
31753             Roo.log("set column width?");
31754                         this.initialColumnWidth = this.columnWidth  ;
31755
31756             // if first elem has no width, default to size of container
31757             
31758         }
31759         
31760         
31761         if (this.initialColumnWidth) {
31762             this.columnWidth = this.initialColumnWidth;
31763         }
31764         
31765         
31766             
31767         // column width is fixed at the top - however if container width get's smaller we should
31768         // reduce it...
31769         
31770         // this bit calcs how man columns..
31771             
31772         var columnWidth = this.columnWidth += this.gutter;
31773       
31774         // calculate columns
31775         var containerWidth = this.containerWidth + this.gutter;
31776         
31777         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31778         // fix rounding errors, typically with gutters
31779         var excess = columnWidth - containerWidth % columnWidth;
31780         
31781         
31782         // if overshoot is less than a pixel, round up, otherwise floor it
31783         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31784         cols = Math[ mathMethod ]( cols );
31785         this.cols = Math.max( cols, 1 );
31786         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31787         
31788          // padding positioning..
31789         var totalColWidth = this.cols * this.columnWidth;
31790         var padavail = this.containerWidth - totalColWidth;
31791         // so for 2 columns - we need 3 'pads'
31792         
31793         var padNeeded = (1+this.cols) * this.padWidth;
31794         
31795         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31796         
31797         this.columnWidth += padExtra
31798         //this.padWidth = Math.floor(padavail /  ( this.cols));
31799         
31800         // adjust colum width so that padding is fixed??
31801         
31802         // we have 3 columns ... total = width * 3
31803         // we have X left over... that should be used by 
31804         
31805         //if (this.expandC) {
31806             
31807         //}
31808         
31809         
31810         
31811     },
31812     
31813     getContainerWidth : function()
31814     {
31815        /* // container is parent if fit width
31816         var container = this.isFitWidth ? this.element.parentNode : this.element;
31817         // check that this.size and size are there
31818         // IE8 triggers resize on body size change, so they might not be
31819         
31820         var size = getSize( container );  //FIXME
31821         this.containerWidth = size && size.innerWidth; //FIXME
31822         */
31823          
31824         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31825         
31826     },
31827     
31828     _getItemLayoutPosition : function( item )  // what is item?
31829     {
31830         // we resize the item to our columnWidth..
31831       
31832         item.setWidth(this.columnWidth);
31833         item.autoBoxAdjust  = false;
31834         
31835         var sz = item.getSize();
31836  
31837         // how many columns does this brick span
31838         var remainder = this.containerWidth % this.columnWidth;
31839         
31840         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31841         // round if off by 1 pixel, otherwise use ceil
31842         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31843         colSpan = Math.min( colSpan, this.cols );
31844         
31845         // normally this should be '1' as we dont' currently allow multi width columns..
31846         
31847         var colGroup = this._getColGroup( colSpan );
31848         // get the minimum Y value from the columns
31849         var minimumY = Math.min.apply( Math, colGroup );
31850         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31851         
31852         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31853          
31854         // position the brick
31855         var position = {
31856             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31857             y: this.currentSize.y + minimumY + this.padHeight
31858         };
31859         
31860         Roo.log(position);
31861         // apply setHeight to necessary columns
31862         var setHeight = minimumY + sz.height + this.padHeight;
31863         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31864         
31865         var setSpan = this.cols + 1 - colGroup.length;
31866         for ( var i = 0; i < setSpan; i++ ) {
31867           this.colYs[ shortColIndex + i ] = setHeight ;
31868         }
31869       
31870         return position;
31871     },
31872     
31873     /**
31874      * @param {Number} colSpan - number of columns the element spans
31875      * @returns {Array} colGroup
31876      */
31877     _getColGroup : function( colSpan )
31878     {
31879         if ( colSpan < 2 ) {
31880           // if brick spans only one column, use all the column Ys
31881           return this.colYs;
31882         }
31883       
31884         var colGroup = [];
31885         // how many different places could this brick fit horizontally
31886         var groupCount = this.cols + 1 - colSpan;
31887         // for each group potential horizontal position
31888         for ( var i = 0; i < groupCount; i++ ) {
31889           // make an array of colY values for that one group
31890           var groupColYs = this.colYs.slice( i, i + colSpan );
31891           // and get the max value of the array
31892           colGroup[i] = Math.max.apply( Math, groupColYs );
31893         }
31894         return colGroup;
31895     },
31896     /*
31897     _manageStamp : function( stamp )
31898     {
31899         var stampSize =  stamp.getSize();
31900         var offset = stamp.getBox();
31901         // get the columns that this stamp affects
31902         var firstX = this.isOriginLeft ? offset.x : offset.right;
31903         var lastX = firstX + stampSize.width;
31904         var firstCol = Math.floor( firstX / this.columnWidth );
31905         firstCol = Math.max( 0, firstCol );
31906         
31907         var lastCol = Math.floor( lastX / this.columnWidth );
31908         // lastCol should not go over if multiple of columnWidth #425
31909         lastCol -= lastX % this.columnWidth ? 0 : 1;
31910         lastCol = Math.min( this.cols - 1, lastCol );
31911         
31912         // set colYs to bottom of the stamp
31913         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31914             stampSize.height;
31915             
31916         for ( var i = firstCol; i <= lastCol; i++ ) {
31917           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31918         }
31919     },
31920     */
31921     
31922     _getContainerSize : function()
31923     {
31924         this.maxY = Math.max.apply( Math, this.colYs );
31925         var size = {
31926             height: this.maxY
31927         };
31928       
31929         if ( this.isFitWidth ) {
31930             size.width = this._getContainerFitWidth();
31931         }
31932       
31933         return size;
31934     },
31935     
31936     _getContainerFitWidth : function()
31937     {
31938         var unusedCols = 0;
31939         // count unused columns
31940         var i = this.cols;
31941         while ( --i ) {
31942           if ( this.colYs[i] !== 0 ) {
31943             break;
31944           }
31945           unusedCols++;
31946         }
31947         // fit container to columns that have been used
31948         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31949     },
31950     
31951     needsResizeLayout : function()
31952     {
31953         var previousWidth = this.containerWidth;
31954         this.getContainerWidth();
31955         return previousWidth !== this.containerWidth;
31956     }
31957  
31958 });
31959
31960  
31961
31962  /*
31963  * - LGPL
31964  *
31965  * element
31966  * 
31967  */
31968
31969 /**
31970  * @class Roo.bootstrap.MasonryBrick
31971  * @extends Roo.bootstrap.Component
31972  * Bootstrap MasonryBrick class
31973  * 
31974  * @constructor
31975  * Create a new MasonryBrick
31976  * @param {Object} config The config object
31977  */
31978
31979 Roo.bootstrap.MasonryBrick = function(config){
31980     
31981     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31982     
31983     Roo.bootstrap.MasonryBrick.register(this);
31984     
31985     this.addEvents({
31986         // raw events
31987         /**
31988          * @event click
31989          * When a MasonryBrick is clcik
31990          * @param {Roo.bootstrap.MasonryBrick} this
31991          * @param {Roo.EventObject} e
31992          */
31993         "click" : true
31994     });
31995 };
31996
31997 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31998     
31999     /**
32000      * @cfg {String} title
32001      */   
32002     title : '',
32003     /**
32004      * @cfg {String} html
32005      */   
32006     html : '',
32007     /**
32008      * @cfg {String} bgimage
32009      */   
32010     bgimage : '',
32011     /**
32012      * @cfg {String} videourl
32013      */   
32014     videourl : '',
32015     /**
32016      * @cfg {String} cls
32017      */   
32018     cls : '',
32019     /**
32020      * @cfg {String} href
32021      */   
32022     href : '',
32023     /**
32024      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32025      */   
32026     size : 'xs',
32027     
32028     /**
32029      * @cfg {String} placetitle (center|bottom)
32030      */   
32031     placetitle : '',
32032     
32033     /**
32034      * @cfg {Boolean} isFitContainer defalut true
32035      */   
32036     isFitContainer : true, 
32037     
32038     /**
32039      * @cfg {Boolean} preventDefault defalut false
32040      */   
32041     preventDefault : false, 
32042     
32043     /**
32044      * @cfg {Boolean} inverse defalut false
32045      */   
32046     maskInverse : false, 
32047     
32048     getAutoCreate : function()
32049     {
32050         if(!this.isFitContainer){
32051             return this.getSplitAutoCreate();
32052         }
32053         
32054         var cls = 'masonry-brick masonry-brick-full';
32055         
32056         if(this.href.length){
32057             cls += ' masonry-brick-link';
32058         }
32059         
32060         if(this.bgimage.length){
32061             cls += ' masonry-brick-image';
32062         }
32063         
32064         if(this.maskInverse){
32065             cls += ' mask-inverse';
32066         }
32067         
32068         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32069             cls += ' enable-mask';
32070         }
32071         
32072         if(this.size){
32073             cls += ' masonry-' + this.size + '-brick';
32074         }
32075         
32076         if(this.placetitle.length){
32077             
32078             switch (this.placetitle) {
32079                 case 'center' :
32080                     cls += ' masonry-center-title';
32081                     break;
32082                 case 'bottom' :
32083                     cls += ' masonry-bottom-title';
32084                     break;
32085                 default:
32086                     break;
32087             }
32088             
32089         } else {
32090             if(!this.html.length && !this.bgimage.length){
32091                 cls += ' masonry-center-title';
32092             }
32093
32094             if(!this.html.length && this.bgimage.length){
32095                 cls += ' masonry-bottom-title';
32096             }
32097         }
32098         
32099         if(this.cls){
32100             cls += ' ' + this.cls;
32101         }
32102         
32103         var cfg = {
32104             tag: (this.href.length) ? 'a' : 'div',
32105             cls: cls,
32106             cn: [
32107                 {
32108                     tag: 'div',
32109                     cls: 'masonry-brick-mask'
32110                 },
32111                 {
32112                     tag: 'div',
32113                     cls: 'masonry-brick-paragraph',
32114                     cn: []
32115                 }
32116             ]
32117         };
32118         
32119         if(this.href.length){
32120             cfg.href = this.href;
32121         }
32122         
32123         var cn = cfg.cn[1].cn;
32124         
32125         if(this.title.length){
32126             cn.push({
32127                 tag: 'h4',
32128                 cls: 'masonry-brick-title',
32129                 html: this.title
32130             });
32131         }
32132         
32133         if(this.html.length){
32134             cn.push({
32135                 tag: 'p',
32136                 cls: 'masonry-brick-text',
32137                 html: this.html
32138             });
32139         }
32140         
32141         if (!this.title.length && !this.html.length) {
32142             cfg.cn[1].cls += ' hide';
32143         }
32144         
32145         if(this.bgimage.length){
32146             cfg.cn.push({
32147                 tag: 'img',
32148                 cls: 'masonry-brick-image-view',
32149                 src: this.bgimage
32150             });
32151         }
32152         
32153         if(this.videourl.length){
32154             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32155             // youtube support only?
32156             cfg.cn.push({
32157                 tag: 'iframe',
32158                 cls: 'masonry-brick-image-view',
32159                 src: vurl,
32160                 frameborder : 0,
32161                 allowfullscreen : true
32162             });
32163         }
32164         
32165         return cfg;
32166         
32167     },
32168     
32169     getSplitAutoCreate : function()
32170     {
32171         var cls = 'masonry-brick masonry-brick-split';
32172         
32173         if(this.href.length){
32174             cls += ' masonry-brick-link';
32175         }
32176         
32177         if(this.bgimage.length){
32178             cls += ' masonry-brick-image';
32179         }
32180         
32181         if(this.size){
32182             cls += ' masonry-' + this.size + '-brick';
32183         }
32184         
32185         switch (this.placetitle) {
32186             case 'center' :
32187                 cls += ' masonry-center-title';
32188                 break;
32189             case 'bottom' :
32190                 cls += ' masonry-bottom-title';
32191                 break;
32192             default:
32193                 if(!this.bgimage.length){
32194                     cls += ' masonry-center-title';
32195                 }
32196
32197                 if(this.bgimage.length){
32198                     cls += ' masonry-bottom-title';
32199                 }
32200                 break;
32201         }
32202         
32203         if(this.cls){
32204             cls += ' ' + this.cls;
32205         }
32206         
32207         var cfg = {
32208             tag: (this.href.length) ? 'a' : 'div',
32209             cls: cls,
32210             cn: [
32211                 {
32212                     tag: 'div',
32213                     cls: 'masonry-brick-split-head',
32214                     cn: [
32215                         {
32216                             tag: 'div',
32217                             cls: 'masonry-brick-paragraph',
32218                             cn: []
32219                         }
32220                     ]
32221                 },
32222                 {
32223                     tag: 'div',
32224                     cls: 'masonry-brick-split-body',
32225                     cn: []
32226                 }
32227             ]
32228         };
32229         
32230         if(this.href.length){
32231             cfg.href = this.href;
32232         }
32233         
32234         if(this.title.length){
32235             cfg.cn[0].cn[0].cn.push({
32236                 tag: 'h4',
32237                 cls: 'masonry-brick-title',
32238                 html: this.title
32239             });
32240         }
32241         
32242         if(this.html.length){
32243             cfg.cn[1].cn.push({
32244                 tag: 'p',
32245                 cls: 'masonry-brick-text',
32246                 html: this.html
32247             });
32248         }
32249
32250         if(this.bgimage.length){
32251             cfg.cn[0].cn.push({
32252                 tag: 'img',
32253                 cls: 'masonry-brick-image-view',
32254                 src: this.bgimage
32255             });
32256         }
32257         
32258         if(this.videourl.length){
32259             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32260             // youtube support only?
32261             cfg.cn[0].cn.cn.push({
32262                 tag: 'iframe',
32263                 cls: 'masonry-brick-image-view',
32264                 src: vurl,
32265                 frameborder : 0,
32266                 allowfullscreen : true
32267             });
32268         }
32269         
32270         return cfg;
32271     },
32272     
32273     initEvents: function() 
32274     {
32275         switch (this.size) {
32276             case 'xs' :
32277                 this.x = 1;
32278                 this.y = 1;
32279                 break;
32280             case 'sm' :
32281                 this.x = 2;
32282                 this.y = 2;
32283                 break;
32284             case 'md' :
32285             case 'md-left' :
32286             case 'md-right' :
32287                 this.x = 3;
32288                 this.y = 3;
32289                 break;
32290             case 'tall' :
32291                 this.x = 2;
32292                 this.y = 3;
32293                 break;
32294             case 'wide' :
32295                 this.x = 3;
32296                 this.y = 2;
32297                 break;
32298             case 'wide-thin' :
32299                 this.x = 3;
32300                 this.y = 1;
32301                 break;
32302                         
32303             default :
32304                 break;
32305         }
32306         
32307         if(Roo.isTouch){
32308             this.el.on('touchstart', this.onTouchStart, this);
32309             this.el.on('touchmove', this.onTouchMove, this);
32310             this.el.on('touchend', this.onTouchEnd, this);
32311             this.el.on('contextmenu', this.onContextMenu, this);
32312         } else {
32313             this.el.on('mouseenter'  ,this.enter, this);
32314             this.el.on('mouseleave', this.leave, this);
32315             this.el.on('click', this.onClick, this);
32316         }
32317         
32318         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32319             this.parent().bricks.push(this);   
32320         }
32321         
32322     },
32323     
32324     onClick: function(e, el)
32325     {
32326         var time = this.endTimer - this.startTimer;
32327         // Roo.log(e.preventDefault());
32328         if(Roo.isTouch){
32329             if(time > 1000){
32330                 e.preventDefault();
32331                 return;
32332             }
32333         }
32334         
32335         if(!this.preventDefault){
32336             return;
32337         }
32338         
32339         e.preventDefault();
32340         
32341         if (this.activcClass != '') {
32342             this.selectBrick();
32343         }
32344         
32345         this.fireEvent('click', this);
32346     },
32347     
32348     enter: function(e, el)
32349     {
32350         e.preventDefault();
32351         
32352         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32353             return;
32354         }
32355         
32356         if(this.bgimage.length && this.html.length){
32357             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32358         }
32359     },
32360     
32361     leave: function(e, el)
32362     {
32363         e.preventDefault();
32364         
32365         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32366             return;
32367         }
32368         
32369         if(this.bgimage.length && this.html.length){
32370             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32371         }
32372     },
32373     
32374     onTouchStart: function(e, el)
32375     {
32376 //        e.preventDefault();
32377         
32378         this.touchmoved = false;
32379         
32380         if(!this.isFitContainer){
32381             return;
32382         }
32383         
32384         if(!this.bgimage.length || !this.html.length){
32385             return;
32386         }
32387         
32388         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32389         
32390         this.timer = new Date().getTime();
32391         
32392     },
32393     
32394     onTouchMove: function(e, el)
32395     {
32396         this.touchmoved = true;
32397     },
32398     
32399     onContextMenu : function(e,el)
32400     {
32401         e.preventDefault();
32402         e.stopPropagation();
32403         return false;
32404     },
32405     
32406     onTouchEnd: function(e, el)
32407     {
32408 //        e.preventDefault();
32409         
32410         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32411         
32412             this.leave(e,el);
32413             
32414             return;
32415         }
32416         
32417         if(!this.bgimage.length || !this.html.length){
32418             
32419             if(this.href.length){
32420                 window.location.href = this.href;
32421             }
32422             
32423             return;
32424         }
32425         
32426         if(!this.isFitContainer){
32427             return;
32428         }
32429         
32430         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32431         
32432         window.location.href = this.href;
32433     },
32434     
32435     //selection on single brick only
32436     selectBrick : function() {
32437         
32438         if (!this.parentId) {
32439             return;
32440         }
32441         
32442         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32443         var index = m.selectedBrick.indexOf(this.id);
32444         
32445         if ( index > -1) {
32446             m.selectedBrick.splice(index,1);
32447             this.el.removeClass(this.activeClass);
32448             return;
32449         }
32450         
32451         for(var i = 0; i < m.selectedBrick.length; i++) {
32452             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32453             b.el.removeClass(b.activeClass);
32454         }
32455         
32456         m.selectedBrick = [];
32457         
32458         m.selectedBrick.push(this.id);
32459         this.el.addClass(this.activeClass);
32460         return;
32461     }
32462     
32463 });
32464
32465 Roo.apply(Roo.bootstrap.MasonryBrick, {
32466     
32467     //groups: {},
32468     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32469      /**
32470     * register a Masonry Brick
32471     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32472     */
32473     
32474     register : function(brick)
32475     {
32476         //this.groups[brick.id] = brick;
32477         this.groups.add(brick.id, brick);
32478     },
32479     /**
32480     * fetch a  masonry brick based on the masonry brick ID
32481     * @param {string} the masonry brick to add
32482     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32483     */
32484     
32485     get: function(brick_id) 
32486     {
32487         // if (typeof(this.groups[brick_id]) == 'undefined') {
32488         //     return false;
32489         // }
32490         // return this.groups[brick_id] ;
32491         
32492         if(this.groups.key(brick_id)) {
32493             return this.groups.key(brick_id);
32494         }
32495         
32496         return false;
32497     }
32498     
32499     
32500     
32501 });
32502
32503  /*
32504  * - LGPL
32505  *
32506  * element
32507  * 
32508  */
32509
32510 /**
32511  * @class Roo.bootstrap.Brick
32512  * @extends Roo.bootstrap.Component
32513  * Bootstrap Brick class
32514  * 
32515  * @constructor
32516  * Create a new Brick
32517  * @param {Object} config The config object
32518  */
32519
32520 Roo.bootstrap.Brick = function(config){
32521     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32522     
32523     this.addEvents({
32524         // raw events
32525         /**
32526          * @event click
32527          * When a Brick is click
32528          * @param {Roo.bootstrap.Brick} this
32529          * @param {Roo.EventObject} e
32530          */
32531         "click" : true
32532     });
32533 };
32534
32535 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32536     
32537     /**
32538      * @cfg {String} title
32539      */   
32540     title : '',
32541     /**
32542      * @cfg {String} html
32543      */   
32544     html : '',
32545     /**
32546      * @cfg {String} bgimage
32547      */   
32548     bgimage : '',
32549     /**
32550      * @cfg {String} cls
32551      */   
32552     cls : '',
32553     /**
32554      * @cfg {String} href
32555      */   
32556     href : '',
32557     /**
32558      * @cfg {String} video
32559      */   
32560     video : '',
32561     /**
32562      * @cfg {Boolean} square
32563      */   
32564     square : true,
32565     
32566     getAutoCreate : function()
32567     {
32568         var cls = 'roo-brick';
32569         
32570         if(this.href.length){
32571             cls += ' roo-brick-link';
32572         }
32573         
32574         if(this.bgimage.length){
32575             cls += ' roo-brick-image';
32576         }
32577         
32578         if(!this.html.length && !this.bgimage.length){
32579             cls += ' roo-brick-center-title';
32580         }
32581         
32582         if(!this.html.length && this.bgimage.length){
32583             cls += ' roo-brick-bottom-title';
32584         }
32585         
32586         if(this.cls){
32587             cls += ' ' + this.cls;
32588         }
32589         
32590         var cfg = {
32591             tag: (this.href.length) ? 'a' : 'div',
32592             cls: cls,
32593             cn: [
32594                 {
32595                     tag: 'div',
32596                     cls: 'roo-brick-paragraph',
32597                     cn: []
32598                 }
32599             ]
32600         };
32601         
32602         if(this.href.length){
32603             cfg.href = this.href;
32604         }
32605         
32606         var cn = cfg.cn[0].cn;
32607         
32608         if(this.title.length){
32609             cn.push({
32610                 tag: 'h4',
32611                 cls: 'roo-brick-title',
32612                 html: this.title
32613             });
32614         }
32615         
32616         if(this.html.length){
32617             cn.push({
32618                 tag: 'p',
32619                 cls: 'roo-brick-text',
32620                 html: this.html
32621             });
32622         } else {
32623             cn.cls += ' hide';
32624         }
32625         
32626         if(this.bgimage.length){
32627             cfg.cn.push({
32628                 tag: 'img',
32629                 cls: 'roo-brick-image-view',
32630                 src: this.bgimage
32631             });
32632         }
32633         
32634         return cfg;
32635     },
32636     
32637     initEvents: function() 
32638     {
32639         if(this.title.length || this.html.length){
32640             this.el.on('mouseenter'  ,this.enter, this);
32641             this.el.on('mouseleave', this.leave, this);
32642         }
32643         
32644         Roo.EventManager.onWindowResize(this.resize, this); 
32645         
32646         if(this.bgimage.length){
32647             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32648             this.imageEl.on('load', this.onImageLoad, this);
32649             return;
32650         }
32651         
32652         this.resize();
32653     },
32654     
32655     onImageLoad : function()
32656     {
32657         this.resize();
32658     },
32659     
32660     resize : function()
32661     {
32662         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32663         
32664         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32665         
32666         if(this.bgimage.length){
32667             var image = this.el.select('.roo-brick-image-view', true).first();
32668             
32669             image.setWidth(paragraph.getWidth());
32670             
32671             if(this.square){
32672                 image.setHeight(paragraph.getWidth());
32673             }
32674             
32675             this.el.setHeight(image.getHeight());
32676             paragraph.setHeight(image.getHeight());
32677             
32678         }
32679         
32680     },
32681     
32682     enter: function(e, el)
32683     {
32684         e.preventDefault();
32685         
32686         if(this.bgimage.length){
32687             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32688             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32689         }
32690     },
32691     
32692     leave: function(e, el)
32693     {
32694         e.preventDefault();
32695         
32696         if(this.bgimage.length){
32697             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32698             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32699         }
32700     }
32701     
32702 });
32703
32704  
32705
32706  /*
32707  * - LGPL
32708  *
32709  * Input
32710  * 
32711  */
32712
32713 /**
32714  * @class Roo.bootstrap.NumberField
32715  * @extends Roo.bootstrap.Input
32716  * Bootstrap NumberField class
32717  * 
32718  * 
32719  * 
32720  * 
32721  * @constructor
32722  * Create a new NumberField
32723  * @param {Object} config The config object
32724  */
32725
32726 Roo.bootstrap.NumberField = function(config){
32727     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32728 };
32729
32730 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32731     
32732     /**
32733      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32734      */
32735     allowDecimals : true,
32736     /**
32737      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32738      */
32739     decimalSeparator : ".",
32740     /**
32741      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32742      */
32743     decimalPrecision : 2,
32744     /**
32745      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32746      */
32747     allowNegative : true,
32748     /**
32749      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32750      */
32751     minValue : Number.NEGATIVE_INFINITY,
32752     /**
32753      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32754      */
32755     maxValue : Number.MAX_VALUE,
32756     /**
32757      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32758      */
32759     minText : "The minimum value for this field is {0}",
32760     /**
32761      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32762      */
32763     maxText : "The maximum value for this field is {0}",
32764     /**
32765      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32766      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32767      */
32768     nanText : "{0} is not a valid number",
32769     /**
32770      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32771      */
32772     castInt : true,
32773
32774     // private
32775     initEvents : function()
32776     {   
32777         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32778         
32779         var allowed = "0123456789";
32780         
32781         if(this.allowDecimals){
32782             allowed += this.decimalSeparator;
32783         }
32784         
32785         if(this.allowNegative){
32786             allowed += "-";
32787         }
32788         
32789         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32790         
32791         var keyPress = function(e){
32792             
32793             var k = e.getKey();
32794             
32795             var c = e.getCharCode();
32796             
32797             if(
32798                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32799                     allowed.indexOf(String.fromCharCode(c)) === -1
32800             ){
32801                 e.stopEvent();
32802                 return;
32803             }
32804             
32805             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32806                 return;
32807             }
32808             
32809             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32810                 e.stopEvent();
32811             }
32812         };
32813         
32814         this.el.on("keypress", keyPress, this);
32815     },
32816     
32817     validateValue : function(value)
32818     {
32819         
32820         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32821             return false;
32822         }
32823         
32824         var num = this.parseValue(value);
32825         
32826         if(isNaN(num)){
32827             this.markInvalid(String.format(this.nanText, value));
32828             return false;
32829         }
32830         
32831         if(num < this.minValue){
32832             this.markInvalid(String.format(this.minText, this.minValue));
32833             return false;
32834         }
32835         
32836         if(num > this.maxValue){
32837             this.markInvalid(String.format(this.maxText, this.maxValue));
32838             return false;
32839         }
32840         
32841         return true;
32842     },
32843
32844     getValue : function()
32845     {
32846         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32847     },
32848
32849     parseValue : function(value)
32850     {
32851         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32852         return isNaN(value) ? '' : value;
32853     },
32854
32855     fixPrecision : function(value)
32856     {
32857         var nan = isNaN(value);
32858         
32859         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32860             return nan ? '' : value;
32861         }
32862         return parseFloat(value).toFixed(this.decimalPrecision);
32863     },
32864
32865     setValue : function(v)
32866     {
32867         v = this.fixPrecision(v);
32868         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32869     },
32870
32871     decimalPrecisionFcn : function(v)
32872     {
32873         return Math.floor(v);
32874     },
32875
32876     beforeBlur : function()
32877     {
32878         if(!this.castInt){
32879             return;
32880         }
32881         
32882         var v = this.parseValue(this.getRawValue());
32883         if(v){
32884             this.setValue(v);
32885         }
32886     }
32887     
32888 });
32889
32890  
32891
32892 /*
32893 * Licence: LGPL
32894 */
32895
32896 /**
32897  * @class Roo.bootstrap.DocumentSlider
32898  * @extends Roo.bootstrap.Component
32899  * Bootstrap DocumentSlider class
32900  * 
32901  * @constructor
32902  * Create a new DocumentViewer
32903  * @param {Object} config The config object
32904  */
32905
32906 Roo.bootstrap.DocumentSlider = function(config){
32907     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32908     
32909     this.files = [];
32910     
32911     this.addEvents({
32912         /**
32913          * @event initial
32914          * Fire after initEvent
32915          * @param {Roo.bootstrap.DocumentSlider} this
32916          */
32917         "initial" : true,
32918         /**
32919          * @event update
32920          * Fire after update
32921          * @param {Roo.bootstrap.DocumentSlider} this
32922          */
32923         "update" : true,
32924         /**
32925          * @event click
32926          * Fire after click
32927          * @param {Roo.bootstrap.DocumentSlider} this
32928          */
32929         "click" : true
32930     });
32931 };
32932
32933 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32934     
32935     files : false,
32936     
32937     indicator : 0,
32938     
32939     getAutoCreate : function()
32940     {
32941         var cfg = {
32942             tag : 'div',
32943             cls : 'roo-document-slider',
32944             cn : [
32945                 {
32946                     tag : 'div',
32947                     cls : 'roo-document-slider-header',
32948                     cn : [
32949                         {
32950                             tag : 'div',
32951                             cls : 'roo-document-slider-header-title'
32952                         }
32953                     ]
32954                 },
32955                 {
32956                     tag : 'div',
32957                     cls : 'roo-document-slider-body',
32958                     cn : [
32959                         {
32960                             tag : 'div',
32961                             cls : 'roo-document-slider-prev',
32962                             cn : [
32963                                 {
32964                                     tag : 'i',
32965                                     cls : 'fa fa-chevron-left'
32966                                 }
32967                             ]
32968                         },
32969                         {
32970                             tag : 'div',
32971                             cls : 'roo-document-slider-thumb',
32972                             cn : [
32973                                 {
32974                                     tag : 'img',
32975                                     cls : 'roo-document-slider-image'
32976                                 }
32977                             ]
32978                         },
32979                         {
32980                             tag : 'div',
32981                             cls : 'roo-document-slider-next',
32982                             cn : [
32983                                 {
32984                                     tag : 'i',
32985                                     cls : 'fa fa-chevron-right'
32986                                 }
32987                             ]
32988                         }
32989                     ]
32990                 }
32991             ]
32992         };
32993         
32994         return cfg;
32995     },
32996     
32997     initEvents : function()
32998     {
32999         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33000         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33001         
33002         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33003         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33004         
33005         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33006         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33007         
33008         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33009         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33010         
33011         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33012         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33013         
33014         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33015         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33016         
33017         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33018         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33019         
33020         this.thumbEl.on('click', this.onClick, this);
33021         
33022         this.prevIndicator.on('click', this.prev, this);
33023         
33024         this.nextIndicator.on('click', this.next, this);
33025         
33026     },
33027     
33028     initial : function()
33029     {
33030         if(this.files.length){
33031             this.indicator = 1;
33032             this.update()
33033         }
33034         
33035         this.fireEvent('initial', this);
33036     },
33037     
33038     update : function()
33039     {
33040         this.imageEl.attr('src', this.files[this.indicator - 1]);
33041         
33042         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33043         
33044         this.prevIndicator.show();
33045         
33046         if(this.indicator == 1){
33047             this.prevIndicator.hide();
33048         }
33049         
33050         this.nextIndicator.show();
33051         
33052         if(this.indicator == this.files.length){
33053             this.nextIndicator.hide();
33054         }
33055         
33056         this.thumbEl.scrollTo('top');
33057         
33058         this.fireEvent('update', this);
33059     },
33060     
33061     onClick : function(e)
33062     {
33063         e.preventDefault();
33064         
33065         this.fireEvent('click', this);
33066     },
33067     
33068     prev : function(e)
33069     {
33070         e.preventDefault();
33071         
33072         this.indicator = Math.max(1, this.indicator - 1);
33073         
33074         this.update();
33075     },
33076     
33077     next : function(e)
33078     {
33079         e.preventDefault();
33080         
33081         this.indicator = Math.min(this.files.length, this.indicator + 1);
33082         
33083         this.update();
33084     }
33085 });
33086 /*
33087  * - LGPL
33088  *
33089  * RadioSet
33090  *
33091  *
33092  */
33093
33094 /**
33095  * @class Roo.bootstrap.RadioSet
33096  * @extends Roo.bootstrap.Input
33097  * Bootstrap RadioSet class
33098  * @cfg {String} indicatorpos (left|right) default left
33099  * @cfg {Boolean} inline (true|false) inline the element (default true)
33100  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33101  * @constructor
33102  * Create a new RadioSet
33103  * @param {Object} config The config object
33104  */
33105
33106 Roo.bootstrap.RadioSet = function(config){
33107     
33108     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33109     
33110     this.radioes = [];
33111     
33112     Roo.bootstrap.RadioSet.register(this);
33113     
33114     this.addEvents({
33115         /**
33116         * @event check
33117         * Fires when the element is checked or unchecked.
33118         * @param {Roo.bootstrap.RadioSet} this This radio
33119         * @param {Roo.bootstrap.Radio} item The checked item
33120         */
33121        check : true
33122     });
33123     
33124 };
33125
33126 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33127
33128     radioes : false,
33129     
33130     inline : true,
33131     
33132     weight : '',
33133     
33134     indicatorpos : 'left',
33135     
33136     getAutoCreate : function()
33137     {
33138         var label = {
33139             tag : 'label',
33140             cls : 'roo-radio-set-label',
33141             cn : [
33142                 {
33143                     tag : 'span',
33144                     html : this.fieldLabel
33145                 }
33146             ]
33147         };
33148         
33149         if(this.indicatorpos == 'left'){
33150             label.cn.unshift({
33151                 tag : 'i',
33152                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33153                 tooltip : 'This field is required'
33154             });
33155         } else {
33156             label.cn.push({
33157                 tag : 'i',
33158                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33159                 tooltip : 'This field is required'
33160             });
33161         }
33162         
33163         var items = {
33164             tag : 'div',
33165             cls : 'roo-radio-set-items'
33166         };
33167         
33168         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33169         
33170         if (align === 'left' && this.fieldLabel.length) {
33171             
33172             items = {
33173                 cls : "roo-radio-set-right", 
33174                 cn: [
33175                     items
33176                 ]
33177             };
33178             
33179             if(this.labelWidth > 12){
33180                 label.style = "width: " + this.labelWidth + 'px';
33181             }
33182             
33183             if(this.labelWidth < 13 && this.labelmd == 0){
33184                 this.labelmd = this.labelWidth;
33185             }
33186             
33187             if(this.labellg > 0){
33188                 label.cls += ' col-lg-' + this.labellg;
33189                 items.cls += ' col-lg-' + (12 - this.labellg);
33190             }
33191             
33192             if(this.labelmd > 0){
33193                 label.cls += ' col-md-' + this.labelmd;
33194                 items.cls += ' col-md-' + (12 - this.labelmd);
33195             }
33196             
33197             if(this.labelsm > 0){
33198                 label.cls += ' col-sm-' + this.labelsm;
33199                 items.cls += ' col-sm-' + (12 - this.labelsm);
33200             }
33201             
33202             if(this.labelxs > 0){
33203                 label.cls += ' col-xs-' + this.labelxs;
33204                 items.cls += ' col-xs-' + (12 - this.labelxs);
33205             }
33206         }
33207         
33208         var cfg = {
33209             tag : 'div',
33210             cls : 'roo-radio-set',
33211             cn : [
33212                 {
33213                     tag : 'input',
33214                     cls : 'roo-radio-set-input',
33215                     type : 'hidden',
33216                     name : this.name,
33217                     value : this.value ? this.value :  ''
33218                 },
33219                 label,
33220                 items
33221             ]
33222         };
33223         
33224         if(this.weight.length){
33225             cfg.cls += ' roo-radio-' + this.weight;
33226         }
33227         
33228         if(this.inline) {
33229             cfg.cls += ' roo-radio-set-inline';
33230         }
33231         
33232         return cfg;
33233         
33234     },
33235
33236     initEvents : function()
33237     {
33238         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33239         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33240         
33241         if(!this.fieldLabel.length){
33242             this.labelEl.hide();
33243         }
33244         
33245         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33246         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33249         this.indicatorEl().hide();
33250         
33251         this.originalValue = this.getValue();
33252         
33253     },
33254     
33255     inputEl: function ()
33256     {
33257         return this.el.select('.roo-radio-set-input', true).first();
33258     },
33259     
33260     getChildContainer : function()
33261     {
33262         return this.itemsEl;
33263     },
33264     
33265     register : function(item)
33266     {
33267         this.radioes.push(item);
33268         
33269     },
33270     
33271     validate : function()
33272     {   
33273         var valid = false;
33274         
33275         Roo.each(this.radioes, function(i){
33276             if(!i.checked){
33277                 return;
33278             }
33279             
33280             valid = true;
33281             return false;
33282         });
33283         
33284         if(this.allowBlank) {
33285             return true;
33286         }
33287         
33288         if(this.disabled || valid){
33289             this.markValid();
33290             return true;
33291         }
33292         
33293         this.markInvalid();
33294         return false;
33295         
33296     },
33297     
33298     markValid : function()
33299     {
33300         if(this.labelEl.isVisible(true)){
33301             this.indicatorEl().hide();
33302         }
33303         
33304         this.el.removeClass([this.invalidClass, this.validClass]);
33305         this.el.addClass(this.validClass);
33306         
33307         this.fireEvent('valid', this);
33308     },
33309     
33310     markInvalid : function(msg)
33311     {
33312         if(this.allowBlank || this.disabled){
33313             return;
33314         }
33315         
33316         if(this.labelEl.isVisible(true)){
33317             this.indicatorEl().show();
33318         }
33319         
33320         this.el.removeClass([this.invalidClass, this.validClass]);
33321         this.el.addClass(this.invalidClass);
33322         
33323         this.fireEvent('invalid', this, msg);
33324         
33325     },
33326     
33327     setValue : function(v, suppressEvent)
33328     {   
33329         this.value = v;
33330         if(this.rendered){
33331             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33332         }
33333         
33334         Roo.each(this.radioes, function(i){
33335             
33336             i.checked = false;
33337             i.el.removeClass('checked');
33338             
33339             if(i.value === v || i.value.toString() === v.toString()){
33340                 i.checked = true;
33341                 i.el.addClass('checked');
33342                 
33343                 if(suppressEvent !== true){
33344                     this.fireEvent('check', this, i);
33345                 }
33346             }
33347             
33348         }, this);
33349         
33350         this.validate();
33351     },
33352     
33353     clearInvalid : function(){
33354         
33355         if(!this.el || this.preventMark){
33356             return;
33357         }
33358         
33359         this.el.removeClass([this.invalidClass]);
33360         
33361         this.fireEvent('valid', this);
33362     }
33363     
33364 });
33365
33366 Roo.apply(Roo.bootstrap.RadioSet, {
33367     
33368     groups: {},
33369     
33370     register : function(set)
33371     {
33372         this.groups[set.name] = set;
33373     },
33374     
33375     get: function(name) 
33376     {
33377         if (typeof(this.groups[name]) == 'undefined') {
33378             return false;
33379         }
33380         
33381         return this.groups[name] ;
33382     }
33383     
33384 });
33385 /*
33386  * Based on:
33387  * Ext JS Library 1.1.1
33388  * Copyright(c) 2006-2007, Ext JS, LLC.
33389  *
33390  * Originally Released Under LGPL - original licence link has changed is not relivant.
33391  *
33392  * Fork - LGPL
33393  * <script type="text/javascript">
33394  */
33395
33396
33397 /**
33398  * @class Roo.bootstrap.SplitBar
33399  * @extends Roo.util.Observable
33400  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33401  * <br><br>
33402  * Usage:
33403  * <pre><code>
33404 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33405                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33406 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33407 split.minSize = 100;
33408 split.maxSize = 600;
33409 split.animate = true;
33410 split.on('moved', splitterMoved);
33411 </code></pre>
33412  * @constructor
33413  * Create a new SplitBar
33414  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33415  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33416  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33417  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33418                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33419                         position of the SplitBar).
33420  */
33421 Roo.bootstrap.SplitBar = function(cfg){
33422     
33423     /** @private */
33424     
33425     //{
33426     //  dragElement : elm
33427     //  resizingElement: el,
33428         // optional..
33429     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33430     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33431         // existingProxy ???
33432     //}
33433     
33434     this.el = Roo.get(cfg.dragElement, true);
33435     this.el.dom.unselectable = "on";
33436     /** @private */
33437     this.resizingEl = Roo.get(cfg.resizingElement, true);
33438
33439     /**
33440      * @private
33441      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33442      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33443      * @type Number
33444      */
33445     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33446     
33447     /**
33448      * The minimum size of the resizing element. (Defaults to 0)
33449      * @type Number
33450      */
33451     this.minSize = 0;
33452     
33453     /**
33454      * The maximum size of the resizing element. (Defaults to 2000)
33455      * @type Number
33456      */
33457     this.maxSize = 2000;
33458     
33459     /**
33460      * Whether to animate the transition to the new size
33461      * @type Boolean
33462      */
33463     this.animate = false;
33464     
33465     /**
33466      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33467      * @type Boolean
33468      */
33469     this.useShim = false;
33470     
33471     /** @private */
33472     this.shim = null;
33473     
33474     if(!cfg.existingProxy){
33475         /** @private */
33476         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33477     }else{
33478         this.proxy = Roo.get(cfg.existingProxy).dom;
33479     }
33480     /** @private */
33481     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33482     
33483     /** @private */
33484     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33485     
33486     /** @private */
33487     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33488     
33489     /** @private */
33490     this.dragSpecs = {};
33491     
33492     /**
33493      * @private The adapter to use to positon and resize elements
33494      */
33495     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33496     this.adapter.init(this);
33497     
33498     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33499         /** @private */
33500         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33501         this.el.addClass("roo-splitbar-h");
33502     }else{
33503         /** @private */
33504         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33505         this.el.addClass("roo-splitbar-v");
33506     }
33507     
33508     this.addEvents({
33509         /**
33510          * @event resize
33511          * Fires when the splitter is moved (alias for {@link #event-moved})
33512          * @param {Roo.bootstrap.SplitBar} this
33513          * @param {Number} newSize the new width or height
33514          */
33515         "resize" : true,
33516         /**
33517          * @event moved
33518          * Fires when the splitter is moved
33519          * @param {Roo.bootstrap.SplitBar} this
33520          * @param {Number} newSize the new width or height
33521          */
33522         "moved" : true,
33523         /**
33524          * @event beforeresize
33525          * Fires before the splitter is dragged
33526          * @param {Roo.bootstrap.SplitBar} this
33527          */
33528         "beforeresize" : true,
33529
33530         "beforeapply" : true
33531     });
33532
33533     Roo.util.Observable.call(this);
33534 };
33535
33536 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33537     onStartProxyDrag : function(x, y){
33538         this.fireEvent("beforeresize", this);
33539         if(!this.overlay){
33540             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33541             o.unselectable();
33542             o.enableDisplayMode("block");
33543             // all splitbars share the same overlay
33544             Roo.bootstrap.SplitBar.prototype.overlay = o;
33545         }
33546         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33547         this.overlay.show();
33548         Roo.get(this.proxy).setDisplayed("block");
33549         var size = this.adapter.getElementSize(this);
33550         this.activeMinSize = this.getMinimumSize();;
33551         this.activeMaxSize = this.getMaximumSize();;
33552         var c1 = size - this.activeMinSize;
33553         var c2 = Math.max(this.activeMaxSize - size, 0);
33554         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33555             this.dd.resetConstraints();
33556             this.dd.setXConstraint(
33557                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33558                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33559             );
33560             this.dd.setYConstraint(0, 0);
33561         }else{
33562             this.dd.resetConstraints();
33563             this.dd.setXConstraint(0, 0);
33564             this.dd.setYConstraint(
33565                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33566                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33567             );
33568          }
33569         this.dragSpecs.startSize = size;
33570         this.dragSpecs.startPoint = [x, y];
33571         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33572     },
33573     
33574     /** 
33575      * @private Called after the drag operation by the DDProxy
33576      */
33577     onEndProxyDrag : function(e){
33578         Roo.get(this.proxy).setDisplayed(false);
33579         var endPoint = Roo.lib.Event.getXY(e);
33580         if(this.overlay){
33581             this.overlay.hide();
33582         }
33583         var newSize;
33584         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33585             newSize = this.dragSpecs.startSize + 
33586                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33587                     endPoint[0] - this.dragSpecs.startPoint[0] :
33588                     this.dragSpecs.startPoint[0] - endPoint[0]
33589                 );
33590         }else{
33591             newSize = this.dragSpecs.startSize + 
33592                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33593                     endPoint[1] - this.dragSpecs.startPoint[1] :
33594                     this.dragSpecs.startPoint[1] - endPoint[1]
33595                 );
33596         }
33597         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33598         if(newSize != this.dragSpecs.startSize){
33599             if(this.fireEvent('beforeapply', this, newSize) !== false){
33600                 this.adapter.setElementSize(this, newSize);
33601                 this.fireEvent("moved", this, newSize);
33602                 this.fireEvent("resize", this, newSize);
33603             }
33604         }
33605     },
33606     
33607     /**
33608      * Get the adapter this SplitBar uses
33609      * @return The adapter object
33610      */
33611     getAdapter : function(){
33612         return this.adapter;
33613     },
33614     
33615     /**
33616      * Set the adapter this SplitBar uses
33617      * @param {Object} adapter A SplitBar adapter object
33618      */
33619     setAdapter : function(adapter){
33620         this.adapter = adapter;
33621         this.adapter.init(this);
33622     },
33623     
33624     /**
33625      * Gets the minimum size for the resizing element
33626      * @return {Number} The minimum size
33627      */
33628     getMinimumSize : function(){
33629         return this.minSize;
33630     },
33631     
33632     /**
33633      * Sets the minimum size for the resizing element
33634      * @param {Number} minSize The minimum size
33635      */
33636     setMinimumSize : function(minSize){
33637         this.minSize = minSize;
33638     },
33639     
33640     /**
33641      * Gets the maximum size for the resizing element
33642      * @return {Number} The maximum size
33643      */
33644     getMaximumSize : function(){
33645         return this.maxSize;
33646     },
33647     
33648     /**
33649      * Sets the maximum size for the resizing element
33650      * @param {Number} maxSize The maximum size
33651      */
33652     setMaximumSize : function(maxSize){
33653         this.maxSize = maxSize;
33654     },
33655     
33656     /**
33657      * Sets the initialize size for the resizing element
33658      * @param {Number} size The initial size
33659      */
33660     setCurrentSize : function(size){
33661         var oldAnimate = this.animate;
33662         this.animate = false;
33663         this.adapter.setElementSize(this, size);
33664         this.animate = oldAnimate;
33665     },
33666     
33667     /**
33668      * Destroy this splitbar. 
33669      * @param {Boolean} removeEl True to remove the element
33670      */
33671     destroy : function(removeEl){
33672         if(this.shim){
33673             this.shim.remove();
33674         }
33675         this.dd.unreg();
33676         this.proxy.parentNode.removeChild(this.proxy);
33677         if(removeEl){
33678             this.el.remove();
33679         }
33680     }
33681 });
33682
33683 /**
33684  * @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.
33685  */
33686 Roo.bootstrap.SplitBar.createProxy = function(dir){
33687     var proxy = new Roo.Element(document.createElement("div"));
33688     proxy.unselectable();
33689     var cls = 'roo-splitbar-proxy';
33690     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33691     document.body.appendChild(proxy.dom);
33692     return proxy.dom;
33693 };
33694
33695 /** 
33696  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33697  * Default Adapter. It assumes the splitter and resizing element are not positioned
33698  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33699  */
33700 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33701 };
33702
33703 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33704     // do nothing for now
33705     init : function(s){
33706     
33707     },
33708     /**
33709      * Called before drag operations to get the current size of the resizing element. 
33710      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33711      */
33712      getElementSize : function(s){
33713         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33714             return s.resizingEl.getWidth();
33715         }else{
33716             return s.resizingEl.getHeight();
33717         }
33718     },
33719     
33720     /**
33721      * Called after drag operations to set the size of the resizing element.
33722      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33723      * @param {Number} newSize The new size to set
33724      * @param {Function} onComplete A function to be invoked when resizing is complete
33725      */
33726     setElementSize : function(s, newSize, onComplete){
33727         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33728             if(!s.animate){
33729                 s.resizingEl.setWidth(newSize);
33730                 if(onComplete){
33731                     onComplete(s, newSize);
33732                 }
33733             }else{
33734                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33735             }
33736         }else{
33737             
33738             if(!s.animate){
33739                 s.resizingEl.setHeight(newSize);
33740                 if(onComplete){
33741                     onComplete(s, newSize);
33742                 }
33743             }else{
33744                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33745             }
33746         }
33747     }
33748 };
33749
33750 /** 
33751  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33752  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33753  * Adapter that  moves the splitter element to align with the resized sizing element. 
33754  * Used with an absolute positioned SplitBar.
33755  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33756  * document.body, make sure you assign an id to the body element.
33757  */
33758 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33759     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33760     this.container = Roo.get(container);
33761 };
33762
33763 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33764     init : function(s){
33765         this.basic.init(s);
33766     },
33767     
33768     getElementSize : function(s){
33769         return this.basic.getElementSize(s);
33770     },
33771     
33772     setElementSize : function(s, newSize, onComplete){
33773         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33774     },
33775     
33776     moveSplitter : function(s){
33777         var yes = Roo.bootstrap.SplitBar;
33778         switch(s.placement){
33779             case yes.LEFT:
33780                 s.el.setX(s.resizingEl.getRight());
33781                 break;
33782             case yes.RIGHT:
33783                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33784                 break;
33785             case yes.TOP:
33786                 s.el.setY(s.resizingEl.getBottom());
33787                 break;
33788             case yes.BOTTOM:
33789                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33790                 break;
33791         }
33792     }
33793 };
33794
33795 /**
33796  * Orientation constant - Create a vertical SplitBar
33797  * @static
33798  * @type Number
33799  */
33800 Roo.bootstrap.SplitBar.VERTICAL = 1;
33801
33802 /**
33803  * Orientation constant - Create a horizontal SplitBar
33804  * @static
33805  * @type Number
33806  */
33807 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33808
33809 /**
33810  * Placement constant - The resizing element is to the left of the splitter element
33811  * @static
33812  * @type Number
33813  */
33814 Roo.bootstrap.SplitBar.LEFT = 1;
33815
33816 /**
33817  * Placement constant - The resizing element is to the right of the splitter element
33818  * @static
33819  * @type Number
33820  */
33821 Roo.bootstrap.SplitBar.RIGHT = 2;
33822
33823 /**
33824  * Placement constant - The resizing element is positioned above the splitter element
33825  * @static
33826  * @type Number
33827  */
33828 Roo.bootstrap.SplitBar.TOP = 3;
33829
33830 /**
33831  * Placement constant - The resizing element is positioned under splitter element
33832  * @static
33833  * @type Number
33834  */
33835 Roo.bootstrap.SplitBar.BOTTOM = 4;
33836 Roo.namespace("Roo.bootstrap.layout");/*
33837  * Based on:
33838  * Ext JS Library 1.1.1
33839  * Copyright(c) 2006-2007, Ext JS, LLC.
33840  *
33841  * Originally Released Under LGPL - original licence link has changed is not relivant.
33842  *
33843  * Fork - LGPL
33844  * <script type="text/javascript">
33845  */
33846
33847 /**
33848  * @class Roo.bootstrap.layout.Manager
33849  * @extends Roo.bootstrap.Component
33850  * Base class for layout managers.
33851  */
33852 Roo.bootstrap.layout.Manager = function(config)
33853 {
33854     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33855
33856
33857
33858
33859
33860     /** false to disable window resize monitoring @type Boolean */
33861     this.monitorWindowResize = true;
33862     this.regions = {};
33863     this.addEvents({
33864         /**
33865          * @event layout
33866          * Fires when a layout is performed.
33867          * @param {Roo.LayoutManager} this
33868          */
33869         "layout" : true,
33870         /**
33871          * @event regionresized
33872          * Fires when the user resizes a region.
33873          * @param {Roo.LayoutRegion} region The resized region
33874          * @param {Number} newSize The new size (width for east/west, height for north/south)
33875          */
33876         "regionresized" : true,
33877         /**
33878          * @event regioncollapsed
33879          * Fires when a region is collapsed.
33880          * @param {Roo.LayoutRegion} region The collapsed region
33881          */
33882         "regioncollapsed" : true,
33883         /**
33884          * @event regionexpanded
33885          * Fires when a region is expanded.
33886          * @param {Roo.LayoutRegion} region The expanded region
33887          */
33888         "regionexpanded" : true
33889     });
33890     this.updating = false;
33891
33892     if (config.el) {
33893         this.el = Roo.get(config.el);
33894         this.initEvents();
33895     }
33896
33897 };
33898
33899 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33900
33901
33902     regions : null,
33903
33904     monitorWindowResize : true,
33905
33906
33907     updating : false,
33908
33909
33910     onRender : function(ct, position)
33911     {
33912         if(!this.el){
33913             this.el = Roo.get(ct);
33914             this.initEvents();
33915         }
33916         //this.fireEvent('render',this);
33917     },
33918
33919
33920     initEvents: function()
33921     {
33922
33923
33924         // ie scrollbar fix
33925         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33926             document.body.scroll = "no";
33927         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33928             this.el.position('relative');
33929         }
33930         this.id = this.el.id;
33931         this.el.addClass("roo-layout-container");
33932         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33933         if(this.el.dom != document.body ) {
33934             this.el.on('resize', this.layout,this);
33935             this.el.on('show', this.layout,this);
33936         }
33937
33938     },
33939
33940     /**
33941      * Returns true if this layout is currently being updated
33942      * @return {Boolean}
33943      */
33944     isUpdating : function(){
33945         return this.updating;
33946     },
33947
33948     /**
33949      * Suspend the LayoutManager from doing auto-layouts while
33950      * making multiple add or remove calls
33951      */
33952     beginUpdate : function(){
33953         this.updating = true;
33954     },
33955
33956     /**
33957      * Restore auto-layouts and optionally disable the manager from performing a layout
33958      * @param {Boolean} noLayout true to disable a layout update
33959      */
33960     endUpdate : function(noLayout){
33961         this.updating = false;
33962         if(!noLayout){
33963             this.layout();
33964         }
33965     },
33966
33967     layout: function(){
33968         // abstract...
33969     },
33970
33971     onRegionResized : function(region, newSize){
33972         this.fireEvent("regionresized", region, newSize);
33973         this.layout();
33974     },
33975
33976     onRegionCollapsed : function(region){
33977         this.fireEvent("regioncollapsed", region);
33978     },
33979
33980     onRegionExpanded : function(region){
33981         this.fireEvent("regionexpanded", region);
33982     },
33983
33984     /**
33985      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33986      * performs box-model adjustments.
33987      * @return {Object} The size as an object {width: (the width), height: (the height)}
33988      */
33989     getViewSize : function()
33990     {
33991         var size;
33992         if(this.el.dom != document.body){
33993             size = this.el.getSize();
33994         }else{
33995             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33996         }
33997         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33998         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33999         return size;
34000     },
34001
34002     /**
34003      * Returns the Element this layout is bound to.
34004      * @return {Roo.Element}
34005      */
34006     getEl : function(){
34007         return this.el;
34008     },
34009
34010     /**
34011      * Returns the specified region.
34012      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34013      * @return {Roo.LayoutRegion}
34014      */
34015     getRegion : function(target){
34016         return this.regions[target.toLowerCase()];
34017     },
34018
34019     onWindowResize : function(){
34020         if(this.monitorWindowResize){
34021             this.layout();
34022         }
34023     }
34024 });
34025 /*
34026  * Based on:
34027  * Ext JS Library 1.1.1
34028  * Copyright(c) 2006-2007, Ext JS, LLC.
34029  *
34030  * Originally Released Under LGPL - original licence link has changed is not relivant.
34031  *
34032  * Fork - LGPL
34033  * <script type="text/javascript">
34034  */
34035 /**
34036  * @class Roo.bootstrap.layout.Border
34037  * @extends Roo.bootstrap.layout.Manager
34038  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34039  * please see: examples/bootstrap/nested.html<br><br>
34040  
34041 <b>The container the layout is rendered into can be either the body element or any other element.
34042 If it is not the body element, the container needs to either be an absolute positioned element,
34043 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34044 the container size if it is not the body element.</b>
34045
34046 * @constructor
34047 * Create a new Border
34048 * @param {Object} config Configuration options
34049  */
34050 Roo.bootstrap.layout.Border = function(config){
34051     config = config || {};
34052     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34053     
34054     
34055     
34056     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34057         if(config[region]){
34058             config[region].region = region;
34059             this.addRegion(config[region]);
34060         }
34061     },this);
34062     
34063 };
34064
34065 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34066
34067 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34068     /**
34069      * Creates and adds a new region if it doesn't already exist.
34070      * @param {String} target The target region key (north, south, east, west or center).
34071      * @param {Object} config The regions config object
34072      * @return {BorderLayoutRegion} The new region
34073      */
34074     addRegion : function(config)
34075     {
34076         if(!this.regions[config.region]){
34077             var r = this.factory(config);
34078             this.bindRegion(r);
34079         }
34080         return this.regions[config.region];
34081     },
34082
34083     // private (kinda)
34084     bindRegion : function(r){
34085         this.regions[r.config.region] = r;
34086         
34087         r.on("visibilitychange",    this.layout, this);
34088         r.on("paneladded",          this.layout, this);
34089         r.on("panelremoved",        this.layout, this);
34090         r.on("invalidated",         this.layout, this);
34091         r.on("resized",             this.onRegionResized, this);
34092         r.on("collapsed",           this.onRegionCollapsed, this);
34093         r.on("expanded",            this.onRegionExpanded, this);
34094     },
34095
34096     /**
34097      * Performs a layout update.
34098      */
34099     layout : function()
34100     {
34101         if(this.updating) {
34102             return;
34103         }
34104         
34105         // render all the rebions if they have not been done alreayd?
34106         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34107             if(this.regions[region] && !this.regions[region].bodyEl){
34108                 this.regions[region].onRender(this.el)
34109             }
34110         },this);
34111         
34112         var size = this.getViewSize();
34113         var w = size.width;
34114         var h = size.height;
34115         var centerW = w;
34116         var centerH = h;
34117         var centerY = 0;
34118         var centerX = 0;
34119         //var x = 0, y = 0;
34120
34121         var rs = this.regions;
34122         var north = rs["north"];
34123         var south = rs["south"]; 
34124         var west = rs["west"];
34125         var east = rs["east"];
34126         var center = rs["center"];
34127         //if(this.hideOnLayout){ // not supported anymore
34128             //c.el.setStyle("display", "none");
34129         //}
34130         if(north && north.isVisible()){
34131             var b = north.getBox();
34132             var m = north.getMargins();
34133             b.width = w - (m.left+m.right);
34134             b.x = m.left;
34135             b.y = m.top;
34136             centerY = b.height + b.y + m.bottom;
34137             centerH -= centerY;
34138             north.updateBox(this.safeBox(b));
34139         }
34140         if(south && south.isVisible()){
34141             var b = south.getBox();
34142             var m = south.getMargins();
34143             b.width = w - (m.left+m.right);
34144             b.x = m.left;
34145             var totalHeight = (b.height + m.top + m.bottom);
34146             b.y = h - totalHeight + m.top;
34147             centerH -= totalHeight;
34148             south.updateBox(this.safeBox(b));
34149         }
34150         if(west && west.isVisible()){
34151             var b = west.getBox();
34152             var m = west.getMargins();
34153             b.height = centerH - (m.top+m.bottom);
34154             b.x = m.left;
34155             b.y = centerY + m.top;
34156             var totalWidth = (b.width + m.left + m.right);
34157             centerX += totalWidth;
34158             centerW -= totalWidth;
34159             west.updateBox(this.safeBox(b));
34160         }
34161         if(east && east.isVisible()){
34162             var b = east.getBox();
34163             var m = east.getMargins();
34164             b.height = centerH - (m.top+m.bottom);
34165             var totalWidth = (b.width + m.left + m.right);
34166             b.x = w - totalWidth + m.left;
34167             b.y = centerY + m.top;
34168             centerW -= totalWidth;
34169             east.updateBox(this.safeBox(b));
34170         }
34171         if(center){
34172             var m = center.getMargins();
34173             var centerBox = {
34174                 x: centerX + m.left,
34175                 y: centerY + m.top,
34176                 width: centerW - (m.left+m.right),
34177                 height: centerH - (m.top+m.bottom)
34178             };
34179             //if(this.hideOnLayout){
34180                 //center.el.setStyle("display", "block");
34181             //}
34182             center.updateBox(this.safeBox(centerBox));
34183         }
34184         this.el.repaint();
34185         this.fireEvent("layout", this);
34186     },
34187
34188     // private
34189     safeBox : function(box){
34190         box.width = Math.max(0, box.width);
34191         box.height = Math.max(0, box.height);
34192         return box;
34193     },
34194
34195     /**
34196      * Adds a ContentPanel (or subclass) to this layout.
34197      * @param {String} target The target region key (north, south, east, west or center).
34198      * @param {Roo.ContentPanel} panel The panel to add
34199      * @return {Roo.ContentPanel} The added panel
34200      */
34201     add : function(target, panel){
34202          
34203         target = target.toLowerCase();
34204         return this.regions[target].add(panel);
34205     },
34206
34207     /**
34208      * Remove a ContentPanel (or subclass) to this layout.
34209      * @param {String} target The target region key (north, south, east, west or center).
34210      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34211      * @return {Roo.ContentPanel} The removed panel
34212      */
34213     remove : function(target, panel){
34214         target = target.toLowerCase();
34215         return this.regions[target].remove(panel);
34216     },
34217
34218     /**
34219      * Searches all regions for a panel with the specified id
34220      * @param {String} panelId
34221      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34222      */
34223     findPanel : function(panelId){
34224         var rs = this.regions;
34225         for(var target in rs){
34226             if(typeof rs[target] != "function"){
34227                 var p = rs[target].getPanel(panelId);
34228                 if(p){
34229                     return p;
34230                 }
34231             }
34232         }
34233         return null;
34234     },
34235
34236     /**
34237      * Searches all regions for a panel with the specified id and activates (shows) it.
34238      * @param {String/ContentPanel} panelId The panels id or the panel itself
34239      * @return {Roo.ContentPanel} The shown panel or null
34240      */
34241     showPanel : function(panelId) {
34242       var rs = this.regions;
34243       for(var target in rs){
34244          var r = rs[target];
34245          if(typeof r != "function"){
34246             if(r.hasPanel(panelId)){
34247                return r.showPanel(panelId);
34248             }
34249          }
34250       }
34251       return null;
34252    },
34253
34254    /**
34255      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34256      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34257      */
34258    /*
34259     restoreState : function(provider){
34260         if(!provider){
34261             provider = Roo.state.Manager;
34262         }
34263         var sm = new Roo.LayoutStateManager();
34264         sm.init(this, provider);
34265     },
34266 */
34267  
34268  
34269     /**
34270      * Adds a xtype elements to the layout.
34271      * <pre><code>
34272
34273 layout.addxtype({
34274        xtype : 'ContentPanel',
34275        region: 'west',
34276        items: [ .... ]
34277    }
34278 );
34279
34280 layout.addxtype({
34281         xtype : 'NestedLayoutPanel',
34282         region: 'west',
34283         layout: {
34284            center: { },
34285            west: { }   
34286         },
34287         items : [ ... list of content panels or nested layout panels.. ]
34288    }
34289 );
34290 </code></pre>
34291      * @param {Object} cfg Xtype definition of item to add.
34292      */
34293     addxtype : function(cfg)
34294     {
34295         // basically accepts a pannel...
34296         // can accept a layout region..!?!?
34297         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34298         
34299         
34300         // theory?  children can only be panels??
34301         
34302         //if (!cfg.xtype.match(/Panel$/)) {
34303         //    return false;
34304         //}
34305         var ret = false;
34306         
34307         if (typeof(cfg.region) == 'undefined') {
34308             Roo.log("Failed to add Panel, region was not set");
34309             Roo.log(cfg);
34310             return false;
34311         }
34312         var region = cfg.region;
34313         delete cfg.region;
34314         
34315           
34316         var xitems = [];
34317         if (cfg.items) {
34318             xitems = cfg.items;
34319             delete cfg.items;
34320         }
34321         var nb = false;
34322         
34323         switch(cfg.xtype) 
34324         {
34325             case 'Content':  // ContentPanel (el, cfg)
34326             case 'Scroll':  // ContentPanel (el, cfg)
34327             case 'View': 
34328                 cfg.autoCreate = true;
34329                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34330                 //} else {
34331                 //    var el = this.el.createChild();
34332                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34333                 //}
34334                 
34335                 this.add(region, ret);
34336                 break;
34337             
34338             /*
34339             case 'TreePanel': // our new panel!
34340                 cfg.el = this.el.createChild();
34341                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34342                 this.add(region, ret);
34343                 break;
34344             */
34345             
34346             case 'Nest': 
34347                 // create a new Layout (which is  a Border Layout...
34348                 
34349                 var clayout = cfg.layout;
34350                 clayout.el  = this.el.createChild();
34351                 clayout.items   = clayout.items  || [];
34352                 
34353                 delete cfg.layout;
34354                 
34355                 // replace this exitems with the clayout ones..
34356                 xitems = clayout.items;
34357                  
34358                 // force background off if it's in center...
34359                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34360                     cfg.background = false;
34361                 }
34362                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34363                 
34364                 
34365                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34366                 //console.log('adding nested layout panel '  + cfg.toSource());
34367                 this.add(region, ret);
34368                 nb = {}; /// find first...
34369                 break;
34370             
34371             case 'Grid':
34372                 
34373                 // needs grid and region
34374                 
34375                 //var el = this.getRegion(region).el.createChild();
34376                 /*
34377                  *var el = this.el.createChild();
34378                 // create the grid first...
34379                 cfg.grid.container = el;
34380                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34381                 */
34382                 
34383                 if (region == 'center' && this.active ) {
34384                     cfg.background = false;
34385                 }
34386                 
34387                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34388                 
34389                 this.add(region, ret);
34390                 /*
34391                 if (cfg.background) {
34392                     // render grid on panel activation (if panel background)
34393                     ret.on('activate', function(gp) {
34394                         if (!gp.grid.rendered) {
34395                     //        gp.grid.render(el);
34396                         }
34397                     });
34398                 } else {
34399                   //  cfg.grid.render(el);
34400                 }
34401                 */
34402                 break;
34403            
34404            
34405             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34406                 // it was the old xcomponent building that caused this before.
34407                 // espeically if border is the top element in the tree.
34408                 ret = this;
34409                 break; 
34410                 
34411                     
34412                 
34413                 
34414                 
34415             default:
34416                 /*
34417                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34418                     
34419                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34420                     this.add(region, ret);
34421                 } else {
34422                 */
34423                     Roo.log(cfg);
34424                     throw "Can not add '" + cfg.xtype + "' to Border";
34425                     return null;
34426              
34427                                 
34428              
34429         }
34430         this.beginUpdate();
34431         // add children..
34432         var region = '';
34433         var abn = {};
34434         Roo.each(xitems, function(i)  {
34435             region = nb && i.region ? i.region : false;
34436             
34437             var add = ret.addxtype(i);
34438            
34439             if (region) {
34440                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34441                 if (!i.background) {
34442                     abn[region] = nb[region] ;
34443                 }
34444             }
34445             
34446         });
34447         this.endUpdate();
34448
34449         // make the last non-background panel active..
34450         //if (nb) { Roo.log(abn); }
34451         if (nb) {
34452             
34453             for(var r in abn) {
34454                 region = this.getRegion(r);
34455                 if (region) {
34456                     // tried using nb[r], but it does not work..
34457                      
34458                     region.showPanel(abn[r]);
34459                    
34460                 }
34461             }
34462         }
34463         return ret;
34464         
34465     },
34466     
34467     
34468 // private
34469     factory : function(cfg)
34470     {
34471         
34472         var validRegions = Roo.bootstrap.layout.Border.regions;
34473
34474         var target = cfg.region;
34475         cfg.mgr = this;
34476         
34477         var r = Roo.bootstrap.layout;
34478         Roo.log(target);
34479         switch(target){
34480             case "north":
34481                 return new r.North(cfg);
34482             case "south":
34483                 return new r.South(cfg);
34484             case "east":
34485                 return new r.East(cfg);
34486             case "west":
34487                 return new r.West(cfg);
34488             case "center":
34489                 return new r.Center(cfg);
34490         }
34491         throw 'Layout region "'+target+'" not supported.';
34492     }
34493     
34494     
34495 });
34496  /*
34497  * Based on:
34498  * Ext JS Library 1.1.1
34499  * Copyright(c) 2006-2007, Ext JS, LLC.
34500  *
34501  * Originally Released Under LGPL - original licence link has changed is not relivant.
34502  *
34503  * Fork - LGPL
34504  * <script type="text/javascript">
34505  */
34506  
34507 /**
34508  * @class Roo.bootstrap.layout.Basic
34509  * @extends Roo.util.Observable
34510  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34511  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34512  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34513  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34514  * @cfg {string}   region  the region that it inhabits..
34515  * @cfg {bool}   skipConfig skip config?
34516  * 
34517
34518  */
34519 Roo.bootstrap.layout.Basic = function(config){
34520     
34521     this.mgr = config.mgr;
34522     
34523     this.position = config.region;
34524     
34525     var skipConfig = config.skipConfig;
34526     
34527     this.events = {
34528         /**
34529          * @scope Roo.BasicLayoutRegion
34530          */
34531         
34532         /**
34533          * @event beforeremove
34534          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34535          * @param {Roo.LayoutRegion} this
34536          * @param {Roo.ContentPanel} panel The panel
34537          * @param {Object} e The cancel event object
34538          */
34539         "beforeremove" : true,
34540         /**
34541          * @event invalidated
34542          * Fires when the layout for this region is changed.
34543          * @param {Roo.LayoutRegion} this
34544          */
34545         "invalidated" : true,
34546         /**
34547          * @event visibilitychange
34548          * Fires when this region is shown or hidden 
34549          * @param {Roo.LayoutRegion} this
34550          * @param {Boolean} visibility true or false
34551          */
34552         "visibilitychange" : true,
34553         /**
34554          * @event paneladded
34555          * Fires when a panel is added. 
34556          * @param {Roo.LayoutRegion} this
34557          * @param {Roo.ContentPanel} panel The panel
34558          */
34559         "paneladded" : true,
34560         /**
34561          * @event panelremoved
34562          * Fires when a panel is removed. 
34563          * @param {Roo.LayoutRegion} this
34564          * @param {Roo.ContentPanel} panel The panel
34565          */
34566         "panelremoved" : true,
34567         /**
34568          * @event beforecollapse
34569          * Fires when this region before collapse.
34570          * @param {Roo.LayoutRegion} this
34571          */
34572         "beforecollapse" : true,
34573         /**
34574          * @event collapsed
34575          * Fires when this region is collapsed.
34576          * @param {Roo.LayoutRegion} this
34577          */
34578         "collapsed" : true,
34579         /**
34580          * @event expanded
34581          * Fires when this region is expanded.
34582          * @param {Roo.LayoutRegion} this
34583          */
34584         "expanded" : true,
34585         /**
34586          * @event slideshow
34587          * Fires when this region is slid into view.
34588          * @param {Roo.LayoutRegion} this
34589          */
34590         "slideshow" : true,
34591         /**
34592          * @event slidehide
34593          * Fires when this region slides out of view. 
34594          * @param {Roo.LayoutRegion} this
34595          */
34596         "slidehide" : true,
34597         /**
34598          * @event panelactivated
34599          * Fires when a panel is activated. 
34600          * @param {Roo.LayoutRegion} this
34601          * @param {Roo.ContentPanel} panel The activated panel
34602          */
34603         "panelactivated" : true,
34604         /**
34605          * @event resized
34606          * Fires when the user resizes this region. 
34607          * @param {Roo.LayoutRegion} this
34608          * @param {Number} newSize The new size (width for east/west, height for north/south)
34609          */
34610         "resized" : true
34611     };
34612     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34613     this.panels = new Roo.util.MixedCollection();
34614     this.panels.getKey = this.getPanelId.createDelegate(this);
34615     this.box = null;
34616     this.activePanel = null;
34617     // ensure listeners are added...
34618     
34619     if (config.listeners || config.events) {
34620         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34621             listeners : config.listeners || {},
34622             events : config.events || {}
34623         });
34624     }
34625     
34626     if(skipConfig !== true){
34627         this.applyConfig(config);
34628     }
34629 };
34630
34631 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34632 {
34633     getPanelId : function(p){
34634         return p.getId();
34635     },
34636     
34637     applyConfig : function(config){
34638         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34639         this.config = config;
34640         
34641     },
34642     
34643     /**
34644      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34645      * the width, for horizontal (north, south) the height.
34646      * @param {Number} newSize The new width or height
34647      */
34648     resizeTo : function(newSize){
34649         var el = this.el ? this.el :
34650                  (this.activePanel ? this.activePanel.getEl() : null);
34651         if(el){
34652             switch(this.position){
34653                 case "east":
34654                 case "west":
34655                     el.setWidth(newSize);
34656                     this.fireEvent("resized", this, newSize);
34657                 break;
34658                 case "north":
34659                 case "south":
34660                     el.setHeight(newSize);
34661                     this.fireEvent("resized", this, newSize);
34662                 break;                
34663             }
34664         }
34665     },
34666     
34667     getBox : function(){
34668         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34669     },
34670     
34671     getMargins : function(){
34672         return this.margins;
34673     },
34674     
34675     updateBox : function(box){
34676         this.box = box;
34677         var el = this.activePanel.getEl();
34678         el.dom.style.left = box.x + "px";
34679         el.dom.style.top = box.y + "px";
34680         this.activePanel.setSize(box.width, box.height);
34681     },
34682     
34683     /**
34684      * Returns the container element for this region.
34685      * @return {Roo.Element}
34686      */
34687     getEl : function(){
34688         return this.activePanel;
34689     },
34690     
34691     /**
34692      * Returns true if this region is currently visible.
34693      * @return {Boolean}
34694      */
34695     isVisible : function(){
34696         return this.activePanel ? true : false;
34697     },
34698     
34699     setActivePanel : function(panel){
34700         panel = this.getPanel(panel);
34701         if(this.activePanel && this.activePanel != panel){
34702             this.activePanel.setActiveState(false);
34703             this.activePanel.getEl().setLeftTop(-10000,-10000);
34704         }
34705         this.activePanel = panel;
34706         panel.setActiveState(true);
34707         if(this.box){
34708             panel.setSize(this.box.width, this.box.height);
34709         }
34710         this.fireEvent("panelactivated", this, panel);
34711         this.fireEvent("invalidated");
34712     },
34713     
34714     /**
34715      * Show the specified panel.
34716      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34717      * @return {Roo.ContentPanel} The shown panel or null
34718      */
34719     showPanel : function(panel){
34720         panel = this.getPanel(panel);
34721         if(panel){
34722             this.setActivePanel(panel);
34723         }
34724         return panel;
34725     },
34726     
34727     /**
34728      * Get the active panel for this region.
34729      * @return {Roo.ContentPanel} The active panel or null
34730      */
34731     getActivePanel : function(){
34732         return this.activePanel;
34733     },
34734     
34735     /**
34736      * Add the passed ContentPanel(s)
34737      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34738      * @return {Roo.ContentPanel} The panel added (if only one was added)
34739      */
34740     add : function(panel){
34741         if(arguments.length > 1){
34742             for(var i = 0, len = arguments.length; i < len; i++) {
34743                 this.add(arguments[i]);
34744             }
34745             return null;
34746         }
34747         if(this.hasPanel(panel)){
34748             this.showPanel(panel);
34749             return panel;
34750         }
34751         var el = panel.getEl();
34752         if(el.dom.parentNode != this.mgr.el.dom){
34753             this.mgr.el.dom.appendChild(el.dom);
34754         }
34755         if(panel.setRegion){
34756             panel.setRegion(this);
34757         }
34758         this.panels.add(panel);
34759         el.setStyle("position", "absolute");
34760         if(!panel.background){
34761             this.setActivePanel(panel);
34762             if(this.config.initialSize && this.panels.getCount()==1){
34763                 this.resizeTo(this.config.initialSize);
34764             }
34765         }
34766         this.fireEvent("paneladded", this, panel);
34767         return panel;
34768     },
34769     
34770     /**
34771      * Returns true if the panel is in this region.
34772      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34773      * @return {Boolean}
34774      */
34775     hasPanel : function(panel){
34776         if(typeof panel == "object"){ // must be panel obj
34777             panel = panel.getId();
34778         }
34779         return this.getPanel(panel) ? true : false;
34780     },
34781     
34782     /**
34783      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34784      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34785      * @param {Boolean} preservePanel Overrides the config preservePanel option
34786      * @return {Roo.ContentPanel} The panel that was removed
34787      */
34788     remove : function(panel, preservePanel){
34789         panel = this.getPanel(panel);
34790         if(!panel){
34791             return null;
34792         }
34793         var e = {};
34794         this.fireEvent("beforeremove", this, panel, e);
34795         if(e.cancel === true){
34796             return null;
34797         }
34798         var panelId = panel.getId();
34799         this.panels.removeKey(panelId);
34800         return panel;
34801     },
34802     
34803     /**
34804      * Returns the panel specified or null if it's not in this region.
34805      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34806      * @return {Roo.ContentPanel}
34807      */
34808     getPanel : function(id){
34809         if(typeof id == "object"){ // must be panel obj
34810             return id;
34811         }
34812         return this.panels.get(id);
34813     },
34814     
34815     /**
34816      * Returns this regions position (north/south/east/west/center).
34817      * @return {String} 
34818      */
34819     getPosition: function(){
34820         return this.position;    
34821     }
34822 });/*
34823  * Based on:
34824  * Ext JS Library 1.1.1
34825  * Copyright(c) 2006-2007, Ext JS, LLC.
34826  *
34827  * Originally Released Under LGPL - original licence link has changed is not relivant.
34828  *
34829  * Fork - LGPL
34830  * <script type="text/javascript">
34831  */
34832  
34833 /**
34834  * @class Roo.bootstrap.layout.Region
34835  * @extends Roo.bootstrap.layout.Basic
34836  * This class represents a region in a layout manager.
34837  
34838  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34839  * @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})
34840  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34841  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34842  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34843  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34844  * @cfg {String}    title           The title for the region (overrides panel titles)
34845  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34846  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34847  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34848  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34849  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34850  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34851  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34852  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34853  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34854  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34855
34856  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34857  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34858  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34859  * @cfg {Number}    width           For East/West panels
34860  * @cfg {Number}    height          For North/South panels
34861  * @cfg {Boolean}   split           To show the splitter
34862  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34863  * 
34864  * @cfg {string}   cls             Extra CSS classes to add to region
34865  * 
34866  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34867  * @cfg {string}   region  the region that it inhabits..
34868  *
34869
34870  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34871  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34872
34873  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34874  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34875  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34876  */
34877 Roo.bootstrap.layout.Region = function(config)
34878 {
34879     this.applyConfig(config);
34880
34881     var mgr = config.mgr;
34882     var pos = config.region;
34883     config.skipConfig = true;
34884     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34885     
34886     if (mgr.el) {
34887         this.onRender(mgr.el);   
34888     }
34889      
34890     this.visible = true;
34891     this.collapsed = false;
34892     this.unrendered_panels = [];
34893 };
34894
34895 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34896
34897     position: '', // set by wrapper (eg. north/south etc..)
34898     unrendered_panels : null,  // unrendered panels.
34899     createBody : function(){
34900         /** This region's body element 
34901         * @type Roo.Element */
34902         this.bodyEl = this.el.createChild({
34903                 tag: "div",
34904                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34905         });
34906     },
34907
34908     onRender: function(ctr, pos)
34909     {
34910         var dh = Roo.DomHelper;
34911         /** This region's container element 
34912         * @type Roo.Element */
34913         this.el = dh.append(ctr.dom, {
34914                 tag: "div",
34915                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34916             }, true);
34917         /** This region's title element 
34918         * @type Roo.Element */
34919     
34920         this.titleEl = dh.append(this.el.dom,
34921             {
34922                     tag: "div",
34923                     unselectable: "on",
34924                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34925                     children:[
34926                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34927                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34928                     ]}, true);
34929         
34930         this.titleEl.enableDisplayMode();
34931         /** This region's title text element 
34932         * @type HTMLElement */
34933         this.titleTextEl = this.titleEl.dom.firstChild;
34934         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34935         /*
34936         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34937         this.closeBtn.enableDisplayMode();
34938         this.closeBtn.on("click", this.closeClicked, this);
34939         this.closeBtn.hide();
34940     */
34941         this.createBody(this.config);
34942         if(this.config.hideWhenEmpty){
34943             this.hide();
34944             this.on("paneladded", this.validateVisibility, this);
34945             this.on("panelremoved", this.validateVisibility, this);
34946         }
34947         if(this.autoScroll){
34948             this.bodyEl.setStyle("overflow", "auto");
34949         }else{
34950             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34951         }
34952         //if(c.titlebar !== false){
34953             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34954                 this.titleEl.hide();
34955             }else{
34956                 this.titleEl.show();
34957                 if(this.config.title){
34958                     this.titleTextEl.innerHTML = this.config.title;
34959                 }
34960             }
34961         //}
34962         if(this.config.collapsed){
34963             this.collapse(true);
34964         }
34965         if(this.config.hidden){
34966             this.hide();
34967         }
34968         
34969         if (this.unrendered_panels && this.unrendered_panels.length) {
34970             for (var i =0;i< this.unrendered_panels.length; i++) {
34971                 this.add(this.unrendered_panels[i]);
34972             }
34973             this.unrendered_panels = null;
34974             
34975         }
34976         
34977     },
34978     
34979     applyConfig : function(c)
34980     {
34981         /*
34982          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34983             var dh = Roo.DomHelper;
34984             if(c.titlebar !== false){
34985                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34986                 this.collapseBtn.on("click", this.collapse, this);
34987                 this.collapseBtn.enableDisplayMode();
34988                 /*
34989                 if(c.showPin === true || this.showPin){
34990                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34991                     this.stickBtn.enableDisplayMode();
34992                     this.stickBtn.on("click", this.expand, this);
34993                     this.stickBtn.hide();
34994                 }
34995                 
34996             }
34997             */
34998             /** This region's collapsed element
34999             * @type Roo.Element */
35000             /*
35001              *
35002             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35003                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35004             ]}, true);
35005             
35006             if(c.floatable !== false){
35007                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35008                this.collapsedEl.on("click", this.collapseClick, this);
35009             }
35010
35011             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35012                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35013                    id: "message", unselectable: "on", style:{"float":"left"}});
35014                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35015              }
35016             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35017             this.expandBtn.on("click", this.expand, this);
35018             
35019         }
35020         
35021         if(this.collapseBtn){
35022             this.collapseBtn.setVisible(c.collapsible == true);
35023         }
35024         
35025         this.cmargins = c.cmargins || this.cmargins ||
35026                          (this.position == "west" || this.position == "east" ?
35027                              {top: 0, left: 2, right:2, bottom: 0} :
35028                              {top: 2, left: 0, right:0, bottom: 2});
35029         */
35030         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35031         
35032         
35033         this.bottomTabs = c.tabPosition != "top";
35034         
35035         this.autoScroll = c.autoScroll || false;
35036         
35037         
35038        
35039         
35040         this.duration = c.duration || .30;
35041         this.slideDuration = c.slideDuration || .45;
35042         this.config = c;
35043        
35044     },
35045     /**
35046      * Returns true if this region is currently visible.
35047      * @return {Boolean}
35048      */
35049     isVisible : function(){
35050         return this.visible;
35051     },
35052
35053     /**
35054      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35055      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35056      */
35057     //setCollapsedTitle : function(title){
35058     //    title = title || "&#160;";
35059      //   if(this.collapsedTitleTextEl){
35060       //      this.collapsedTitleTextEl.innerHTML = title;
35061        // }
35062     //},
35063
35064     getBox : function(){
35065         var b;
35066       //  if(!this.collapsed){
35067             b = this.el.getBox(false, true);
35068        // }else{
35069           //  b = this.collapsedEl.getBox(false, true);
35070         //}
35071         return b;
35072     },
35073
35074     getMargins : function(){
35075         return this.margins;
35076         //return this.collapsed ? this.cmargins : this.margins;
35077     },
35078 /*
35079     highlight : function(){
35080         this.el.addClass("x-layout-panel-dragover");
35081     },
35082
35083     unhighlight : function(){
35084         this.el.removeClass("x-layout-panel-dragover");
35085     },
35086 */
35087     updateBox : function(box)
35088     {
35089         if (!this.bodyEl) {
35090             return; // not rendered yet..
35091         }
35092         
35093         this.box = box;
35094         if(!this.collapsed){
35095             this.el.dom.style.left = box.x + "px";
35096             this.el.dom.style.top = box.y + "px";
35097             this.updateBody(box.width, box.height);
35098         }else{
35099             this.collapsedEl.dom.style.left = box.x + "px";
35100             this.collapsedEl.dom.style.top = box.y + "px";
35101             this.collapsedEl.setSize(box.width, box.height);
35102         }
35103         if(this.tabs){
35104             this.tabs.autoSizeTabs();
35105         }
35106     },
35107
35108     updateBody : function(w, h)
35109     {
35110         if(w !== null){
35111             this.el.setWidth(w);
35112             w -= this.el.getBorderWidth("rl");
35113             if(this.config.adjustments){
35114                 w += this.config.adjustments[0];
35115             }
35116         }
35117         if(h !== null && h > 0){
35118             this.el.setHeight(h);
35119             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35120             h -= this.el.getBorderWidth("tb");
35121             if(this.config.adjustments){
35122                 h += this.config.adjustments[1];
35123             }
35124             this.bodyEl.setHeight(h);
35125             if(this.tabs){
35126                 h = this.tabs.syncHeight(h);
35127             }
35128         }
35129         if(this.panelSize){
35130             w = w !== null ? w : this.panelSize.width;
35131             h = h !== null ? h : this.panelSize.height;
35132         }
35133         if(this.activePanel){
35134             var el = this.activePanel.getEl();
35135             w = w !== null ? w : el.getWidth();
35136             h = h !== null ? h : el.getHeight();
35137             this.panelSize = {width: w, height: h};
35138             this.activePanel.setSize(w, h);
35139         }
35140         if(Roo.isIE && this.tabs){
35141             this.tabs.el.repaint();
35142         }
35143     },
35144
35145     /**
35146      * Returns the container element for this region.
35147      * @return {Roo.Element}
35148      */
35149     getEl : function(){
35150         return this.el;
35151     },
35152
35153     /**
35154      * Hides this region.
35155      */
35156     hide : function(){
35157         //if(!this.collapsed){
35158             this.el.dom.style.left = "-2000px";
35159             this.el.hide();
35160         //}else{
35161          //   this.collapsedEl.dom.style.left = "-2000px";
35162          //   this.collapsedEl.hide();
35163        // }
35164         this.visible = false;
35165         this.fireEvent("visibilitychange", this, false);
35166     },
35167
35168     /**
35169      * Shows this region if it was previously hidden.
35170      */
35171     show : function(){
35172         //if(!this.collapsed){
35173             this.el.show();
35174         //}else{
35175         //    this.collapsedEl.show();
35176        // }
35177         this.visible = true;
35178         this.fireEvent("visibilitychange", this, true);
35179     },
35180 /*
35181     closeClicked : function(){
35182         if(this.activePanel){
35183             this.remove(this.activePanel);
35184         }
35185     },
35186
35187     collapseClick : function(e){
35188         if(this.isSlid){
35189            e.stopPropagation();
35190            this.slideIn();
35191         }else{
35192            e.stopPropagation();
35193            this.slideOut();
35194         }
35195     },
35196 */
35197     /**
35198      * Collapses this region.
35199      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35200      */
35201     /*
35202     collapse : function(skipAnim, skipCheck = false){
35203         if(this.collapsed) {
35204             return;
35205         }
35206         
35207         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35208             
35209             this.collapsed = true;
35210             if(this.split){
35211                 this.split.el.hide();
35212             }
35213             if(this.config.animate && skipAnim !== true){
35214                 this.fireEvent("invalidated", this);
35215                 this.animateCollapse();
35216             }else{
35217                 this.el.setLocation(-20000,-20000);
35218                 this.el.hide();
35219                 this.collapsedEl.show();
35220                 this.fireEvent("collapsed", this);
35221                 this.fireEvent("invalidated", this);
35222             }
35223         }
35224         
35225     },
35226 */
35227     animateCollapse : function(){
35228         // overridden
35229     },
35230
35231     /**
35232      * Expands this region if it was previously collapsed.
35233      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35234      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35235      */
35236     /*
35237     expand : function(e, skipAnim){
35238         if(e) {
35239             e.stopPropagation();
35240         }
35241         if(!this.collapsed || this.el.hasActiveFx()) {
35242             return;
35243         }
35244         if(this.isSlid){
35245             this.afterSlideIn();
35246             skipAnim = true;
35247         }
35248         this.collapsed = false;
35249         if(this.config.animate && skipAnim !== true){
35250             this.animateExpand();
35251         }else{
35252             this.el.show();
35253             if(this.split){
35254                 this.split.el.show();
35255             }
35256             this.collapsedEl.setLocation(-2000,-2000);
35257             this.collapsedEl.hide();
35258             this.fireEvent("invalidated", this);
35259             this.fireEvent("expanded", this);
35260         }
35261     },
35262 */
35263     animateExpand : function(){
35264         // overridden
35265     },
35266
35267     initTabs : function()
35268     {
35269         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35270         
35271         var ts = new Roo.bootstrap.panel.Tabs({
35272                 el: this.bodyEl.dom,
35273                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35274                 disableTooltips: this.config.disableTabTips,
35275                 toolbar : this.config.toolbar
35276             });
35277         
35278         if(this.config.hideTabs){
35279             ts.stripWrap.setDisplayed(false);
35280         }
35281         this.tabs = ts;
35282         ts.resizeTabs = this.config.resizeTabs === true;
35283         ts.minTabWidth = this.config.minTabWidth || 40;
35284         ts.maxTabWidth = this.config.maxTabWidth || 250;
35285         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35286         ts.monitorResize = false;
35287         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35288         ts.bodyEl.addClass('roo-layout-tabs-body');
35289         this.panels.each(this.initPanelAsTab, this);
35290     },
35291
35292     initPanelAsTab : function(panel){
35293         var ti = this.tabs.addTab(
35294             panel.getEl().id,
35295             panel.getTitle(),
35296             null,
35297             this.config.closeOnTab && panel.isClosable(),
35298             panel.tpl
35299         );
35300         if(panel.tabTip !== undefined){
35301             ti.setTooltip(panel.tabTip);
35302         }
35303         ti.on("activate", function(){
35304               this.setActivePanel(panel);
35305         }, this);
35306         
35307         if(this.config.closeOnTab){
35308             ti.on("beforeclose", function(t, e){
35309                 e.cancel = true;
35310                 this.remove(panel);
35311             }, this);
35312         }
35313         
35314         panel.tabItem = ti;
35315         
35316         return ti;
35317     },
35318
35319     updatePanelTitle : function(panel, title)
35320     {
35321         if(this.activePanel == panel){
35322             this.updateTitle(title);
35323         }
35324         if(this.tabs){
35325             var ti = this.tabs.getTab(panel.getEl().id);
35326             ti.setText(title);
35327             if(panel.tabTip !== undefined){
35328                 ti.setTooltip(panel.tabTip);
35329             }
35330         }
35331     },
35332
35333     updateTitle : function(title){
35334         if(this.titleTextEl && !this.config.title){
35335             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35336         }
35337     },
35338
35339     setActivePanel : function(panel)
35340     {
35341         panel = this.getPanel(panel);
35342         if(this.activePanel && this.activePanel != panel){
35343             this.activePanel.setActiveState(false);
35344         }
35345         this.activePanel = panel;
35346         panel.setActiveState(true);
35347         if(this.panelSize){
35348             panel.setSize(this.panelSize.width, this.panelSize.height);
35349         }
35350         if(this.closeBtn){
35351             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35352         }
35353         this.updateTitle(panel.getTitle());
35354         if(this.tabs){
35355             this.fireEvent("invalidated", this);
35356         }
35357         this.fireEvent("panelactivated", this, panel);
35358     },
35359
35360     /**
35361      * Shows the specified panel.
35362      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35363      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35364      */
35365     showPanel : function(panel)
35366     {
35367         panel = this.getPanel(panel);
35368         if(panel){
35369             if(this.tabs){
35370                 var tab = this.tabs.getTab(panel.getEl().id);
35371                 if(tab.isHidden()){
35372                     this.tabs.unhideTab(tab.id);
35373                 }
35374                 tab.activate();
35375             }else{
35376                 this.setActivePanel(panel);
35377             }
35378         }
35379         return panel;
35380     },
35381
35382     /**
35383      * Get the active panel for this region.
35384      * @return {Roo.ContentPanel} The active panel or null
35385      */
35386     getActivePanel : function(){
35387         return this.activePanel;
35388     },
35389
35390     validateVisibility : function(){
35391         if(this.panels.getCount() < 1){
35392             this.updateTitle("&#160;");
35393             this.closeBtn.hide();
35394             this.hide();
35395         }else{
35396             if(!this.isVisible()){
35397                 this.show();
35398             }
35399         }
35400     },
35401
35402     /**
35403      * Adds the passed ContentPanel(s) to this region.
35404      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35405      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35406      */
35407     add : function(panel)
35408     {
35409         if(arguments.length > 1){
35410             for(var i = 0, len = arguments.length; i < len; i++) {
35411                 this.add(arguments[i]);
35412             }
35413             return null;
35414         }
35415         
35416         // if we have not been rendered yet, then we can not really do much of this..
35417         if (!this.bodyEl) {
35418             this.unrendered_panels.push(panel);
35419             return panel;
35420         }
35421         
35422         
35423         
35424         
35425         if(this.hasPanel(panel)){
35426             this.showPanel(panel);
35427             return panel;
35428         }
35429         panel.setRegion(this);
35430         this.panels.add(panel);
35431        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35432             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35433             // and hide them... ???
35434             this.bodyEl.dom.appendChild(panel.getEl().dom);
35435             if(panel.background !== true){
35436                 this.setActivePanel(panel);
35437             }
35438             this.fireEvent("paneladded", this, panel);
35439             return panel;
35440         }
35441         */
35442         if(!this.tabs){
35443             this.initTabs();
35444         }else{
35445             this.initPanelAsTab(panel);
35446         }
35447         
35448         
35449         if(panel.background !== true){
35450             this.tabs.activate(panel.getEl().id);
35451         }
35452         this.fireEvent("paneladded", this, panel);
35453         return panel;
35454     },
35455
35456     /**
35457      * Hides the tab for the specified panel.
35458      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35459      */
35460     hidePanel : function(panel){
35461         if(this.tabs && (panel = this.getPanel(panel))){
35462             this.tabs.hideTab(panel.getEl().id);
35463         }
35464     },
35465
35466     /**
35467      * Unhides the tab for a previously hidden panel.
35468      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35469      */
35470     unhidePanel : function(panel){
35471         if(this.tabs && (panel = this.getPanel(panel))){
35472             this.tabs.unhideTab(panel.getEl().id);
35473         }
35474     },
35475
35476     clearPanels : function(){
35477         while(this.panels.getCount() > 0){
35478              this.remove(this.panels.first());
35479         }
35480     },
35481
35482     /**
35483      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35485      * @param {Boolean} preservePanel Overrides the config preservePanel option
35486      * @return {Roo.ContentPanel} The panel that was removed
35487      */
35488     remove : function(panel, preservePanel)
35489     {
35490         panel = this.getPanel(panel);
35491         if(!panel){
35492             return null;
35493         }
35494         var e = {};
35495         this.fireEvent("beforeremove", this, panel, e);
35496         if(e.cancel === true){
35497             return null;
35498         }
35499         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35500         var panelId = panel.getId();
35501         this.panels.removeKey(panelId);
35502         if(preservePanel){
35503             document.body.appendChild(panel.getEl().dom);
35504         }
35505         if(this.tabs){
35506             this.tabs.removeTab(panel.getEl().id);
35507         }else if (!preservePanel){
35508             this.bodyEl.dom.removeChild(panel.getEl().dom);
35509         }
35510         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35511             var p = this.panels.first();
35512             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35513             tempEl.appendChild(p.getEl().dom);
35514             this.bodyEl.update("");
35515             this.bodyEl.dom.appendChild(p.getEl().dom);
35516             tempEl = null;
35517             this.updateTitle(p.getTitle());
35518             this.tabs = null;
35519             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35520             this.setActivePanel(p);
35521         }
35522         panel.setRegion(null);
35523         if(this.activePanel == panel){
35524             this.activePanel = null;
35525         }
35526         if(this.config.autoDestroy !== false && preservePanel !== true){
35527             try{panel.destroy();}catch(e){}
35528         }
35529         this.fireEvent("panelremoved", this, panel);
35530         return panel;
35531     },
35532
35533     /**
35534      * Returns the TabPanel component used by this region
35535      * @return {Roo.TabPanel}
35536      */
35537     getTabs : function(){
35538         return this.tabs;
35539     },
35540
35541     createTool : function(parentEl, className){
35542         var btn = Roo.DomHelper.append(parentEl, {
35543             tag: "div",
35544             cls: "x-layout-tools-button",
35545             children: [ {
35546                 tag: "div",
35547                 cls: "roo-layout-tools-button-inner " + className,
35548                 html: "&#160;"
35549             }]
35550         }, true);
35551         btn.addClassOnOver("roo-layout-tools-button-over");
35552         return btn;
35553     }
35554 });/*
35555  * Based on:
35556  * Ext JS Library 1.1.1
35557  * Copyright(c) 2006-2007, Ext JS, LLC.
35558  *
35559  * Originally Released Under LGPL - original licence link has changed is not relivant.
35560  *
35561  * Fork - LGPL
35562  * <script type="text/javascript">
35563  */
35564  
35565
35566
35567 /**
35568  * @class Roo.SplitLayoutRegion
35569  * @extends Roo.LayoutRegion
35570  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35571  */
35572 Roo.bootstrap.layout.Split = function(config){
35573     this.cursor = config.cursor;
35574     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35575 };
35576
35577 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35578 {
35579     splitTip : "Drag to resize.",
35580     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35581     useSplitTips : false,
35582
35583     applyConfig : function(config){
35584         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35585     },
35586     
35587     onRender : function(ctr,pos) {
35588         
35589         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35590         if(!this.config.split){
35591             return;
35592         }
35593         if(!this.split){
35594             
35595             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35596                             tag: "div",
35597                             id: this.el.id + "-split",
35598                             cls: "roo-layout-split roo-layout-split-"+this.position,
35599                             html: "&#160;"
35600             });
35601             /** The SplitBar for this region 
35602             * @type Roo.SplitBar */
35603             // does not exist yet...
35604             Roo.log([this.position, this.orientation]);
35605             
35606             this.split = new Roo.bootstrap.SplitBar({
35607                 dragElement : splitEl,
35608                 resizingElement: this.el,
35609                 orientation : this.orientation
35610             });
35611             
35612             this.split.on("moved", this.onSplitMove, this);
35613             this.split.useShim = this.config.useShim === true;
35614             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35615             if(this.useSplitTips){
35616                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35617             }
35618             //if(config.collapsible){
35619             //    this.split.el.on("dblclick", this.collapse,  this);
35620             //}
35621         }
35622         if(typeof this.config.minSize != "undefined"){
35623             this.split.minSize = this.config.minSize;
35624         }
35625         if(typeof this.config.maxSize != "undefined"){
35626             this.split.maxSize = this.config.maxSize;
35627         }
35628         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35629             this.hideSplitter();
35630         }
35631         
35632     },
35633
35634     getHMaxSize : function(){
35635          var cmax = this.config.maxSize || 10000;
35636          var center = this.mgr.getRegion("center");
35637          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35638     },
35639
35640     getVMaxSize : function(){
35641          var cmax = this.config.maxSize || 10000;
35642          var center = this.mgr.getRegion("center");
35643          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35644     },
35645
35646     onSplitMove : function(split, newSize){
35647         this.fireEvent("resized", this, newSize);
35648     },
35649     
35650     /** 
35651      * Returns the {@link Roo.SplitBar} for this region.
35652      * @return {Roo.SplitBar}
35653      */
35654     getSplitBar : function(){
35655         return this.split;
35656     },
35657     
35658     hide : function(){
35659         this.hideSplitter();
35660         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35661     },
35662
35663     hideSplitter : function(){
35664         if(this.split){
35665             this.split.el.setLocation(-2000,-2000);
35666             this.split.el.hide();
35667         }
35668     },
35669
35670     show : function(){
35671         if(this.split){
35672             this.split.el.show();
35673         }
35674         Roo.bootstrap.layout.Split.superclass.show.call(this);
35675     },
35676     
35677     beforeSlide: function(){
35678         if(Roo.isGecko){// firefox overflow auto bug workaround
35679             this.bodyEl.clip();
35680             if(this.tabs) {
35681                 this.tabs.bodyEl.clip();
35682             }
35683             if(this.activePanel){
35684                 this.activePanel.getEl().clip();
35685                 
35686                 if(this.activePanel.beforeSlide){
35687                     this.activePanel.beforeSlide();
35688                 }
35689             }
35690         }
35691     },
35692     
35693     afterSlide : function(){
35694         if(Roo.isGecko){// firefox overflow auto bug workaround
35695             this.bodyEl.unclip();
35696             if(this.tabs) {
35697                 this.tabs.bodyEl.unclip();
35698             }
35699             if(this.activePanel){
35700                 this.activePanel.getEl().unclip();
35701                 if(this.activePanel.afterSlide){
35702                     this.activePanel.afterSlide();
35703                 }
35704             }
35705         }
35706     },
35707
35708     initAutoHide : function(){
35709         if(this.autoHide !== false){
35710             if(!this.autoHideHd){
35711                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35712                 this.autoHideHd = {
35713                     "mouseout": function(e){
35714                         if(!e.within(this.el, true)){
35715                             st.delay(500);
35716                         }
35717                     },
35718                     "mouseover" : function(e){
35719                         st.cancel();
35720                     },
35721                     scope : this
35722                 };
35723             }
35724             this.el.on(this.autoHideHd);
35725         }
35726     },
35727
35728     clearAutoHide : function(){
35729         if(this.autoHide !== false){
35730             this.el.un("mouseout", this.autoHideHd.mouseout);
35731             this.el.un("mouseover", this.autoHideHd.mouseover);
35732         }
35733     },
35734
35735     clearMonitor : function(){
35736         Roo.get(document).un("click", this.slideInIf, this);
35737     },
35738
35739     // these names are backwards but not changed for compat
35740     slideOut : function(){
35741         if(this.isSlid || this.el.hasActiveFx()){
35742             return;
35743         }
35744         this.isSlid = true;
35745         if(this.collapseBtn){
35746             this.collapseBtn.hide();
35747         }
35748         this.closeBtnState = this.closeBtn.getStyle('display');
35749         this.closeBtn.hide();
35750         if(this.stickBtn){
35751             this.stickBtn.show();
35752         }
35753         this.el.show();
35754         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35755         this.beforeSlide();
35756         this.el.setStyle("z-index", 10001);
35757         this.el.slideIn(this.getSlideAnchor(), {
35758             callback: function(){
35759                 this.afterSlide();
35760                 this.initAutoHide();
35761                 Roo.get(document).on("click", this.slideInIf, this);
35762                 this.fireEvent("slideshow", this);
35763             },
35764             scope: this,
35765             block: true
35766         });
35767     },
35768
35769     afterSlideIn : function(){
35770         this.clearAutoHide();
35771         this.isSlid = false;
35772         this.clearMonitor();
35773         this.el.setStyle("z-index", "");
35774         if(this.collapseBtn){
35775             this.collapseBtn.show();
35776         }
35777         this.closeBtn.setStyle('display', this.closeBtnState);
35778         if(this.stickBtn){
35779             this.stickBtn.hide();
35780         }
35781         this.fireEvent("slidehide", this);
35782     },
35783
35784     slideIn : function(cb){
35785         if(!this.isSlid || this.el.hasActiveFx()){
35786             Roo.callback(cb);
35787             return;
35788         }
35789         this.isSlid = false;
35790         this.beforeSlide();
35791         this.el.slideOut(this.getSlideAnchor(), {
35792             callback: function(){
35793                 this.el.setLeftTop(-10000, -10000);
35794                 this.afterSlide();
35795                 this.afterSlideIn();
35796                 Roo.callback(cb);
35797             },
35798             scope: this,
35799             block: true
35800         });
35801     },
35802     
35803     slideInIf : function(e){
35804         if(!e.within(this.el)){
35805             this.slideIn();
35806         }
35807     },
35808
35809     animateCollapse : function(){
35810         this.beforeSlide();
35811         this.el.setStyle("z-index", 20000);
35812         var anchor = this.getSlideAnchor();
35813         this.el.slideOut(anchor, {
35814             callback : function(){
35815                 this.el.setStyle("z-index", "");
35816                 this.collapsedEl.slideIn(anchor, {duration:.3});
35817                 this.afterSlide();
35818                 this.el.setLocation(-10000,-10000);
35819                 this.el.hide();
35820                 this.fireEvent("collapsed", this);
35821             },
35822             scope: this,
35823             block: true
35824         });
35825     },
35826
35827     animateExpand : function(){
35828         this.beforeSlide();
35829         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35830         this.el.setStyle("z-index", 20000);
35831         this.collapsedEl.hide({
35832             duration:.1
35833         });
35834         this.el.slideIn(this.getSlideAnchor(), {
35835             callback : function(){
35836                 this.el.setStyle("z-index", "");
35837                 this.afterSlide();
35838                 if(this.split){
35839                     this.split.el.show();
35840                 }
35841                 this.fireEvent("invalidated", this);
35842                 this.fireEvent("expanded", this);
35843             },
35844             scope: this,
35845             block: true
35846         });
35847     },
35848
35849     anchors : {
35850         "west" : "left",
35851         "east" : "right",
35852         "north" : "top",
35853         "south" : "bottom"
35854     },
35855
35856     sanchors : {
35857         "west" : "l",
35858         "east" : "r",
35859         "north" : "t",
35860         "south" : "b"
35861     },
35862
35863     canchors : {
35864         "west" : "tl-tr",
35865         "east" : "tr-tl",
35866         "north" : "tl-bl",
35867         "south" : "bl-tl"
35868     },
35869
35870     getAnchor : function(){
35871         return this.anchors[this.position];
35872     },
35873
35874     getCollapseAnchor : function(){
35875         return this.canchors[this.position];
35876     },
35877
35878     getSlideAnchor : function(){
35879         return this.sanchors[this.position];
35880     },
35881
35882     getAlignAdj : function(){
35883         var cm = this.cmargins;
35884         switch(this.position){
35885             case "west":
35886                 return [0, 0];
35887             break;
35888             case "east":
35889                 return [0, 0];
35890             break;
35891             case "north":
35892                 return [0, 0];
35893             break;
35894             case "south":
35895                 return [0, 0];
35896             break;
35897         }
35898     },
35899
35900     getExpandAdj : function(){
35901         var c = this.collapsedEl, cm = this.cmargins;
35902         switch(this.position){
35903             case "west":
35904                 return [-(cm.right+c.getWidth()+cm.left), 0];
35905             break;
35906             case "east":
35907                 return [cm.right+c.getWidth()+cm.left, 0];
35908             break;
35909             case "north":
35910                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35911             break;
35912             case "south":
35913                 return [0, cm.top+cm.bottom+c.getHeight()];
35914             break;
35915         }
35916     }
35917 });/*
35918  * Based on:
35919  * Ext JS Library 1.1.1
35920  * Copyright(c) 2006-2007, Ext JS, LLC.
35921  *
35922  * Originally Released Under LGPL - original licence link has changed is not relivant.
35923  *
35924  * Fork - LGPL
35925  * <script type="text/javascript">
35926  */
35927 /*
35928  * These classes are private internal classes
35929  */
35930 Roo.bootstrap.layout.Center = function(config){
35931     config.region = "center";
35932     Roo.bootstrap.layout.Region.call(this, config);
35933     this.visible = true;
35934     this.minWidth = config.minWidth || 20;
35935     this.minHeight = config.minHeight || 20;
35936 };
35937
35938 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35939     hide : function(){
35940         // center panel can't be hidden
35941     },
35942     
35943     show : function(){
35944         // center panel can't be hidden
35945     },
35946     
35947     getMinWidth: function(){
35948         return this.minWidth;
35949     },
35950     
35951     getMinHeight: function(){
35952         return this.minHeight;
35953     }
35954 });
35955
35956
35957
35958
35959  
35960
35961
35962
35963
35964
35965 Roo.bootstrap.layout.North = function(config)
35966 {
35967     config.region = 'north';
35968     config.cursor = 'n-resize';
35969     
35970     Roo.bootstrap.layout.Split.call(this, config);
35971     
35972     
35973     if(this.split){
35974         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35975         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35976         this.split.el.addClass("roo-layout-split-v");
35977     }
35978     var size = config.initialSize || config.height;
35979     if(typeof size != "undefined"){
35980         this.el.setHeight(size);
35981     }
35982 };
35983 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35984 {
35985     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35986     
35987     
35988     
35989     getBox : function(){
35990         if(this.collapsed){
35991             return this.collapsedEl.getBox();
35992         }
35993         var box = this.el.getBox();
35994         if(this.split){
35995             box.height += this.split.el.getHeight();
35996         }
35997         return box;
35998     },
35999     
36000     updateBox : function(box){
36001         if(this.split && !this.collapsed){
36002             box.height -= this.split.el.getHeight();
36003             this.split.el.setLeft(box.x);
36004             this.split.el.setTop(box.y+box.height);
36005             this.split.el.setWidth(box.width);
36006         }
36007         if(this.collapsed){
36008             this.updateBody(box.width, null);
36009         }
36010         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36011     }
36012 });
36013
36014
36015
36016
36017
36018 Roo.bootstrap.layout.South = function(config){
36019     config.region = 'south';
36020     config.cursor = 's-resize';
36021     Roo.bootstrap.layout.Split.call(this, config);
36022     if(this.split){
36023         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36024         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36025         this.split.el.addClass("roo-layout-split-v");
36026     }
36027     var size = config.initialSize || config.height;
36028     if(typeof size != "undefined"){
36029         this.el.setHeight(size);
36030     }
36031 };
36032
36033 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36034     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36035     getBox : function(){
36036         if(this.collapsed){
36037             return this.collapsedEl.getBox();
36038         }
36039         var box = this.el.getBox();
36040         if(this.split){
36041             var sh = this.split.el.getHeight();
36042             box.height += sh;
36043             box.y -= sh;
36044         }
36045         return box;
36046     },
36047     
36048     updateBox : function(box){
36049         if(this.split && !this.collapsed){
36050             var sh = this.split.el.getHeight();
36051             box.height -= sh;
36052             box.y += sh;
36053             this.split.el.setLeft(box.x);
36054             this.split.el.setTop(box.y-sh);
36055             this.split.el.setWidth(box.width);
36056         }
36057         if(this.collapsed){
36058             this.updateBody(box.width, null);
36059         }
36060         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36061     }
36062 });
36063
36064 Roo.bootstrap.layout.East = function(config){
36065     config.region = "east";
36066     config.cursor = "e-resize";
36067     Roo.bootstrap.layout.Split.call(this, config);
36068     if(this.split){
36069         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36070         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36071         this.split.el.addClass("roo-layout-split-h");
36072     }
36073     var size = config.initialSize || config.width;
36074     if(typeof size != "undefined"){
36075         this.el.setWidth(size);
36076     }
36077 };
36078 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36079     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36080     getBox : function(){
36081         if(this.collapsed){
36082             return this.collapsedEl.getBox();
36083         }
36084         var box = this.el.getBox();
36085         if(this.split){
36086             var sw = this.split.el.getWidth();
36087             box.width += sw;
36088             box.x -= sw;
36089         }
36090         return box;
36091     },
36092
36093     updateBox : function(box){
36094         if(this.split && !this.collapsed){
36095             var sw = this.split.el.getWidth();
36096             box.width -= sw;
36097             this.split.el.setLeft(box.x);
36098             this.split.el.setTop(box.y);
36099             this.split.el.setHeight(box.height);
36100             box.x += sw;
36101         }
36102         if(this.collapsed){
36103             this.updateBody(null, box.height);
36104         }
36105         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36106     }
36107 });
36108
36109 Roo.bootstrap.layout.West = function(config){
36110     config.region = "west";
36111     config.cursor = "w-resize";
36112     
36113     Roo.bootstrap.layout.Split.call(this, config);
36114     if(this.split){
36115         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36116         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36117         this.split.el.addClass("roo-layout-split-h");
36118     }
36119     
36120 };
36121 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36122     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36123     
36124     onRender: function(ctr, pos)
36125     {
36126         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36127         var size = this.config.initialSize || this.config.width;
36128         if(typeof size != "undefined"){
36129             this.el.setWidth(size);
36130         }
36131     },
36132     
36133     getBox : function(){
36134         if(this.collapsed){
36135             return this.collapsedEl.getBox();
36136         }
36137         var box = this.el.getBox();
36138         if(this.split){
36139             box.width += this.split.el.getWidth();
36140         }
36141         return box;
36142     },
36143     
36144     updateBox : function(box){
36145         if(this.split && !this.collapsed){
36146             var sw = this.split.el.getWidth();
36147             box.width -= sw;
36148             this.split.el.setLeft(box.x+box.width);
36149             this.split.el.setTop(box.y);
36150             this.split.el.setHeight(box.height);
36151         }
36152         if(this.collapsed){
36153             this.updateBody(null, box.height);
36154         }
36155         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36156     }
36157 });
36158 Roo.namespace("Roo.bootstrap.panel");/*
36159  * Based on:
36160  * Ext JS Library 1.1.1
36161  * Copyright(c) 2006-2007, Ext JS, LLC.
36162  *
36163  * Originally Released Under LGPL - original licence link has changed is not relivant.
36164  *
36165  * Fork - LGPL
36166  * <script type="text/javascript">
36167  */
36168 /**
36169  * @class Roo.ContentPanel
36170  * @extends Roo.util.Observable
36171  * A basic ContentPanel element.
36172  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36173  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36174  * @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
36175  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36176  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36177  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36178  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36179  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36180  * @cfg {String} title          The title for this panel
36181  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36182  * @cfg {String} url            Calls {@link #setUrl} with this value
36183  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36184  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36185  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36186  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36187  * @cfg {Boolean} badges render the badges
36188
36189  * @constructor
36190  * Create a new ContentPanel.
36191  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36192  * @param {String/Object} config A string to set only the title or a config object
36193  * @param {String} content (optional) Set the HTML content for this panel
36194  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36195  */
36196 Roo.bootstrap.panel.Content = function( config){
36197     
36198     this.tpl = config.tpl || false;
36199     
36200     var el = config.el;
36201     var content = config.content;
36202
36203     if(config.autoCreate){ // xtype is available if this is called from factory
36204         el = Roo.id();
36205     }
36206     this.el = Roo.get(el);
36207     if(!this.el && config && config.autoCreate){
36208         if(typeof config.autoCreate == "object"){
36209             if(!config.autoCreate.id){
36210                 config.autoCreate.id = config.id||el;
36211             }
36212             this.el = Roo.DomHelper.append(document.body,
36213                         config.autoCreate, true);
36214         }else{
36215             var elcfg =  {   tag: "div",
36216                             cls: "roo-layout-inactive-content",
36217                             id: config.id||el
36218                             };
36219             if (config.html) {
36220                 elcfg.html = config.html;
36221                 
36222             }
36223                         
36224             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36225         }
36226     } 
36227     this.closable = false;
36228     this.loaded = false;
36229     this.active = false;
36230    
36231       
36232     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36233         
36234         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36235         
36236         this.wrapEl = this.el; //this.el.wrap();
36237         var ti = [];
36238         if (config.toolbar.items) {
36239             ti = config.toolbar.items ;
36240             delete config.toolbar.items ;
36241         }
36242         
36243         var nitems = [];
36244         this.toolbar.render(this.wrapEl, 'before');
36245         for(var i =0;i < ti.length;i++) {
36246           //  Roo.log(['add child', items[i]]);
36247             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36248         }
36249         this.toolbar.items = nitems;
36250         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36251         delete config.toolbar;
36252         
36253     }
36254     /*
36255     // xtype created footer. - not sure if will work as we normally have to render first..
36256     if (this.footer && !this.footer.el && this.footer.xtype) {
36257         if (!this.wrapEl) {
36258             this.wrapEl = this.el.wrap();
36259         }
36260     
36261         this.footer.container = this.wrapEl.createChild();
36262          
36263         this.footer = Roo.factory(this.footer, Roo);
36264         
36265     }
36266     */
36267     
36268      if(typeof config == "string"){
36269         this.title = config;
36270     }else{
36271         Roo.apply(this, config);
36272     }
36273     
36274     if(this.resizeEl){
36275         this.resizeEl = Roo.get(this.resizeEl, true);
36276     }else{
36277         this.resizeEl = this.el;
36278     }
36279     // handle view.xtype
36280     
36281  
36282     
36283     
36284     this.addEvents({
36285         /**
36286          * @event activate
36287          * Fires when this panel is activated. 
36288          * @param {Roo.ContentPanel} this
36289          */
36290         "activate" : true,
36291         /**
36292          * @event deactivate
36293          * Fires when this panel is activated. 
36294          * @param {Roo.ContentPanel} this
36295          */
36296         "deactivate" : true,
36297
36298         /**
36299          * @event resize
36300          * Fires when this panel is resized if fitToFrame is true.
36301          * @param {Roo.ContentPanel} this
36302          * @param {Number} width The width after any component adjustments
36303          * @param {Number} height The height after any component adjustments
36304          */
36305         "resize" : true,
36306         
36307          /**
36308          * @event render
36309          * Fires when this tab is created
36310          * @param {Roo.ContentPanel} this
36311          */
36312         "render" : true
36313         
36314         
36315         
36316     });
36317     
36318
36319     
36320     
36321     if(this.autoScroll){
36322         this.resizeEl.setStyle("overflow", "auto");
36323     } else {
36324         // fix randome scrolling
36325         //this.el.on('scroll', function() {
36326         //    Roo.log('fix random scolling');
36327         //    this.scrollTo('top',0); 
36328         //});
36329     }
36330     content = content || this.content;
36331     if(content){
36332         this.setContent(content);
36333     }
36334     if(config && config.url){
36335         this.setUrl(this.url, this.params, this.loadOnce);
36336     }
36337     
36338     
36339     
36340     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36341     
36342     if (this.view && typeof(this.view.xtype) != 'undefined') {
36343         this.view.el = this.el.appendChild(document.createElement("div"));
36344         this.view = Roo.factory(this.view); 
36345         this.view.render  &&  this.view.render(false, '');  
36346     }
36347     
36348     
36349     this.fireEvent('render', this);
36350 };
36351
36352 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36353     
36354     tabTip : '',
36355     
36356     setRegion : function(region){
36357         this.region = region;
36358         this.setActiveClass(region && !this.background);
36359     },
36360     
36361     
36362     setActiveClass: function(state)
36363     {
36364         if(state){
36365            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36366            this.el.setStyle('position','relative');
36367         }else{
36368            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36369            this.el.setStyle('position', 'absolute');
36370         } 
36371     },
36372     
36373     /**
36374      * Returns the toolbar for this Panel if one was configured. 
36375      * @return {Roo.Toolbar} 
36376      */
36377     getToolbar : function(){
36378         return this.toolbar;
36379     },
36380     
36381     setActiveState : function(active)
36382     {
36383         this.active = active;
36384         this.setActiveClass(active);
36385         if(!active){
36386             this.fireEvent("deactivate", this);
36387         }else{
36388             this.fireEvent("activate", this);
36389         }
36390     },
36391     /**
36392      * Updates this panel's element
36393      * @param {String} content The new content
36394      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36395     */
36396     setContent : function(content, loadScripts){
36397         this.el.update(content, loadScripts);
36398     },
36399
36400     ignoreResize : function(w, h){
36401         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36402             return true;
36403         }else{
36404             this.lastSize = {width: w, height: h};
36405             return false;
36406         }
36407     },
36408     /**
36409      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36410      * @return {Roo.UpdateManager} The UpdateManager
36411      */
36412     getUpdateManager : function(){
36413         return this.el.getUpdateManager();
36414     },
36415      /**
36416      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36417      * @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:
36418 <pre><code>
36419 panel.load({
36420     url: "your-url.php",
36421     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36422     callback: yourFunction,
36423     scope: yourObject, //(optional scope)
36424     discardUrl: false,
36425     nocache: false,
36426     text: "Loading...",
36427     timeout: 30,
36428     scripts: false
36429 });
36430 </code></pre>
36431      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36432      * 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.
36433      * @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}
36434      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36435      * @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.
36436      * @return {Roo.ContentPanel} this
36437      */
36438     load : function(){
36439         var um = this.el.getUpdateManager();
36440         um.update.apply(um, arguments);
36441         return this;
36442     },
36443
36444
36445     /**
36446      * 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.
36447      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36448      * @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)
36449      * @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)
36450      * @return {Roo.UpdateManager} The UpdateManager
36451      */
36452     setUrl : function(url, params, loadOnce){
36453         if(this.refreshDelegate){
36454             this.removeListener("activate", this.refreshDelegate);
36455         }
36456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36457         this.on("activate", this.refreshDelegate);
36458         return this.el.getUpdateManager();
36459     },
36460     
36461     _handleRefresh : function(url, params, loadOnce){
36462         if(!loadOnce || !this.loaded){
36463             var updater = this.el.getUpdateManager();
36464             updater.update(url, params, this._setLoaded.createDelegate(this));
36465         }
36466     },
36467     
36468     _setLoaded : function(){
36469         this.loaded = true;
36470     }, 
36471     
36472     /**
36473      * Returns this panel's id
36474      * @return {String} 
36475      */
36476     getId : function(){
36477         return this.el.id;
36478     },
36479     
36480     /** 
36481      * Returns this panel's element - used by regiosn to add.
36482      * @return {Roo.Element} 
36483      */
36484     getEl : function(){
36485         return this.wrapEl || this.el;
36486     },
36487     
36488    
36489     
36490     adjustForComponents : function(width, height)
36491     {
36492         //Roo.log('adjustForComponents ');
36493         if(this.resizeEl != this.el){
36494             width -= this.el.getFrameWidth('lr');
36495             height -= this.el.getFrameWidth('tb');
36496         }
36497         if(this.toolbar){
36498             var te = this.toolbar.getEl();
36499             te.setWidth(width);
36500             height -= te.getHeight();
36501         }
36502         if(this.footer){
36503             var te = this.footer.getEl();
36504             te.setWidth(width);
36505             height -= te.getHeight();
36506         }
36507         
36508         
36509         if(this.adjustments){
36510             width += this.adjustments[0];
36511             height += this.adjustments[1];
36512         }
36513         return {"width": width, "height": height};
36514     },
36515     
36516     setSize : function(width, height){
36517         if(this.fitToFrame && !this.ignoreResize(width, height)){
36518             if(this.fitContainer && this.resizeEl != this.el){
36519                 this.el.setSize(width, height);
36520             }
36521             var size = this.adjustForComponents(width, height);
36522             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36523             this.fireEvent('resize', this, size.width, size.height);
36524         }
36525     },
36526     
36527     /**
36528      * Returns this panel's title
36529      * @return {String} 
36530      */
36531     getTitle : function(){
36532         
36533         if (typeof(this.title) != 'object') {
36534             return this.title;
36535         }
36536         
36537         var t = '';
36538         for (var k in this.title) {
36539             if (!this.title.hasOwnProperty(k)) {
36540                 continue;
36541             }
36542             
36543             if (k.indexOf('-') >= 0) {
36544                 var s = k.split('-');
36545                 for (var i = 0; i<s.length; i++) {
36546                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36547                 }
36548             } else {
36549                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36550             }
36551         }
36552         return t;
36553     },
36554     
36555     /**
36556      * Set this panel's title
36557      * @param {String} title
36558      */
36559     setTitle : function(title){
36560         this.title = title;
36561         if(this.region){
36562             this.region.updatePanelTitle(this, title);
36563         }
36564     },
36565     
36566     /**
36567      * Returns true is this panel was configured to be closable
36568      * @return {Boolean} 
36569      */
36570     isClosable : function(){
36571         return this.closable;
36572     },
36573     
36574     beforeSlide : function(){
36575         this.el.clip();
36576         this.resizeEl.clip();
36577     },
36578     
36579     afterSlide : function(){
36580         this.el.unclip();
36581         this.resizeEl.unclip();
36582     },
36583     
36584     /**
36585      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36586      *   Will fail silently if the {@link #setUrl} method has not been called.
36587      *   This does not activate the panel, just updates its content.
36588      */
36589     refresh : function(){
36590         if(this.refreshDelegate){
36591            this.loaded = false;
36592            this.refreshDelegate();
36593         }
36594     },
36595     
36596     /**
36597      * Destroys this panel
36598      */
36599     destroy : function(){
36600         this.el.removeAllListeners();
36601         var tempEl = document.createElement("span");
36602         tempEl.appendChild(this.el.dom);
36603         tempEl.innerHTML = "";
36604         this.el.remove();
36605         this.el = null;
36606     },
36607     
36608     /**
36609      * form - if the content panel contains a form - this is a reference to it.
36610      * @type {Roo.form.Form}
36611      */
36612     form : false,
36613     /**
36614      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36615      *    This contains a reference to it.
36616      * @type {Roo.View}
36617      */
36618     view : false,
36619     
36620       /**
36621      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36622      * <pre><code>
36623
36624 layout.addxtype({
36625        xtype : 'Form',
36626        items: [ .... ]
36627    }
36628 );
36629
36630 </code></pre>
36631      * @param {Object} cfg Xtype definition of item to add.
36632      */
36633     
36634     
36635     getChildContainer: function () {
36636         return this.getEl();
36637     }
36638     
36639     
36640     /*
36641         var  ret = new Roo.factory(cfg);
36642         return ret;
36643         
36644         
36645         // add form..
36646         if (cfg.xtype.match(/^Form$/)) {
36647             
36648             var el;
36649             //if (this.footer) {
36650             //    el = this.footer.container.insertSibling(false, 'before');
36651             //} else {
36652                 el = this.el.createChild();
36653             //}
36654
36655             this.form = new  Roo.form.Form(cfg);
36656             
36657             
36658             if ( this.form.allItems.length) {
36659                 this.form.render(el.dom);
36660             }
36661             return this.form;
36662         }
36663         // should only have one of theses..
36664         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36665             // views.. should not be just added - used named prop 'view''
36666             
36667             cfg.el = this.el.appendChild(document.createElement("div"));
36668             // factory?
36669             
36670             var ret = new Roo.factory(cfg);
36671              
36672              ret.render && ret.render(false, ''); // render blank..
36673             this.view = ret;
36674             return ret;
36675         }
36676         return false;
36677     }
36678     \*/
36679 });
36680  
36681 /**
36682  * @class Roo.bootstrap.panel.Grid
36683  * @extends Roo.bootstrap.panel.Content
36684  * @constructor
36685  * Create a new GridPanel.
36686  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36687  * @param {Object} config A the config object
36688   
36689  */
36690
36691
36692
36693 Roo.bootstrap.panel.Grid = function(config)
36694 {
36695     
36696       
36697     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36698         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36699
36700     config.el = this.wrapper;
36701     //this.el = this.wrapper;
36702     
36703       if (config.container) {
36704         // ctor'ed from a Border/panel.grid
36705         
36706         
36707         this.wrapper.setStyle("overflow", "hidden");
36708         this.wrapper.addClass('roo-grid-container');
36709
36710     }
36711     
36712     
36713     if(config.toolbar){
36714         var tool_el = this.wrapper.createChild();    
36715         this.toolbar = Roo.factory(config.toolbar);
36716         var ti = [];
36717         if (config.toolbar.items) {
36718             ti = config.toolbar.items ;
36719             delete config.toolbar.items ;
36720         }
36721         
36722         var nitems = [];
36723         this.toolbar.render(tool_el);
36724         for(var i =0;i < ti.length;i++) {
36725           //  Roo.log(['add child', items[i]]);
36726             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36727         }
36728         this.toolbar.items = nitems;
36729         
36730         delete config.toolbar;
36731     }
36732     
36733     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36734     config.grid.scrollBody = true;;
36735     config.grid.monitorWindowResize = false; // turn off autosizing
36736     config.grid.autoHeight = false;
36737     config.grid.autoWidth = false;
36738     
36739     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36740     
36741     if (config.background) {
36742         // render grid on panel activation (if panel background)
36743         this.on('activate', function(gp) {
36744             if (!gp.grid.rendered) {
36745                 gp.grid.render(this.wrapper);
36746                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36747             }
36748         });
36749             
36750     } else {
36751         this.grid.render(this.wrapper);
36752         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36753
36754     }
36755     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36756     // ??? needed ??? config.el = this.wrapper;
36757     
36758     
36759     
36760   
36761     // xtype created footer. - not sure if will work as we normally have to render first..
36762     if (this.footer && !this.footer.el && this.footer.xtype) {
36763         
36764         var ctr = this.grid.getView().getFooterPanel(true);
36765         this.footer.dataSource = this.grid.dataSource;
36766         this.footer = Roo.factory(this.footer, Roo);
36767         this.footer.render(ctr);
36768         
36769     }
36770     
36771     
36772     
36773     
36774      
36775 };
36776
36777 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36778     getId : function(){
36779         return this.grid.id;
36780     },
36781     
36782     /**
36783      * Returns the grid for this panel
36784      * @return {Roo.bootstrap.Table} 
36785      */
36786     getGrid : function(){
36787         return this.grid;    
36788     },
36789     
36790     setSize : function(width, height){
36791         if(!this.ignoreResize(width, height)){
36792             var grid = this.grid;
36793             var size = this.adjustForComponents(width, height);
36794             var gridel = grid.getGridEl();
36795             gridel.setSize(size.width, size.height);
36796             /*
36797             var thd = grid.getGridEl().select('thead',true).first();
36798             var tbd = grid.getGridEl().select('tbody', true).first();
36799             if (tbd) {
36800                 tbd.setSize(width, height - thd.getHeight());
36801             }
36802             */
36803             grid.autoSize();
36804         }
36805     },
36806      
36807     
36808     
36809     beforeSlide : function(){
36810         this.grid.getView().scroller.clip();
36811     },
36812     
36813     afterSlide : function(){
36814         this.grid.getView().scroller.unclip();
36815     },
36816     
36817     destroy : function(){
36818         this.grid.destroy();
36819         delete this.grid;
36820         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36821     }
36822 });
36823
36824 /**
36825  * @class Roo.bootstrap.panel.Nest
36826  * @extends Roo.bootstrap.panel.Content
36827  * @constructor
36828  * Create a new Panel, that can contain a layout.Border.
36829  * 
36830  * 
36831  * @param {Roo.BorderLayout} layout The layout for this panel
36832  * @param {String/Object} config A string to set only the title or a config object
36833  */
36834 Roo.bootstrap.panel.Nest = function(config)
36835 {
36836     // construct with only one argument..
36837     /* FIXME - implement nicer consturctors
36838     if (layout.layout) {
36839         config = layout;
36840         layout = config.layout;
36841         delete config.layout;
36842     }
36843     if (layout.xtype && !layout.getEl) {
36844         // then layout needs constructing..
36845         layout = Roo.factory(layout, Roo);
36846     }
36847     */
36848     
36849     config.el =  config.layout.getEl();
36850     
36851     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36852     
36853     config.layout.monitorWindowResize = false; // turn off autosizing
36854     this.layout = config.layout;
36855     this.layout.getEl().addClass("roo-layout-nested-layout");
36856     
36857     
36858     
36859     
36860 };
36861
36862 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36863
36864     setSize : function(width, height){
36865         if(!this.ignoreResize(width, height)){
36866             var size = this.adjustForComponents(width, height);
36867             var el = this.layout.getEl();
36868             if (size.height < 1) {
36869                 el.setWidth(size.width);   
36870             } else {
36871                 el.setSize(size.width, size.height);
36872             }
36873             var touch = el.dom.offsetWidth;
36874             this.layout.layout();
36875             // ie requires a double layout on the first pass
36876             if(Roo.isIE && !this.initialized){
36877                 this.initialized = true;
36878                 this.layout.layout();
36879             }
36880         }
36881     },
36882     
36883     // activate all subpanels if not currently active..
36884     
36885     setActiveState : function(active){
36886         this.active = active;
36887         this.setActiveClass(active);
36888         
36889         if(!active){
36890             this.fireEvent("deactivate", this);
36891             return;
36892         }
36893         
36894         this.fireEvent("activate", this);
36895         // not sure if this should happen before or after..
36896         if (!this.layout) {
36897             return; // should not happen..
36898         }
36899         var reg = false;
36900         for (var r in this.layout.regions) {
36901             reg = this.layout.getRegion(r);
36902             if (reg.getActivePanel()) {
36903                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36904                 reg.setActivePanel(reg.getActivePanel());
36905                 continue;
36906             }
36907             if (!reg.panels.length) {
36908                 continue;
36909             }
36910             reg.showPanel(reg.getPanel(0));
36911         }
36912         
36913         
36914         
36915         
36916     },
36917     
36918     /**
36919      * Returns the nested BorderLayout for this panel
36920      * @return {Roo.BorderLayout} 
36921      */
36922     getLayout : function(){
36923         return this.layout;
36924     },
36925     
36926      /**
36927      * Adds a xtype elements to the layout of the nested panel
36928      * <pre><code>
36929
36930 panel.addxtype({
36931        xtype : 'ContentPanel',
36932        region: 'west',
36933        items: [ .... ]
36934    }
36935 );
36936
36937 panel.addxtype({
36938         xtype : 'NestedLayoutPanel',
36939         region: 'west',
36940         layout: {
36941            center: { },
36942            west: { }   
36943         },
36944         items : [ ... list of content panels or nested layout panels.. ]
36945    }
36946 );
36947 </code></pre>
36948      * @param {Object} cfg Xtype definition of item to add.
36949      */
36950     addxtype : function(cfg) {
36951         return this.layout.addxtype(cfg);
36952     
36953     }
36954 });        /*
36955  * Based on:
36956  * Ext JS Library 1.1.1
36957  * Copyright(c) 2006-2007, Ext JS, LLC.
36958  *
36959  * Originally Released Under LGPL - original licence link has changed is not relivant.
36960  *
36961  * Fork - LGPL
36962  * <script type="text/javascript">
36963  */
36964 /**
36965  * @class Roo.TabPanel
36966  * @extends Roo.util.Observable
36967  * A lightweight tab container.
36968  * <br><br>
36969  * Usage:
36970  * <pre><code>
36971 // basic tabs 1, built from existing content
36972 var tabs = new Roo.TabPanel("tabs1");
36973 tabs.addTab("script", "View Script");
36974 tabs.addTab("markup", "View Markup");
36975 tabs.activate("script");
36976
36977 // more advanced tabs, built from javascript
36978 var jtabs = new Roo.TabPanel("jtabs");
36979 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36980
36981 // set up the UpdateManager
36982 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36983 var updater = tab2.getUpdateManager();
36984 updater.setDefaultUrl("ajax1.htm");
36985 tab2.on('activate', updater.refresh, updater, true);
36986
36987 // Use setUrl for Ajax loading
36988 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36989 tab3.setUrl("ajax2.htm", null, true);
36990
36991 // Disabled tab
36992 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36993 tab4.disable();
36994
36995 jtabs.activate("jtabs-1");
36996  * </code></pre>
36997  * @constructor
36998  * Create a new TabPanel.
36999  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37000  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37001  */
37002 Roo.bootstrap.panel.Tabs = function(config){
37003     /**
37004     * The container element for this TabPanel.
37005     * @type Roo.Element
37006     */
37007     this.el = Roo.get(config.el);
37008     delete config.el;
37009     if(config){
37010         if(typeof config == "boolean"){
37011             this.tabPosition = config ? "bottom" : "top";
37012         }else{
37013             Roo.apply(this, config);
37014         }
37015     }
37016     
37017     if(this.tabPosition == "bottom"){
37018         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37019         this.el.addClass("roo-tabs-bottom");
37020     }
37021     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37022     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37023     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37024     if(Roo.isIE){
37025         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37026     }
37027     if(this.tabPosition != "bottom"){
37028         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37029          * @type Roo.Element
37030          */
37031         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37032         this.el.addClass("roo-tabs-top");
37033     }
37034     this.items = [];
37035
37036     this.bodyEl.setStyle("position", "relative");
37037
37038     this.active = null;
37039     this.activateDelegate = this.activate.createDelegate(this);
37040
37041     this.addEvents({
37042         /**
37043          * @event tabchange
37044          * Fires when the active tab changes
37045          * @param {Roo.TabPanel} this
37046          * @param {Roo.TabPanelItem} activePanel The new active tab
37047          */
37048         "tabchange": true,
37049         /**
37050          * @event beforetabchange
37051          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37052          * @param {Roo.TabPanel} this
37053          * @param {Object} e Set cancel to true on this object to cancel the tab change
37054          * @param {Roo.TabPanelItem} tab The tab being changed to
37055          */
37056         "beforetabchange" : true
37057     });
37058
37059     Roo.EventManager.onWindowResize(this.onResize, this);
37060     this.cpad = this.el.getPadding("lr");
37061     this.hiddenCount = 0;
37062
37063
37064     // toolbar on the tabbar support...
37065     if (this.toolbar) {
37066         alert("no toolbar support yet");
37067         this.toolbar  = false;
37068         /*
37069         var tcfg = this.toolbar;
37070         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37071         this.toolbar = new Roo.Toolbar(tcfg);
37072         if (Roo.isSafari) {
37073             var tbl = tcfg.container.child('table', true);
37074             tbl.setAttribute('width', '100%');
37075         }
37076         */
37077         
37078     }
37079    
37080
37081
37082     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37083 };
37084
37085 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37086     /*
37087      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37088      */
37089     tabPosition : "top",
37090     /*
37091      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37092      */
37093     currentTabWidth : 0,
37094     /*
37095      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37096      */
37097     minTabWidth : 40,
37098     /*
37099      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37100      */
37101     maxTabWidth : 250,
37102     /*
37103      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37104      */
37105     preferredTabWidth : 175,
37106     /*
37107      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37108      */
37109     resizeTabs : false,
37110     /*
37111      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37112      */
37113     monitorResize : true,
37114     /*
37115      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37116      */
37117     toolbar : false,
37118
37119     /**
37120      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37121      * @param {String} id The id of the div to use <b>or create</b>
37122      * @param {String} text The text for the tab
37123      * @param {String} content (optional) Content to put in the TabPanelItem body
37124      * @param {Boolean} closable (optional) True to create a close icon on the tab
37125      * @return {Roo.TabPanelItem} The created TabPanelItem
37126      */
37127     addTab : function(id, text, content, closable, tpl)
37128     {
37129         var item = new Roo.bootstrap.panel.TabItem({
37130             panel: this,
37131             id : id,
37132             text : text,
37133             closable : closable,
37134             tpl : tpl
37135         });
37136         this.addTabItem(item);
37137         if(content){
37138             item.setContent(content);
37139         }
37140         return item;
37141     },
37142
37143     /**
37144      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37145      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37146      * @return {Roo.TabPanelItem}
37147      */
37148     getTab : function(id){
37149         return this.items[id];
37150     },
37151
37152     /**
37153      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37154      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37155      */
37156     hideTab : function(id){
37157         var t = this.items[id];
37158         if(!t.isHidden()){
37159            t.setHidden(true);
37160            this.hiddenCount++;
37161            this.autoSizeTabs();
37162         }
37163     },
37164
37165     /**
37166      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37167      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37168      */
37169     unhideTab : function(id){
37170         var t = this.items[id];
37171         if(t.isHidden()){
37172            t.setHidden(false);
37173            this.hiddenCount--;
37174            this.autoSizeTabs();
37175         }
37176     },
37177
37178     /**
37179      * Adds an existing {@link Roo.TabPanelItem}.
37180      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37181      */
37182     addTabItem : function(item){
37183         this.items[item.id] = item;
37184         this.items.push(item);
37185       //  if(this.resizeTabs){
37186     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37187   //         this.autoSizeTabs();
37188 //        }else{
37189 //            item.autoSize();
37190        // }
37191     },
37192
37193     /**
37194      * Removes a {@link Roo.TabPanelItem}.
37195      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37196      */
37197     removeTab : function(id){
37198         var items = this.items;
37199         var tab = items[id];
37200         if(!tab) { return; }
37201         var index = items.indexOf(tab);
37202         if(this.active == tab && items.length > 1){
37203             var newTab = this.getNextAvailable(index);
37204             if(newTab) {
37205                 newTab.activate();
37206             }
37207         }
37208         this.stripEl.dom.removeChild(tab.pnode.dom);
37209         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37210             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37211         }
37212         items.splice(index, 1);
37213         delete this.items[tab.id];
37214         tab.fireEvent("close", tab);
37215         tab.purgeListeners();
37216         this.autoSizeTabs();
37217     },
37218
37219     getNextAvailable : function(start){
37220         var items = this.items;
37221         var index = start;
37222         // look for a next tab that will slide over to
37223         // replace the one being removed
37224         while(index < items.length){
37225             var item = items[++index];
37226             if(item && !item.isHidden()){
37227                 return item;
37228             }
37229         }
37230         // if one isn't found select the previous tab (on the left)
37231         index = start;
37232         while(index >= 0){
37233             var item = items[--index];
37234             if(item && !item.isHidden()){
37235                 return item;
37236             }
37237         }
37238         return null;
37239     },
37240
37241     /**
37242      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37243      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37244      */
37245     disableTab : function(id){
37246         var tab = this.items[id];
37247         if(tab && this.active != tab){
37248             tab.disable();
37249         }
37250     },
37251
37252     /**
37253      * Enables a {@link Roo.TabPanelItem} that is disabled.
37254      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37255      */
37256     enableTab : function(id){
37257         var tab = this.items[id];
37258         tab.enable();
37259     },
37260
37261     /**
37262      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37263      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37264      * @return {Roo.TabPanelItem} The TabPanelItem.
37265      */
37266     activate : function(id){
37267         var tab = this.items[id];
37268         if(!tab){
37269             return null;
37270         }
37271         if(tab == this.active || tab.disabled){
37272             return tab;
37273         }
37274         var e = {};
37275         this.fireEvent("beforetabchange", this, e, tab);
37276         if(e.cancel !== true && !tab.disabled){
37277             if(this.active){
37278                 this.active.hide();
37279             }
37280             this.active = this.items[id];
37281             this.active.show();
37282             this.fireEvent("tabchange", this, this.active);
37283         }
37284         return tab;
37285     },
37286
37287     /**
37288      * Gets the active {@link Roo.TabPanelItem}.
37289      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37290      */
37291     getActiveTab : function(){
37292         return this.active;
37293     },
37294
37295     /**
37296      * Updates the tab body element to fit the height of the container element
37297      * for overflow scrolling
37298      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37299      */
37300     syncHeight : function(targetHeight){
37301         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37302         var bm = this.bodyEl.getMargins();
37303         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37304         this.bodyEl.setHeight(newHeight);
37305         return newHeight;
37306     },
37307
37308     onResize : function(){
37309         if(this.monitorResize){
37310             this.autoSizeTabs();
37311         }
37312     },
37313
37314     /**
37315      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37316      */
37317     beginUpdate : function(){
37318         this.updating = true;
37319     },
37320
37321     /**
37322      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37323      */
37324     endUpdate : function(){
37325         this.updating = false;
37326         this.autoSizeTabs();
37327     },
37328
37329     /**
37330      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37331      */
37332     autoSizeTabs : function(){
37333         var count = this.items.length;
37334         var vcount = count - this.hiddenCount;
37335         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37336             return;
37337         }
37338         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37339         var availWidth = Math.floor(w / vcount);
37340         var b = this.stripBody;
37341         if(b.getWidth() > w){
37342             var tabs = this.items;
37343             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37344             if(availWidth < this.minTabWidth){
37345                 /*if(!this.sleft){    // incomplete scrolling code
37346                     this.createScrollButtons();
37347                 }
37348                 this.showScroll();
37349                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37350             }
37351         }else{
37352             if(this.currentTabWidth < this.preferredTabWidth){
37353                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37354             }
37355         }
37356     },
37357
37358     /**
37359      * Returns the number of tabs in this TabPanel.
37360      * @return {Number}
37361      */
37362      getCount : function(){
37363          return this.items.length;
37364      },
37365
37366     /**
37367      * Resizes all the tabs to the passed width
37368      * @param {Number} The new width
37369      */
37370     setTabWidth : function(width){
37371         this.currentTabWidth = width;
37372         for(var i = 0, len = this.items.length; i < len; i++) {
37373                 if(!this.items[i].isHidden()) {
37374                 this.items[i].setWidth(width);
37375             }
37376         }
37377     },
37378
37379     /**
37380      * Destroys this TabPanel
37381      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37382      */
37383     destroy : function(removeEl){
37384         Roo.EventManager.removeResizeListener(this.onResize, this);
37385         for(var i = 0, len = this.items.length; i < len; i++){
37386             this.items[i].purgeListeners();
37387         }
37388         if(removeEl === true){
37389             this.el.update("");
37390             this.el.remove();
37391         }
37392     },
37393     
37394     createStrip : function(container)
37395     {
37396         var strip = document.createElement("nav");
37397         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37398         container.appendChild(strip);
37399         return strip;
37400     },
37401     
37402     createStripList : function(strip)
37403     {
37404         // div wrapper for retard IE
37405         // returns the "tr" element.
37406         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37407         //'<div class="x-tabs-strip-wrap">'+
37408           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37409           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37410         return strip.firstChild; //.firstChild.firstChild.firstChild;
37411     },
37412     createBody : function(container)
37413     {
37414         var body = document.createElement("div");
37415         Roo.id(body, "tab-body");
37416         //Roo.fly(body).addClass("x-tabs-body");
37417         Roo.fly(body).addClass("tab-content");
37418         container.appendChild(body);
37419         return body;
37420     },
37421     createItemBody :function(bodyEl, id){
37422         var body = Roo.getDom(id);
37423         if(!body){
37424             body = document.createElement("div");
37425             body.id = id;
37426         }
37427         //Roo.fly(body).addClass("x-tabs-item-body");
37428         Roo.fly(body).addClass("tab-pane");
37429          bodyEl.insertBefore(body, bodyEl.firstChild);
37430         return body;
37431     },
37432     /** @private */
37433     createStripElements :  function(stripEl, text, closable, tpl)
37434     {
37435         var td = document.createElement("li"); // was td..
37436         
37437         
37438         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37439         
37440         
37441         stripEl.appendChild(td);
37442         /*if(closable){
37443             td.className = "x-tabs-closable";
37444             if(!this.closeTpl){
37445                 this.closeTpl = new Roo.Template(
37446                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37447                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37448                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37449                 );
37450             }
37451             var el = this.closeTpl.overwrite(td, {"text": text});
37452             var close = el.getElementsByTagName("div")[0];
37453             var inner = el.getElementsByTagName("em")[0];
37454             return {"el": el, "close": close, "inner": inner};
37455         } else {
37456         */
37457         // not sure what this is..
37458 //            if(!this.tabTpl){
37459                 //this.tabTpl = new Roo.Template(
37460                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37461                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37462                 //);
37463 //                this.tabTpl = new Roo.Template(
37464 //                   '<a href="#">' +
37465 //                   '<span unselectable="on"' +
37466 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37467 //                            ' >{text}</span></a>'
37468 //                );
37469 //                
37470 //            }
37471
37472
37473             var template = tpl || this.tabTpl || false;
37474             
37475             if(!template){
37476                 
37477                 template = new Roo.Template(
37478                    '<a href="#">' +
37479                    '<span unselectable="on"' +
37480                             (this.disableTooltips ? '' : ' title="{text}"') +
37481                             ' >{text}</span></a>'
37482                 );
37483             }
37484             
37485             switch (typeof(template)) {
37486                 case 'object' :
37487                     break;
37488                 case 'string' :
37489                     template = new Roo.Template(template);
37490                     break;
37491                 default :
37492                     break;
37493             }
37494             
37495             var el = template.overwrite(td, {"text": text});
37496             
37497             var inner = el.getElementsByTagName("span")[0];
37498             
37499             return {"el": el, "inner": inner};
37500             
37501     }
37502         
37503     
37504 });
37505
37506 /**
37507  * @class Roo.TabPanelItem
37508  * @extends Roo.util.Observable
37509  * Represents an individual item (tab plus body) in a TabPanel.
37510  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37511  * @param {String} id The id of this TabPanelItem
37512  * @param {String} text The text for the tab of this TabPanelItem
37513  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37514  */
37515 Roo.bootstrap.panel.TabItem = function(config){
37516     /**
37517      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37518      * @type Roo.TabPanel
37519      */
37520     this.tabPanel = config.panel;
37521     /**
37522      * The id for this TabPanelItem
37523      * @type String
37524      */
37525     this.id = config.id;
37526     /** @private */
37527     this.disabled = false;
37528     /** @private */
37529     this.text = config.text;
37530     /** @private */
37531     this.loaded = false;
37532     this.closable = config.closable;
37533
37534     /**
37535      * The body element for this TabPanelItem.
37536      * @type Roo.Element
37537      */
37538     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37539     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37540     this.bodyEl.setStyle("display", "block");
37541     this.bodyEl.setStyle("zoom", "1");
37542     //this.hideAction();
37543
37544     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37545     /** @private */
37546     this.el = Roo.get(els.el);
37547     this.inner = Roo.get(els.inner, true);
37548     this.textEl = Roo.get(this.el.dom.firstChild, true);
37549     this.pnode = Roo.get(els.el.parentNode, true);
37550     this.el.on("mousedown", this.onTabMouseDown, this);
37551     this.el.on("click", this.onTabClick, this);
37552     /** @private */
37553     if(config.closable){
37554         var c = Roo.get(els.close, true);
37555         c.dom.title = this.closeText;
37556         c.addClassOnOver("close-over");
37557         c.on("click", this.closeClick, this);
37558      }
37559
37560     this.addEvents({
37561          /**
37562          * @event activate
37563          * Fires when this tab becomes the active tab.
37564          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37565          * @param {Roo.TabPanelItem} this
37566          */
37567         "activate": true,
37568         /**
37569          * @event beforeclose
37570          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37571          * @param {Roo.TabPanelItem} this
37572          * @param {Object} e Set cancel to true on this object to cancel the close.
37573          */
37574         "beforeclose": true,
37575         /**
37576          * @event close
37577          * Fires when this tab is closed.
37578          * @param {Roo.TabPanelItem} this
37579          */
37580          "close": true,
37581         /**
37582          * @event deactivate
37583          * Fires when this tab is no longer the active tab.
37584          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37585          * @param {Roo.TabPanelItem} this
37586          */
37587          "deactivate" : true
37588     });
37589     this.hidden = false;
37590
37591     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37592 };
37593
37594 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37595            {
37596     purgeListeners : function(){
37597        Roo.util.Observable.prototype.purgeListeners.call(this);
37598        this.el.removeAllListeners();
37599     },
37600     /**
37601      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37602      */
37603     show : function(){
37604         this.pnode.addClass("active");
37605         this.showAction();
37606         if(Roo.isOpera){
37607             this.tabPanel.stripWrap.repaint();
37608         }
37609         this.fireEvent("activate", this.tabPanel, this);
37610     },
37611
37612     /**
37613      * Returns true if this tab is the active tab.
37614      * @return {Boolean}
37615      */
37616     isActive : function(){
37617         return this.tabPanel.getActiveTab() == this;
37618     },
37619
37620     /**
37621      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37622      */
37623     hide : function(){
37624         this.pnode.removeClass("active");
37625         this.hideAction();
37626         this.fireEvent("deactivate", this.tabPanel, this);
37627     },
37628
37629     hideAction : function(){
37630         this.bodyEl.hide();
37631         this.bodyEl.setStyle("position", "absolute");
37632         this.bodyEl.setLeft("-20000px");
37633         this.bodyEl.setTop("-20000px");
37634     },
37635
37636     showAction : function(){
37637         this.bodyEl.setStyle("position", "relative");
37638         this.bodyEl.setTop("");
37639         this.bodyEl.setLeft("");
37640         this.bodyEl.show();
37641     },
37642
37643     /**
37644      * Set the tooltip for the tab.
37645      * @param {String} tooltip The tab's tooltip
37646      */
37647     setTooltip : function(text){
37648         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37649             this.textEl.dom.qtip = text;
37650             this.textEl.dom.removeAttribute('title');
37651         }else{
37652             this.textEl.dom.title = text;
37653         }
37654     },
37655
37656     onTabClick : function(e){
37657         e.preventDefault();
37658         this.tabPanel.activate(this.id);
37659     },
37660
37661     onTabMouseDown : function(e){
37662         e.preventDefault();
37663         this.tabPanel.activate(this.id);
37664     },
37665 /*
37666     getWidth : function(){
37667         return this.inner.getWidth();
37668     },
37669
37670     setWidth : function(width){
37671         var iwidth = width - this.pnode.getPadding("lr");
37672         this.inner.setWidth(iwidth);
37673         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37674         this.pnode.setWidth(width);
37675     },
37676 */
37677     /**
37678      * Show or hide the tab
37679      * @param {Boolean} hidden True to hide or false to show.
37680      */
37681     setHidden : function(hidden){
37682         this.hidden = hidden;
37683         this.pnode.setStyle("display", hidden ? "none" : "");
37684     },
37685
37686     /**
37687      * Returns true if this tab is "hidden"
37688      * @return {Boolean}
37689      */
37690     isHidden : function(){
37691         return this.hidden;
37692     },
37693
37694     /**
37695      * Returns the text for this tab
37696      * @return {String}
37697      */
37698     getText : function(){
37699         return this.text;
37700     },
37701     /*
37702     autoSize : function(){
37703         //this.el.beginMeasure();
37704         this.textEl.setWidth(1);
37705         /*
37706          *  #2804 [new] Tabs in Roojs
37707          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37708          */
37709         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37710         //this.el.endMeasure();
37711     //},
37712
37713     /**
37714      * Sets the text for the tab (Note: this also sets the tooltip text)
37715      * @param {String} text The tab's text and tooltip
37716      */
37717     setText : function(text){
37718         this.text = text;
37719         this.textEl.update(text);
37720         this.setTooltip(text);
37721         //if(!this.tabPanel.resizeTabs){
37722         //    this.autoSize();
37723         //}
37724     },
37725     /**
37726      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37727      */
37728     activate : function(){
37729         this.tabPanel.activate(this.id);
37730     },
37731
37732     /**
37733      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37734      */
37735     disable : function(){
37736         if(this.tabPanel.active != this){
37737             this.disabled = true;
37738             this.pnode.addClass("disabled");
37739         }
37740     },
37741
37742     /**
37743      * Enables this TabPanelItem if it was previously disabled.
37744      */
37745     enable : function(){
37746         this.disabled = false;
37747         this.pnode.removeClass("disabled");
37748     },
37749
37750     /**
37751      * Sets the content for this TabPanelItem.
37752      * @param {String} content The content
37753      * @param {Boolean} loadScripts true to look for and load scripts
37754      */
37755     setContent : function(content, loadScripts){
37756         this.bodyEl.update(content, loadScripts);
37757     },
37758
37759     /**
37760      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37761      * @return {Roo.UpdateManager} The UpdateManager
37762      */
37763     getUpdateManager : function(){
37764         return this.bodyEl.getUpdateManager();
37765     },
37766
37767     /**
37768      * Set a URL to be used to load the content for this TabPanelItem.
37769      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37770      * @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)
37771      * @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)
37772      * @return {Roo.UpdateManager} The UpdateManager
37773      */
37774     setUrl : function(url, params, loadOnce){
37775         if(this.refreshDelegate){
37776             this.un('activate', this.refreshDelegate);
37777         }
37778         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37779         this.on("activate", this.refreshDelegate);
37780         return this.bodyEl.getUpdateManager();
37781     },
37782
37783     /** @private */
37784     _handleRefresh : function(url, params, loadOnce){
37785         if(!loadOnce || !this.loaded){
37786             var updater = this.bodyEl.getUpdateManager();
37787             updater.update(url, params, this._setLoaded.createDelegate(this));
37788         }
37789     },
37790
37791     /**
37792      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37793      *   Will fail silently if the setUrl method has not been called.
37794      *   This does not activate the panel, just updates its content.
37795      */
37796     refresh : function(){
37797         if(this.refreshDelegate){
37798            this.loaded = false;
37799            this.refreshDelegate();
37800         }
37801     },
37802
37803     /** @private */
37804     _setLoaded : function(){
37805         this.loaded = true;
37806     },
37807
37808     /** @private */
37809     closeClick : function(e){
37810         var o = {};
37811         e.stopEvent();
37812         this.fireEvent("beforeclose", this, o);
37813         if(o.cancel !== true){
37814             this.tabPanel.removeTab(this.id);
37815         }
37816     },
37817     /**
37818      * The text displayed in the tooltip for the close icon.
37819      * @type String
37820      */
37821     closeText : "Close this tab"
37822 });