sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.weightClass = ["btn-default", 
538                        "btn-primary", 
539                        "btn-success", 
540                        "btn-info", 
541                        "btn-warning",
542                        "btn-danger",
543                        "btn-link"
544                       ],  
545     this.addEvents({
546         // raw events
547         /**
548          * @event click
549          * When a butotn is pressed
550          * @param {Roo.bootstrap.Button} this
551          * @param {Roo.EventObject} e
552          */
553         "click" : true,
554          /**
555          * @event toggle
556          * After the button has been toggles
557          * @param {Roo.EventObject} e
558          * @param {boolean} pressed (also available as button.pressed)
559          */
560         "toggle" : true
561     });
562 };
563
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
565     html: false,
566     active: false,
567     weight: '',
568     size: '',
569     tag: 'button',
570     href: '',
571     disabled: false,
572     isClose: false,
573     glyphicon: '',
574     badge: '',
575     theme: 'default',
576     inverse: false,
577     
578     toggle: false,
579     ontext: 'ON',
580     offtext: 'OFF',
581     defaulton: true,
582     preventDefault: true,
583     removeClass: false,
584     name: false,
585     target: false,
586     
587     
588     pressed : null,
589      
590     
591     getAutoCreate : function(){
592         
593         var cfg = {
594             tag : 'button',
595             cls : 'roo-button',
596             html: ''
597         };
598         
599         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
601             this.tag = 'button';
602         } else {
603             cfg.tag = this.tag;
604         }
605         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606         
607         if (this.toggle == true) {
608             cfg={
609                 tag: 'div',
610                 cls: 'slider-frame roo-button',
611                 cn: [
612                     {
613                         tag: 'span',
614                         'data-on-text':'ON',
615                         'data-off-text':'OFF',
616                         cls: 'slider-button',
617                         html: this.offtext
618                     }
619                 ]
620             };
621             
622             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623                 cfg.cls += ' '+this.weight;
624             }
625             
626             return cfg;
627         }
628         
629         if (this.isClose) {
630             cfg.cls += ' close';
631             
632             cfg["aria-hidden"] = true;
633             
634             cfg.html = "&times;";
635             
636             return cfg;
637         }
638         
639          
640         if (this.theme==='default') {
641             cfg.cls = 'btn roo-button';
642             
643             //if (this.parentType != 'Navbar') {
644             this.weight = this.weight.length ?  this.weight : 'default';
645             //}
646             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647                 
648                 cfg.cls += ' btn-' + this.weight;
649             }
650         } else if (this.theme==='glow') {
651             
652             cfg.tag = 'a';
653             cfg.cls = 'btn-glow roo-button';
654             
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' ' + this.weight;
658             }
659         }
660    
661         
662         if (this.inverse) {
663             this.cls += ' inverse';
664         }
665         
666         
667         if (this.active) {
668             cfg.cls += ' active';
669         }
670         
671         if (this.disabled) {
672             cfg.disabled = 'disabled';
673         }
674         
675         if (this.items) {
676             Roo.log('changing to ul' );
677             cfg.tag = 'ul';
678             this.glyphicon = 'caret';
679         }
680         
681         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682          
683         //gsRoo.log(this.parentType);
684         if (this.parentType === 'Navbar' && !this.parent().bar) {
685             Roo.log('changing to li?');
686             
687             cfg.tag = 'li';
688             
689             cfg.cls = '';
690             cfg.cn =  [{
691                 tag : 'a',
692                 cls : 'roo-button',
693                 html : this.html,
694                 href : this.href || '#'
695             }];
696             if (this.menu) {
697                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
698                 cfg.cls += ' dropdown';
699             }   
700             
701             delete cfg.html;
702             
703         }
704         
705        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
706         
707         if (this.glyphicon) {
708             cfg.html = ' ' + cfg.html;
709             
710             cfg.cn = [
711                 {
712                     tag: 'span',
713                     cls: 'glyphicon glyphicon-' + this.glyphicon
714                 }
715             ];
716         }
717         
718         if (this.badge) {
719             cfg.html += ' ';
720             
721             cfg.tag = 'a';
722             
723 //            cfg.cls='btn roo-button';
724             
725             cfg.href=this.href;
726             
727             var value = cfg.html;
728             
729             if(this.glyphicon){
730                 value = {
731                             tag: 'span',
732                             cls: 'glyphicon glyphicon-' + this.glyphicon,
733                             html: this.html
734                         };
735                 
736             }
737             
738             cfg.cn = [
739                 value,
740                 {
741                     tag: 'span',
742                     cls: 'badge',
743                     html: this.badge
744                 }
745             ];
746             
747             cfg.html='';
748         }
749         
750         if (this.menu) {
751             cfg.cls += ' dropdown';
752             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
753         }
754         
755         if (cfg.tag !== 'a' && this.href !== '') {
756             throw "Tag must be a to set href.";
757         } else if (this.href.length > 0) {
758             cfg.href = this.href;
759         }
760         
761         if(this.removeClass){
762             cfg.cls = '';
763         }
764         
765         if(this.target){
766             cfg.target = this.target;
767         }
768         
769         return cfg;
770     },
771     initEvents: function() {
772        // Roo.log('init events?');
773 //        Roo.log(this.el.dom);
774         // add the menu...
775         
776         if (typeof (this.menu) != 'undefined') {
777             this.menu.parentType = this.xtype;
778             this.menu.triggerEl = this.el;
779             this.addxtype(Roo.apply({}, this.menu));
780         }
781
782
783        if (this.el.hasClass('roo-button')) {
784             this.el.on('click', this.onClick, this);
785        } else {
786             this.el.select('.roo-button').on('click', this.onClick, this);
787        }
788        
789        if(this.removeClass){
790            this.el.on('click', this.onClick, this);
791        }
792        
793        this.el.enableDisplayMode();
794         
795     },
796     onClick : function(e)
797     {
798         if (this.disabled) {
799             return;
800         }
801         
802         
803         Roo.log('button on click ');
804         if(this.preventDefault){
805             e.preventDefault();
806         }
807         if (this.pressed === true || this.pressed === false) {
808             this.pressed = !this.pressed;
809             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810             this.fireEvent('toggle', this, e, this.pressed);
811         }
812         
813         
814         this.fireEvent('click', this, e);
815     },
816     
817     /**
818      * Enables this button
819      */
820     enable : function()
821     {
822         this.disabled = false;
823         this.el.removeClass('disabled');
824     },
825     
826     /**
827      * Disable this button
828      */
829     disable : function()
830     {
831         this.disabled = true;
832         this.el.addClass('disabled');
833     },
834      /**
835      * sets the active state on/off, 
836      * @param {Boolean} state (optional) Force a particular state
837      */
838     setActive : function(v) {
839         
840         this.el[v ? 'addClass' : 'removeClass']('active');
841     },
842      /**
843      * toggles the current active state 
844      */
845     toggleActive : function()
846     {
847        var active = this.el.hasClass('active');
848        this.setActive(!active);
849        
850         
851     },
852     setText : function(str)
853     {
854         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
855     },
856     getText : function()
857     {
858         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859     },
860     hide: function() {
861        
862      
863         this.el.hide();   
864     },
865     show: function() {
866        
867         this.el.show();   
868     },
869     setWeight : function(str)
870     {
871           this.el.removeClass(this.weightClass);
872         this.el.addClass('btn-' + str);        
873     }
874     
875     
876 });
877
878  /*
879  * - LGPL
880  *
881  * column
882  * 
883  */
884
885 /**
886  * @class Roo.bootstrap.Column
887  * @extends Roo.bootstrap.Component
888  * Bootstrap Column class
889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
897  *
898  * 
899  * @cfg {Boolean} hidden (true|false) hide the element
900  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901  * @cfg {String} fa (ban|check|...) font awesome icon
902  * @cfg {Number} fasize (1|2|....) font awsome size
903
904  * @cfg {String} icon (info-sign|check|...) glyphicon name
905
906  * @cfg {String} html content of column.
907  * 
908  * @constructor
909  * Create a new Column
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.Column = function(config){
914     Roo.bootstrap.Column.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
918     
919     xs: false,
920     sm: false,
921     md: false,
922     lg: false,
923     xsoff: false,
924     smoff: false,
925     mdoff: false,
926     lgoff: false,
927     html: '',
928     offset: 0,
929     alert: false,
930     fa: false,
931     icon : false,
932     hidden : false,
933     fasize : 1,
934     
935     getAutoCreate : function(){
936         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
937         
938         cfg = {
939             tag: 'div',
940             cls: 'column'
941         };
942         
943         var settings=this;
944         ['xs','sm','md','lg'].map(function(size){
945             //Roo.log( size + ':' + settings[size]);
946             
947             if (settings[size+'off'] !== false) {
948                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
949             }
950             
951             if (settings[size] === false) {
952                 return;
953             }
954             
955             if (!settings[size]) { // 0 = hidden
956                 cfg.cls += ' hidden-' + size;
957                 return;
958             }
959             cfg.cls += ' col-' + size + '-' + settings[size];
960             
961         });
962         
963         if (this.hidden) {
964             cfg.cls += ' hidden';
965         }
966         
967         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968             cfg.cls +=' alert alert-' + this.alert;
969         }
970         
971         
972         if (this.html.length) {
973             cfg.html = this.html;
974         }
975         if (this.fa) {
976             var fasize = '';
977             if (this.fasize > 1) {
978                 fasize = ' fa-' + this.fasize + 'x';
979             }
980             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
981             
982             
983         }
984         if (this.icon) {
985             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
986         }
987         
988         return cfg;
989     }
990    
991 });
992
993  
994
995  /*
996  * - LGPL
997  *
998  * page container.
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Container
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Container class
1007  * @cfg {Boolean} jumbotron is it a jumbotron element
1008  * @cfg {String} html content of element
1009  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1011  * @cfg {String} header content of header (for panel)
1012  * @cfg {String} footer content of footer (for panel)
1013  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014  * @cfg {String} tag (header|aside|section) type of HTML tag.
1015  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016  * @cfg {String} fa font awesome icon
1017  * @cfg {String} icon (info-sign|check|...) glyphicon name
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {Boolean} expandable (true|false) default false
1020  * @cfg {Boolean} expanded (true|false) default true
1021  * @cfg {String} rheader contet on the right of header
1022  * @cfg {Boolean} clickable (true|false) default false
1023
1024  *     
1025  * @constructor
1026  * Create a new Container
1027  * @param {Object} config The config object
1028  */
1029
1030 Roo.bootstrap.Container = function(config){
1031     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035          /**
1036          * @event expand
1037          * After the panel has been expand
1038          * 
1039          * @param {Roo.bootstrap.Container} this
1040          */
1041         "expand" : true,
1042         /**
1043          * @event collapse
1044          * After the panel has been collapsed
1045          * 
1046          * @param {Roo.bootstrap.Container} this
1047          */
1048         "collapse" : true,
1049         /**
1050          * @event click
1051          * When a element is chick
1052          * @param {Roo.bootstrap.Container} this
1053          * @param {Roo.EventObject} e
1054          */
1055         "click" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1060     
1061     jumbotron : false,
1062     well: '',
1063     panel : '',
1064     header: '',
1065     footer : '',
1066     sticky: '',
1067     tag : false,
1068     alert : false,
1069     fa: false,
1070     icon : false,
1071     expandable : false,
1072     rheader : '',
1073     expanded : true,
1074     clickable: false,
1075   
1076      
1077     getChildContainer : function() {
1078         
1079         if(!this.el){
1080             return false;
1081         }
1082         
1083         if (this.panel.length) {
1084             return this.el.select('.panel-body',true).first();
1085         }
1086         
1087         return this.el;
1088     },
1089     
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : this.tag || 'div',
1095             html : '',
1096             cls : ''
1097         };
1098         if (this.jumbotron) {
1099             cfg.cls = 'jumbotron';
1100         }
1101         
1102         
1103         
1104         // - this is applied by the parent..
1105         //if (this.cls) {
1106         //    cfg.cls = this.cls + '';
1107         //}
1108         
1109         if (this.sticky.length) {
1110             
1111             var bd = Roo.get(document.body);
1112             if (!bd.hasClass('bootstrap-sticky')) {
1113                 bd.addClass('bootstrap-sticky');
1114                 Roo.select('html',true).setStyle('height', '100%');
1115             }
1116              
1117             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1118         }
1119         
1120         
1121         if (this.well.length) {
1122             switch (this.well) {
1123                 case 'lg':
1124                 case 'sm':
1125                     cfg.cls +=' well well-' +this.well;
1126                     break;
1127                 default:
1128                     cfg.cls +=' well';
1129                     break;
1130             }
1131         }
1132         
1133         if (this.hidden) {
1134             cfg.cls += ' hidden';
1135         }
1136         
1137         
1138         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139             cfg.cls +=' alert alert-' + this.alert;
1140         }
1141         
1142         var body = cfg;
1143         
1144         if (this.panel.length) {
1145             cfg.cls += ' panel panel-' + this.panel;
1146             cfg.cn = [];
1147             if (this.header.length) {
1148                 
1149                 var h = [];
1150                 
1151                 if(this.expandable){
1152                     
1153                     cfg.cls = cfg.cls + ' expandable';
1154                     
1155                     h.push({
1156                         tag: 'i',
1157                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1158                     });
1159                     
1160                 }
1161                 
1162                 h.push(
1163                     {
1164                         tag: 'span',
1165                         cls : 'panel-title',
1166                         html : (this.expandable ? '&nbsp;' : '') + this.header
1167                     },
1168                     {
1169                         tag: 'span',
1170                         cls: 'panel-header-right',
1171                         html: this.rheader
1172                     }
1173                 );
1174                 
1175                 cfg.cn.push({
1176                     cls : 'panel-heading',
1177                     style : this.expandable ? 'cursor: pointer' : '',
1178                     cn : h
1179                 });
1180                 
1181             }
1182             
1183             body = false;
1184             cfg.cn.push({
1185                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1186                 html : this.html
1187             });
1188             
1189             
1190             if (this.footer.length) {
1191                 cfg.cn.push({
1192                     cls : 'panel-footer',
1193                     html : this.footer
1194                     
1195                 });
1196             }
1197             
1198         }
1199         
1200         if (body) {
1201             body.html = this.html || cfg.html;
1202             // prefix with the icons..
1203             if (this.fa) {
1204                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1205             }
1206             if (this.icon) {
1207                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1208             }
1209             
1210             
1211         }
1212         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213             cfg.cls =  'container';
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() 
1220     {
1221         if(this.expandable){
1222             var headerEl = this.headerEl();
1223         
1224             if(headerEl){
1225                 headerEl.on('click', this.onToggleClick, this);
1226             }
1227         }
1228         
1229         if(this.clickable){
1230             this.el.on('click', this.onClick, this);
1231         }
1232         
1233     },
1234     
1235     onToggleClick : function()
1236     {
1237         var headerEl = this.headerEl();
1238         
1239         if(!headerEl){
1240             return;
1241         }
1242         
1243         if(this.expanded){
1244             this.collapse();
1245             return;
1246         }
1247         
1248         this.expand();
1249     },
1250     
1251     expand : function()
1252     {
1253         if(this.fireEvent('expand', this)) {
1254             
1255             this.expanded = true;
1256             
1257             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258             
1259             this.el.select('.panel-body',true).first().removeClass('hide');
1260             
1261             var toggleEl = this.toggleEl();
1262
1263             if(!toggleEl){
1264                 return;
1265             }
1266
1267             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1268         }
1269         
1270     },
1271     
1272     collapse : function()
1273     {
1274         if(this.fireEvent('collapse', this)) {
1275             
1276             this.expanded = false;
1277             
1278             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279             this.el.select('.panel-body',true).first().addClass('hide');
1280         
1281             var toggleEl = this.toggleEl();
1282
1283             if(!toggleEl){
1284                 return;
1285             }
1286
1287             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1288         }
1289     },
1290     
1291     toggleEl : function()
1292     {
1293         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1294             return;
1295         }
1296         
1297         return this.el.select('.panel-heading .fa',true).first();
1298     },
1299     
1300     headerEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading',true).first()
1307     },
1308     
1309     bodyEl : function()
1310     {
1311         if(!this.el || !this.panel.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-body',true).first()
1316     },
1317     
1318     titleEl : function()
1319     {
1320         if(!this.el || !this.panel.length || !this.header.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-title',true).first();
1325     },
1326     
1327     setTitle : function(v)
1328     {
1329         var titleEl = this.titleEl();
1330         
1331         if(!titleEl){
1332             return;
1333         }
1334         
1335         titleEl.dom.innerHTML = v;
1336     },
1337     
1338     getTitle : function()
1339     {
1340         
1341         var titleEl = this.titleEl();
1342         
1343         if(!titleEl){
1344             return '';
1345         }
1346         
1347         return titleEl.dom.innerHTML;
1348     },
1349     
1350     setRightTitle : function(v)
1351     {
1352         var t = this.el.select('.panel-header-right',true).first();
1353         
1354         if(!t){
1355             return;
1356         }
1357         
1358         t.dom.innerHTML = v;
1359     },
1360     
1361     onClick : function(e)
1362     {
1363         e.preventDefault();
1364         
1365         this.fireEvent('click', this, e);
1366     }
1367    
1368 });
1369
1370  /*
1371  * - LGPL
1372  *
1373  * image
1374  * 
1375  */
1376
1377
1378 /**
1379  * @class Roo.bootstrap.Img
1380  * @extends Roo.bootstrap.Component
1381  * Bootstrap Img class
1382  * @cfg {Boolean} imgResponsive false | true
1383  * @cfg {String} border rounded | circle | thumbnail
1384  * @cfg {String} src image source
1385  * @cfg {String} alt image alternative text
1386  * @cfg {String} href a tag href
1387  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388  * @cfg {String} xsUrl xs image source
1389  * @cfg {String} smUrl sm image source
1390  * @cfg {String} mdUrl md image source
1391  * @cfg {String} lgUrl lg image source
1392  * 
1393  * @constructor
1394  * Create a new Input
1395  * @param {Object} config The config object
1396  */
1397
1398 Roo.bootstrap.Img = function(config){
1399     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1400     
1401     this.addEvents({
1402         // img events
1403         /**
1404          * @event click
1405          * The img click event for the img.
1406          * @param {Roo.EventObject} e
1407          */
1408         "click" : true
1409     });
1410 };
1411
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1413     
1414     imgResponsive: true,
1415     border: '',
1416     src: 'about:blank',
1417     href: false,
1418     target: false,
1419     xsUrl: '',
1420     smUrl: '',
1421     mdUrl: '',
1422     lgUrl: '',
1423
1424     getAutoCreate : function()
1425     {   
1426         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427             return this.createSingleImg();
1428         }
1429         
1430         var cfg = {
1431             tag: 'div',
1432             cls: 'roo-image-responsive-group',
1433             cn: []
1434         };
1435         var _this = this;
1436         
1437         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438             
1439             if(!_this[size + 'Url']){
1440                 return;
1441             }
1442             
1443             var img = {
1444                 tag: 'img',
1445                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446                 html: _this.html || cfg.html,
1447                 src: _this[size + 'Url']
1448             };
1449             
1450             img.cls += ' roo-image-responsive-' + size;
1451             
1452             var s = ['xs', 'sm', 'md', 'lg'];
1453             
1454             s.splice(s.indexOf(size), 1);
1455             
1456             Roo.each(s, function(ss){
1457                 img.cls += ' hidden-' + ss;
1458             });
1459             
1460             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461                 cfg.cls += ' img-' + _this.border;
1462             }
1463             
1464             if(_this.alt){
1465                 cfg.alt = _this.alt;
1466             }
1467             
1468             if(_this.href){
1469                 var a = {
1470                     tag: 'a',
1471                     href: _this.href,
1472                     cn: [
1473                         img
1474                     ]
1475                 };
1476
1477                 if(this.target){
1478                     a.target = _this.target;
1479                 }
1480             }
1481             
1482             cfg.cn.push((_this.href) ? a : img);
1483             
1484         });
1485         
1486         return cfg;
1487     },
1488     
1489     createSingleImg : function()
1490     {
1491         var cfg = {
1492             tag: 'img',
1493             cls: (this.imgResponsive) ? 'img-responsive' : '',
1494             html : null,
1495             src : 'about:blank'  // just incase src get's set to undefined?!?
1496         };
1497         
1498         cfg.html = this.html || cfg.html;
1499         
1500         cfg.src = this.src || cfg.src;
1501         
1502         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503             cfg.cls += ' img-' + this.border;
1504         }
1505         
1506         if(this.alt){
1507             cfg.alt = this.alt;
1508         }
1509         
1510         if(this.href){
1511             var a = {
1512                 tag: 'a',
1513                 href: this.href,
1514                 cn: [
1515                     cfg
1516                 ]
1517             };
1518             
1519             if(this.target){
1520                 a.target = this.target;
1521             }
1522             
1523         }
1524         
1525         return (this.href) ? a : cfg;
1526     },
1527     
1528     initEvents: function() 
1529     {
1530         if(!this.href){
1531             this.el.on('click', this.onClick, this);
1532         }
1533         
1534     },
1535     
1536     onClick : function(e)
1537     {
1538         Roo.log('img onclick');
1539         this.fireEvent('click', this, e);
1540     },
1541     /**
1542      * Sets the url of the image - used to update it
1543      * @param {String} url the url of the image
1544      */
1545     
1546     setSrc : function(url)
1547     {
1548         this.src =  url;
1549         
1550         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551             this.el.dom.src =  url;
1552             return;
1553         }
1554         
1555         this.el.select('img', true).first().dom.src =  url;
1556     }
1557     
1558     
1559    
1560 });
1561
1562  /*
1563  * - LGPL
1564  *
1565  * image
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Link
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Link Class
1574  * @cfg {String} alt image alternative text
1575  * @cfg {String} href a tag href
1576  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577  * @cfg {String} html the content of the link.
1578  * @cfg {String} anchor name for the anchor link
1579  * @cfg {String} fa - favicon
1580
1581  * @cfg {Boolean} preventDefault (true | false) default false
1582
1583  * 
1584  * @constructor
1585  * Create a new Input
1586  * @param {Object} config The config object
1587  */
1588
1589 Roo.bootstrap.Link = function(config){
1590     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1591     
1592     this.addEvents({
1593         // img events
1594         /**
1595          * @event click
1596          * The img click event for the img.
1597          * @param {Roo.EventObject} e
1598          */
1599         "click" : true
1600     });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1604     
1605     href: false,
1606     target: false,
1607     preventDefault: false,
1608     anchor : false,
1609     alt : false,
1610     fa: false,
1611
1612
1613     getAutoCreate : function()
1614     {
1615         var html = this.html || '';
1616         
1617         if (this.fa !== false) {
1618             html = '<i class="fa fa-' + this.fa + '"></i>';
1619         }
1620         var cfg = {
1621             tag: 'a'
1622         };
1623         // anchor's do not require html/href...
1624         if (this.anchor === false) {
1625             cfg.html = html;
1626             cfg.href = this.href || '#';
1627         } else {
1628             cfg.name = this.anchor;
1629             if (this.html !== false || this.fa !== false) {
1630                 cfg.html = html;
1631             }
1632             if (this.href !== false) {
1633                 cfg.href = this.href;
1634             }
1635         }
1636         
1637         if(this.alt !== false){
1638             cfg.alt = this.alt;
1639         }
1640         
1641         
1642         if(this.target !== false) {
1643             cfg.target = this.target;
1644         }
1645         
1646         return cfg;
1647     },
1648     
1649     initEvents: function() {
1650         
1651         if(!this.href || this.preventDefault){
1652             this.el.on('click', this.onClick, this);
1653         }
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         if(this.preventDefault){
1659             e.preventDefault();
1660         }
1661         //Roo.log('img onclick');
1662         this.fireEvent('click', this, e);
1663     }
1664    
1665 });
1666
1667  /*
1668  * - LGPL
1669  *
1670  * header
1671  * 
1672  */
1673
1674 /**
1675  * @class Roo.bootstrap.Header
1676  * @extends Roo.bootstrap.Component
1677  * Bootstrap Header class
1678  * @cfg {String} html content of header
1679  * @cfg {Number} level (1|2|3|4|5|6) default 1
1680  * 
1681  * @constructor
1682  * Create a new Header
1683  * @param {Object} config The config object
1684  */
1685
1686
1687 Roo.bootstrap.Header  = function(config){
1688     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1689 };
1690
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1692     
1693     //href : false,
1694     html : false,
1695     level : 1,
1696     
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         
1702         
1703         var cfg = {
1704             tag: 'h' + (1 *this.level),
1705             html: this.html || ''
1706         } ;
1707         
1708         return cfg;
1709     }
1710    
1711 });
1712
1713  
1714
1715  /*
1716  * Based on:
1717  * Ext JS Library 1.1.1
1718  * Copyright(c) 2006-2007, Ext JS, LLC.
1719  *
1720  * Originally Released Under LGPL - original licence link has changed is not relivant.
1721  *
1722  * Fork - LGPL
1723  * <script type="text/javascript">
1724  */
1725  
1726 /**
1727  * @class Roo.bootstrap.MenuMgr
1728  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1729  * @singleton
1730  */
1731 Roo.bootstrap.MenuMgr = function(){
1732    var menus, active, groups = {}, attached = false, lastShow = new Date();
1733
1734    // private - called when first menu is created
1735    function init(){
1736        menus = {};
1737        active = new Roo.util.MixedCollection();
1738        Roo.get(document).addKeyListener(27, function(){
1739            if(active.length > 0){
1740                hideAll();
1741            }
1742        });
1743    }
1744
1745    // private
1746    function hideAll(){
1747        if(active && active.length > 0){
1748            var c = active.clone();
1749            c.each(function(m){
1750                m.hide();
1751            });
1752        }
1753    }
1754
1755    // private
1756    function onHide(m){
1757        active.remove(m);
1758        if(active.length < 1){
1759            Roo.get(document).un("mouseup", onMouseDown);
1760             
1761            attached = false;
1762        }
1763    }
1764
1765    // private
1766    function onShow(m){
1767        var last = active.last();
1768        lastShow = new Date();
1769        active.add(m);
1770        if(!attached){
1771           Roo.get(document).on("mouseup", onMouseDown);
1772            
1773            attached = true;
1774        }
1775        if(m.parentMenu){
1776           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777           m.parentMenu.activeChild = m;
1778        }else if(last && last.isVisible()){
1779           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1780        }
1781    }
1782
1783    // private
1784    function onBeforeHide(m){
1785        if(m.activeChild){
1786            m.activeChild.hide();
1787        }
1788        if(m.autoHideTimer){
1789            clearTimeout(m.autoHideTimer);
1790            delete m.autoHideTimer;
1791        }
1792    }
1793
1794    // private
1795    function onBeforeShow(m){
1796        var pm = m.parentMenu;
1797        if(!pm && !m.allowOtherMenus){
1798            hideAll();
1799        }else if(pm && pm.activeChild && active != m){
1800            pm.activeChild.hide();
1801        }
1802    }
1803
1804    // private this should really trigger on mouseup..
1805    function onMouseDown(e){
1806         Roo.log("on Mouse Up");
1807         
1808         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809             Roo.log("MenuManager hideAll");
1810             hideAll();
1811             e.stopEvent();
1812         }
1813         
1814         
1815    }
1816
1817    // private
1818    function onBeforeCheck(mi, state){
1819        if(state){
1820            var g = groups[mi.group];
1821            for(var i = 0, l = g.length; i < l; i++){
1822                if(g[i] != mi){
1823                    g[i].setChecked(false);
1824                }
1825            }
1826        }
1827    }
1828
1829    return {
1830
1831        /**
1832         * Hides all menus that are currently visible
1833         */
1834        hideAll : function(){
1835             hideAll();  
1836        },
1837
1838        // private
1839        register : function(menu){
1840            if(!menus){
1841                init();
1842            }
1843            menus[menu.id] = menu;
1844            menu.on("beforehide", onBeforeHide);
1845            menu.on("hide", onHide);
1846            menu.on("beforeshow", onBeforeShow);
1847            menu.on("show", onShow);
1848            var g = menu.group;
1849            if(g && menu.events["checkchange"]){
1850                if(!groups[g]){
1851                    groups[g] = [];
1852                }
1853                groups[g].push(menu);
1854                menu.on("checkchange", onCheck);
1855            }
1856        },
1857
1858         /**
1859          * Returns a {@link Roo.menu.Menu} object
1860          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861          * be used to generate and return a new Menu instance.
1862          */
1863        get : function(menu){
1864            if(typeof menu == "string"){ // menu id
1865                return menus[menu];
1866            }else if(menu.events){  // menu instance
1867                return menu;
1868            }
1869            /*else if(typeof menu.length == 'number'){ // array of menu items?
1870                return new Roo.bootstrap.Menu({items:menu});
1871            }else{ // otherwise, must be a config
1872                return new Roo.bootstrap.Menu(menu);
1873            }
1874            */
1875            return false;
1876        },
1877
1878        // private
1879        unregister : function(menu){
1880            delete menus[menu.id];
1881            menu.un("beforehide", onBeforeHide);
1882            menu.un("hide", onHide);
1883            menu.un("beforeshow", onBeforeShow);
1884            menu.un("show", onShow);
1885            var g = menu.group;
1886            if(g && menu.events["checkchange"]){
1887                groups[g].remove(menu);
1888                menu.un("checkchange", onCheck);
1889            }
1890        },
1891
1892        // private
1893        registerCheckable : function(menuItem){
1894            var g = menuItem.group;
1895            if(g){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menuItem);
1900                menuItem.on("beforecheckchange", onBeforeCheck);
1901            }
1902        },
1903
1904        // private
1905        unregisterCheckable : function(menuItem){
1906            var g = menuItem.group;
1907            if(g){
1908                groups[g].remove(menuItem);
1909                menuItem.un("beforecheckchange", onBeforeCheck);
1910            }
1911        }
1912    };
1913 }();/*
1914  * - LGPL
1915  *
1916  * menu
1917  * 
1918  */
1919
1920 /**
1921  * @class Roo.bootstrap.Menu
1922  * @extends Roo.bootstrap.Component
1923  * Bootstrap Menu class - container for MenuItems
1924  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1926  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1927  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1928  * 
1929  * @constructor
1930  * Create a new Menu
1931  * @param {Object} config The config object
1932  */
1933
1934
1935 Roo.bootstrap.Menu = function(config){
1936     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937     if (this.registerMenu && this.type != 'treeview')  {
1938         Roo.bootstrap.MenuMgr.register(this);
1939     }
1940     this.addEvents({
1941         /**
1942          * @event beforeshow
1943          * Fires before this menu is displayed
1944          * @param {Roo.menu.Menu} this
1945          */
1946         beforeshow : true,
1947         /**
1948          * @event beforehide
1949          * Fires before this menu is hidden
1950          * @param {Roo.menu.Menu} this
1951          */
1952         beforehide : true,
1953         /**
1954          * @event show
1955          * Fires after this menu is displayed
1956          * @param {Roo.menu.Menu} this
1957          */
1958         show : true,
1959         /**
1960          * @event hide
1961          * Fires after this menu is hidden
1962          * @param {Roo.menu.Menu} this
1963          */
1964         hide : true,
1965         /**
1966          * @event click
1967          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968          * @param {Roo.menu.Menu} this
1969          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970          * @param {Roo.EventObject} e
1971          */
1972         click : true,
1973         /**
1974          * @event mouseover
1975          * Fires when the mouse is hovering over this menu
1976          * @param {Roo.menu.Menu} this
1977          * @param {Roo.EventObject} e
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          */
1980         mouseover : true,
1981         /**
1982          * @event mouseout
1983          * Fires when the mouse exits this menu
1984          * @param {Roo.menu.Menu} this
1985          * @param {Roo.EventObject} e
1986          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1987          */
1988         mouseout : true,
1989         /**
1990          * @event itemclick
1991          * Fires when a menu item contained in this menu is clicked
1992          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993          * @param {Roo.EventObject} e
1994          */
1995         itemclick: true
1996     });
1997     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1998 };
1999
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2001     
2002    /// html : false,
2003     //align : '',
2004     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2005     type: false,
2006     /**
2007      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008      */
2009     registerMenu : true,
2010     
2011     menuItems :false, // stores the menu items..
2012     
2013     hidden:true,
2014         
2015     parentMenu : false,
2016     
2017     stopEvent : true,
2018     
2019     isLink : false,
2020     
2021     getChildContainer : function() {
2022         return this.el;  
2023     },
2024     
2025     getAutoCreate : function(){
2026          
2027         //if (['right'].indexOf(this.align)!==-1) {
2028         //    cfg.cn[1].cls += ' pull-right'
2029         //}
2030         
2031         
2032         var cfg = {
2033             tag : 'ul',
2034             cls : 'dropdown-menu' ,
2035             style : 'z-index:1000'
2036             
2037         };
2038         
2039         if (this.type === 'submenu') {
2040             cfg.cls = 'submenu active';
2041         }
2042         if (this.type === 'treeview') {
2043             cfg.cls = 'treeview-menu';
2044         }
2045         
2046         return cfg;
2047     },
2048     initEvents : function() {
2049         
2050        // Roo.log("ADD event");
2051        // Roo.log(this.triggerEl.dom);
2052         
2053         this.triggerEl.on('click', this.onTriggerClick, this);
2054         
2055         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056         
2057         this.triggerEl.addClass('dropdown-toggle');
2058         
2059         if (Roo.isTouch) {
2060             this.el.on('touchstart'  , this.onTouch, this);
2061         }
2062         this.el.on('click' , this.onClick, this);
2063
2064         this.el.on("mouseover", this.onMouseOver, this);
2065         this.el.on("mouseout", this.onMouseOut, this);
2066         
2067     },
2068     
2069     findTargetItem : function(e)
2070     {
2071         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2072         if(!t){
2073             return false;
2074         }
2075         //Roo.log(t);         Roo.log(t.id);
2076         if(t && t.id){
2077             //Roo.log(this.menuitems);
2078             return this.menuitems.get(t.id);
2079             
2080             //return this.items.get(t.menuItemId);
2081         }
2082         
2083         return false;
2084     },
2085     
2086     onTouch : function(e) 
2087     {
2088         Roo.log("menu.onTouch");
2089         //e.stopEvent(); this make the user popdown broken
2090         this.onClick(e);
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log("menu.onClick");
2096         
2097         var t = this.findTargetItem(e);
2098         if(!t || t.isContainer){
2099             return;
2100         }
2101         Roo.log(e);
2102         /*
2103         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2104             if(t == this.activeItem && t.shouldDeactivate(e)){
2105                 this.activeItem.deactivate();
2106                 delete this.activeItem;
2107                 return;
2108             }
2109             if(t.canActivate){
2110                 this.setActiveItem(t, true);
2111             }
2112             return;
2113             
2114             
2115         }
2116         */
2117        
2118         Roo.log('pass click event');
2119         
2120         t.onClick(e);
2121         
2122         this.fireEvent("click", this, t, e);
2123         
2124         var _this = this;
2125         
2126         (function() { _this.hide(); }).defer(100);
2127     },
2128     
2129     onMouseOver : function(e){
2130         var t  = this.findTargetItem(e);
2131         //Roo.log(t);
2132         //if(t){
2133         //    if(t.canActivate && !t.disabled){
2134         //        this.setActiveItem(t, true);
2135         //    }
2136         //}
2137         
2138         this.fireEvent("mouseover", this, e, t);
2139     },
2140     isVisible : function(){
2141         return !this.hidden;
2142     },
2143      onMouseOut : function(e){
2144         var t  = this.findTargetItem(e);
2145         
2146         //if(t ){
2147         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2148         //        this.activeItem.deactivate();
2149         //        delete this.activeItem;
2150         //    }
2151         //}
2152         this.fireEvent("mouseout", this, e, t);
2153     },
2154     
2155     
2156     /**
2157      * Displays this menu relative to another element
2158      * @param {String/HTMLElement/Roo.Element} element The element to align to
2159      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2160      * the element (defaults to this.defaultAlign)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     show : function(el, pos, parentMenu){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         this.fireEvent("beforeshow", this);
2169         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2170     },
2171      /**
2172      * Displays this menu at a specific xy position
2173      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2174      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2175      */
2176     showAt : function(xy, parentMenu, /* private: */_e){
2177         this.parentMenu = parentMenu;
2178         if(!this.el){
2179             this.render();
2180         }
2181         if(_e !== false){
2182             this.fireEvent("beforeshow", this);
2183             //xy = this.el.adjustForConstraints(xy);
2184         }
2185         
2186         //this.el.show();
2187         this.hideMenuItems();
2188         this.hidden = false;
2189         this.triggerEl.addClass('open');
2190         
2191         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2192             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2193         }
2194         
2195         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2196             this.el.setXY(xy);
2197         }
2198         
2199         this.focus();
2200         this.fireEvent("show", this);
2201     },
2202     
2203     focus : function(){
2204         return;
2205         if(!this.hidden){
2206             this.doFocus.defer(50, this);
2207         }
2208     },
2209
2210     doFocus : function(){
2211         if(!this.hidden){
2212             this.focusEl.focus();
2213         }
2214     },
2215
2216     /**
2217      * Hides this menu and optionally all parent menus
2218      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2219      */
2220     hide : function(deep)
2221     {
2222         
2223         this.hideMenuItems();
2224         if(this.el && this.isVisible()){
2225             this.fireEvent("beforehide", this);
2226             if(this.activeItem){
2227                 this.activeItem.deactivate();
2228                 this.activeItem = null;
2229             }
2230             this.triggerEl.removeClass('open');;
2231             this.hidden = true;
2232             this.fireEvent("hide", this);
2233         }
2234         if(deep === true && this.parentMenu){
2235             this.parentMenu.hide(true);
2236         }
2237     },
2238     
2239     onTriggerClick : function(e)
2240     {
2241         Roo.log('trigger click');
2242         
2243         var target = e.getTarget();
2244         
2245         Roo.log(target.nodeName.toLowerCase());
2246         
2247         if(target.nodeName.toLowerCase() === 'i'){
2248             e.preventDefault();
2249         }
2250         
2251     },
2252     
2253     onTriggerPress  : function(e)
2254     {
2255         Roo.log('trigger press');
2256         //Roo.log(e.getTarget());
2257        // Roo.log(this.triggerEl.dom);
2258        
2259         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2260         var pel = Roo.get(e.getTarget());
2261         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2262             Roo.log('is treeview or dropdown?');
2263             return;
2264         }
2265         
2266         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2267             return;
2268         }
2269         
2270         if (this.isVisible()) {
2271             Roo.log('hide');
2272             this.hide();
2273         } else {
2274             Roo.log('show');
2275             this.show(this.triggerEl, false, false);
2276         }
2277         
2278         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2279             e.stopEvent();
2280         }
2281         
2282     },
2283        
2284     
2285     hideMenuItems : function()
2286     {
2287         Roo.log("hide Menu Items");
2288         if (!this.el) { 
2289             return;
2290         }
2291         //$(backdrop).remove()
2292         this.el.select('.open',true).each(function(aa) {
2293             
2294             aa.removeClass('open');
2295           //var parent = getParent($(this))
2296           //var relatedTarget = { relatedTarget: this }
2297           
2298            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2299           //if (e.isDefaultPrevented()) return
2300            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2301         });
2302     },
2303     addxtypeChild : function (tree, cntr) {
2304         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2305           
2306         this.menuitems.add(comp);
2307         return comp;
2308
2309     },
2310     getEl : function()
2311     {
2312         Roo.log(this.el);
2313         return this.el;
2314     }
2315 });
2316
2317  
2318  /*
2319  * - LGPL
2320  *
2321  * menu item
2322  * 
2323  */
2324
2325
2326 /**
2327  * @class Roo.bootstrap.MenuItem
2328  * @extends Roo.bootstrap.Component
2329  * Bootstrap MenuItem class
2330  * @cfg {String} html the menu label
2331  * @cfg {String} href the link
2332  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2333  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2334  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2335  * @cfg {String} fa favicon to show on left of menu item.
2336  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2337  * 
2338  * 
2339  * @constructor
2340  * Create a new MenuItem
2341  * @param {Object} config The config object
2342  */
2343
2344
2345 Roo.bootstrap.MenuItem = function(config){
2346     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2347     this.addEvents({
2348         // raw events
2349         /**
2350          * @event click
2351          * The raw click event for the entire grid.
2352          * @param {Roo.bootstrap.MenuItem} this
2353          * @param {Roo.EventObject} e
2354          */
2355         "click" : true
2356     });
2357 };
2358
2359 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2360     
2361     href : false,
2362     html : false,
2363     preventDefault: false,
2364     isContainer : false,
2365     active : false,
2366     fa: false,
2367     
2368     getAutoCreate : function(){
2369         
2370         if(this.isContainer){
2371             return {
2372                 tag: 'li',
2373                 cls: 'dropdown-menu-item'
2374             };
2375         }
2376         var ctag = {
2377             tag: 'span',
2378             html: 'Link'
2379         };
2380         
2381         var anc = {
2382             tag : 'a',
2383             href : '#',
2384             cn : [  ]
2385         };
2386         
2387         if (this.fa !== false) {
2388             anc.cn.push({
2389                 tag : 'i',
2390                 cls : 'fa fa-' + this.fa
2391             });
2392         }
2393         
2394         anc.cn.push(ctag);
2395         
2396         
2397         var cfg= {
2398             tag: 'li',
2399             cls: 'dropdown-menu-item',
2400             cn: [ anc ]
2401         };
2402         if (this.parent().type == 'treeview') {
2403             cfg.cls = 'treeview-menu';
2404         }
2405         if (this.active) {
2406             cfg.cls += ' active';
2407         }
2408         
2409         
2410         
2411         anc.href = this.href || cfg.cn[0].href ;
2412         ctag.html = this.html || cfg.cn[0].html ;
2413         return cfg;
2414     },
2415     
2416     initEvents: function()
2417     {
2418         if (this.parent().type == 'treeview') {
2419             this.el.select('a').on('click', this.onClick, this);
2420         }
2421         if (this.menu) {
2422             this.menu.parentType = this.xtype;
2423             this.menu.triggerEl = this.el;
2424             this.menu = this.addxtype(Roo.apply({}, this.menu));
2425         }
2426         
2427     },
2428     onClick : function(e)
2429     {
2430         Roo.log('item on click ');
2431         
2432         if(this.preventDefault){
2433             e.preventDefault();
2434         }
2435         //this.parent().hideMenuItems();
2436         
2437         this.fireEvent('click', this, e);
2438     },
2439     getEl : function()
2440     {
2441         return this.el;
2442     } 
2443 });
2444
2445  
2446
2447  /*
2448  * - LGPL
2449  *
2450  * menu separator
2451  * 
2452  */
2453
2454
2455 /**
2456  * @class Roo.bootstrap.MenuSeparator
2457  * @extends Roo.bootstrap.Component
2458  * Bootstrap MenuSeparator class
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuSeparator = function(config){
2467     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2468 };
2469
2470 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2471     
2472     getAutoCreate : function(){
2473         var cfg = {
2474             cls: 'divider',
2475             tag : 'li'
2476         };
2477         
2478         return cfg;
2479     }
2480    
2481 });
2482
2483  
2484
2485  
2486 /*
2487 * Licence: LGPL
2488 */
2489
2490 /**
2491  * @class Roo.bootstrap.Modal
2492  * @extends Roo.bootstrap.Component
2493  * Bootstrap Modal class
2494  * @cfg {String} title Title of dialog
2495  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2496  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2497  * @cfg {Boolean} specificTitle default false
2498  * @cfg {Array} buttons Array of buttons or standard button set..
2499  * @cfg {String} buttonPosition (left|right|center) default right
2500  * @cfg {Boolean} animate default true
2501  * @cfg {Boolean} allow_close default true
2502  * @cfg {Boolean} fitwindow default false
2503  * @cfg {String} size (sm|lg) default empty
2504  *
2505  *
2506  * @constructor
2507  * Create a new Modal Dialog
2508  * @param {Object} config The config object
2509  */
2510
2511 Roo.bootstrap.Modal = function(config){
2512     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2513     this.addEvents({
2514         // raw events
2515         /**
2516          * @event btnclick
2517          * The raw btnclick event for the button
2518          * @param {Roo.EventObject} e
2519          */
2520         "btnclick" : true,
2521         /**
2522          * @event resize
2523          * Fire when dialog resize
2524          * @param {Roo.bootstrap.Modal} this
2525          * @param {Roo.EventObject} e
2526          */
2527         "resize" : true
2528     });
2529     this.buttons = this.buttons || [];
2530
2531     if (this.tmpl) {
2532         this.tmpl = Roo.factory(this.tmpl);
2533     }
2534
2535 };
2536
2537 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2538
2539     title : 'test dialog',
2540
2541     buttons : false,
2542
2543     // set on load...
2544
2545     html: false,
2546
2547     tmp: false,
2548
2549     specificTitle: false,
2550
2551     buttonPosition: 'right',
2552
2553     allow_close : true,
2554
2555     animate : true,
2556
2557     fitwindow: false,
2558
2559
2560      // private
2561     dialogEl: false,
2562     bodyEl:  false,
2563     footerEl:  false,
2564     titleEl:  false,
2565     closeEl:  false,
2566
2567     size: '',
2568
2569
2570     onRender : function(ct, position)
2571     {
2572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2573
2574         if(!this.el){
2575             var cfg = Roo.apply({},  this.getAutoCreate());
2576             cfg.id = Roo.id();
2577             //if(!cfg.name){
2578             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2579             //}
2580             //if (!cfg.name.length) {
2581             //    delete cfg.name;
2582            // }
2583             if (this.cls) {
2584                 cfg.cls += ' ' + this.cls;
2585             }
2586             if (this.style) {
2587                 cfg.style = this.style;
2588             }
2589             this.el = Roo.get(document.body).createChild(cfg, position);
2590         }
2591         //var type = this.el.dom.type;
2592
2593
2594         if(this.tabIndex !== undefined){
2595             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2596         }
2597
2598         this.dialogEl = this.el.select('.modal-dialog',true).first();
2599         this.bodyEl = this.el.select('.modal-body',true).first();
2600         this.closeEl = this.el.select('.modal-header .close', true).first();
2601         this.headerEl = this.el.select('.modal-header',true).first();
2602         this.titleEl = this.el.select('.modal-title',true).first();
2603         this.footerEl = this.el.select('.modal-footer',true).first();
2604
2605         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2606         this.maskEl.enableDisplayMode("block");
2607         this.maskEl.hide();
2608         //this.el.addClass("x-dlg-modal");
2609
2610         if (this.buttons.length) {
2611             Roo.each(this.buttons, function(bb) {
2612                 var b = Roo.apply({}, bb);
2613                 b.xns = b.xns || Roo.bootstrap;
2614                 b.xtype = b.xtype || 'Button';
2615                 if (typeof(b.listeners) == 'undefined') {
2616                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2617                 }
2618
2619                 var btn = Roo.factory(b);
2620
2621                 btn.render(this.el.select('.modal-footer div').first());
2622
2623             },this);
2624         }
2625         // render the children.
2626         var nitems = [];
2627
2628         if(typeof(this.items) != 'undefined'){
2629             var items = this.items;
2630             delete this.items;
2631
2632             for(var i =0;i < items.length;i++) {
2633                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2634             }
2635         }
2636
2637         this.items = nitems;
2638
2639         // where are these used - they used to be body/close/footer
2640
2641
2642         this.initEvents();
2643         //this.el.addClass([this.fieldClass, this.cls]);
2644
2645     },
2646
2647     getAutoCreate : function(){
2648
2649
2650         var bdy = {
2651                 cls : 'modal-body',
2652                 html : this.html || ''
2653         };
2654
2655         var title = {
2656             tag: 'h4',
2657             cls : 'modal-title',
2658             html : this.title
2659         };
2660
2661         if(this.specificTitle){
2662             title = this.title;
2663
2664         };
2665
2666         var header = [];
2667         if (this.allow_close) {
2668             header.push({
2669                 tag: 'button',
2670                 cls : 'close',
2671                 html : '&times'
2672             });
2673         }
2674
2675         header.push(title);
2676
2677         var size = '';
2678
2679         if(this.size.length){
2680             size = 'modal-' + this.size;
2681         }
2682
2683         var modal = {
2684             cls: "modal",
2685             style : 'display: none',
2686             cn : [
2687                 {
2688                     cls: "modal-dialog " + size,
2689                     cn : [
2690                         {
2691                             cls : "modal-content",
2692                             cn : [
2693                                 {
2694                                     cls : 'modal-header',
2695                                     cn : header
2696                                 },
2697                                 bdy,
2698                                 {
2699                                     cls : 'modal-footer',
2700                                     cn : [
2701                                         {
2702                                             tag: 'div',
2703                                             cls: 'btn-' + this.buttonPosition
2704                                         }
2705                                     ]
2706
2707                                 }
2708
2709
2710                             ]
2711
2712                         }
2713                     ]
2714
2715                 }
2716             ]
2717         };
2718
2719         if(this.animate){
2720             modal.cls += ' fade';
2721         }
2722
2723         return modal;
2724
2725     },
2726     getChildContainer : function() {
2727
2728          return this.bodyEl;
2729
2730     },
2731     getButtonContainer : function() {
2732          return this.el.select('.modal-footer div',true).first();
2733
2734     },
2735     initEvents : function()
2736     {
2737         if (this.allow_close) {
2738             this.closeEl.on('click', this.hide, this);
2739         }
2740         Roo.EventManager.onWindowResize(this.resize, this, true);
2741
2742
2743     },
2744
2745     resize : function()
2746     {
2747         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2748         if (this.fitwindow) {
2749             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2750             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2751             this.setSize(w,h);
2752         }
2753     },
2754
2755     setSize : function(w,h)
2756     {
2757         if (!w && !h) {
2758             return;
2759         }
2760         this.resizeTo(w,h);
2761     },
2762
2763     show : function() {
2764
2765         if (!this.rendered) {
2766             this.render();
2767         }
2768
2769         this.el.setStyle('display', 'block');
2770
2771         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2772             var _this = this;
2773             (function(){
2774                 this.el.addClass('in');
2775             }).defer(50, this);
2776         }else{
2777             this.el.addClass('in');
2778
2779         }
2780
2781         // not sure how we can show data in here..
2782         //if (this.tmpl) {
2783         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2784         //}
2785
2786         Roo.get(document.body).addClass("x-body-masked");
2787         
2788         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2789         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2790         this.maskEl.show();
2791         
2792         this.resize();
2793         
2794         this.fireEvent('show', this);
2795
2796         // set zindex here - otherwise it appears to be ignored...
2797         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2798
2799         (function () {
2800             this.items.forEach( function(e) {
2801                 e.layout ? e.layout() : false;
2802
2803             });
2804         }).defer(100,this);
2805
2806     },
2807     hide : function()
2808     {
2809         if(this.fireEvent("beforehide", this) !== false){
2810             this.maskEl.hide();
2811             Roo.get(document.body).removeClass("x-body-masked");
2812             this.el.removeClass('in');
2813             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2814
2815             if(this.animate){ // why
2816                 var _this = this;
2817                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2818             }else{
2819                 this.el.setStyle('display', 'none');
2820             }
2821             this.fireEvent('hide', this);
2822         }
2823     },
2824
2825     addButton : function(str, cb)
2826     {
2827
2828
2829         var b = Roo.apply({}, { html : str } );
2830         b.xns = b.xns || Roo.bootstrap;
2831         b.xtype = b.xtype || 'Button';
2832         if (typeof(b.listeners) == 'undefined') {
2833             b.listeners = { click : cb.createDelegate(this)  };
2834         }
2835
2836         var btn = Roo.factory(b);
2837
2838         btn.render(this.el.select('.modal-footer div').first());
2839
2840         return btn;
2841
2842     },
2843
2844     setDefaultButton : function(btn)
2845     {
2846         //this.el.select('.modal-footer').()
2847     },
2848     diff : false,
2849
2850     resizeTo: function(w,h)
2851     {
2852         // skip.. ?? why??
2853
2854         this.dialogEl.setWidth(w);
2855         if (this.diff === false) {
2856             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2857         }
2858
2859         this.bodyEl.setHeight(h-this.diff);
2860
2861         this.fireEvent('resize', this);
2862
2863     },
2864     setContentSize  : function(w, h)
2865     {
2866
2867     },
2868     onButtonClick: function(btn,e)
2869     {
2870         //Roo.log([a,b,c]);
2871         this.fireEvent('btnclick', btn.name, e);
2872     },
2873      /**
2874      * Set the title of the Dialog
2875      * @param {String} str new Title
2876      */
2877     setTitle: function(str) {
2878         this.titleEl.dom.innerHTML = str;
2879     },
2880     /**
2881      * Set the body of the Dialog
2882      * @param {String} str new Title
2883      */
2884     setBody: function(str) {
2885         this.bodyEl.dom.innerHTML = str;
2886     },
2887     /**
2888      * Set the body of the Dialog using the template
2889      * @param {Obj} data - apply this data to the template and replace the body contents.
2890      */
2891     applyBody: function(obj)
2892     {
2893         if (!this.tmpl) {
2894             Roo.log("Error - using apply Body without a template");
2895             //code
2896         }
2897         this.tmpl.overwrite(this.bodyEl, obj);
2898     }
2899
2900 });
2901
2902
2903 Roo.apply(Roo.bootstrap.Modal,  {
2904     /**
2905          * Button config that displays a single OK button
2906          * @type Object
2907          */
2908         OK :  [{
2909             name : 'ok',
2910             weight : 'primary',
2911             html : 'OK'
2912         }],
2913         /**
2914          * Button config that displays Yes and No buttons
2915          * @type Object
2916          */
2917         YESNO : [
2918             {
2919                 name  : 'no',
2920                 html : 'No'
2921             },
2922             {
2923                 name  :'yes',
2924                 weight : 'primary',
2925                 html : 'Yes'
2926             }
2927         ],
2928
2929         /**
2930          * Button config that displays OK and Cancel buttons
2931          * @type Object
2932          */
2933         OKCANCEL : [
2934             {
2935                name : 'cancel',
2936                 html : 'Cancel'
2937             },
2938             {
2939                 name : 'ok',
2940                 weight : 'primary',
2941                 html : 'OK'
2942             }
2943         ],
2944         /**
2945          * Button config that displays Yes, No and Cancel buttons
2946          * @type Object
2947          */
2948         YESNOCANCEL : [
2949             {
2950                 name : 'yes',
2951                 weight : 'primary',
2952                 html : 'Yes'
2953             },
2954             {
2955                 name : 'no',
2956                 html : 'No'
2957             },
2958             {
2959                 name : 'cancel',
2960                 html : 'Cancel'
2961             }
2962         ],
2963         
2964         zIndex : 10001
2965 });
2966 /*
2967  * - LGPL
2968  *
2969  * messagebox - can be used as a replace
2970  * 
2971  */
2972 /**
2973  * @class Roo.MessageBox
2974  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2975  * Example usage:
2976  *<pre><code>
2977 // Basic alert:
2978 Roo.Msg.alert('Status', 'Changes saved successfully.');
2979
2980 // Prompt for user data:
2981 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2982     if (btn == 'ok'){
2983         // process text value...
2984     }
2985 });
2986
2987 // Show a dialog using config options:
2988 Roo.Msg.show({
2989    title:'Save Changes?',
2990    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2991    buttons: Roo.Msg.YESNOCANCEL,
2992    fn: processResult,
2993    animEl: 'elId'
2994 });
2995 </code></pre>
2996  * @singleton
2997  */
2998 Roo.bootstrap.MessageBox = function(){
2999     var dlg, opt, mask, waitTimer;
3000     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3001     var buttons, activeTextEl, bwidth;
3002
3003     
3004     // private
3005     var handleButton = function(button){
3006         dlg.hide();
3007         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3008     };
3009
3010     // private
3011     var handleHide = function(){
3012         if(opt && opt.cls){
3013             dlg.el.removeClass(opt.cls);
3014         }
3015         //if(waitTimer){
3016         //    Roo.TaskMgr.stop(waitTimer);
3017         //    waitTimer = null;
3018         //}
3019     };
3020
3021     // private
3022     var updateButtons = function(b){
3023         var width = 0;
3024         if(!b){
3025             buttons["ok"].hide();
3026             buttons["cancel"].hide();
3027             buttons["yes"].hide();
3028             buttons["no"].hide();
3029             //dlg.footer.dom.style.display = 'none';
3030             return width;
3031         }
3032         dlg.footerEl.dom.style.display = '';
3033         for(var k in buttons){
3034             if(typeof buttons[k] != "function"){
3035                 if(b[k]){
3036                     buttons[k].show();
3037                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3038                     width += buttons[k].el.getWidth()+15;
3039                 }else{
3040                     buttons[k].hide();
3041                 }
3042             }
3043         }
3044         return width;
3045     };
3046
3047     // private
3048     var handleEsc = function(d, k, e){
3049         if(opt && opt.closable !== false){
3050             dlg.hide();
3051         }
3052         if(e){
3053             e.stopEvent();
3054         }
3055     };
3056
3057     return {
3058         /**
3059          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3060          * @return {Roo.BasicDialog} The BasicDialog element
3061          */
3062         getDialog : function(){
3063            if(!dlg){
3064                 dlg = new Roo.bootstrap.Modal( {
3065                     //draggable: true,
3066                     //resizable:false,
3067                     //constraintoviewport:false,
3068                     //fixedcenter:true,
3069                     //collapsible : false,
3070                     //shim:true,
3071                     //modal: true,
3072                 //    width: 'auto',
3073                   //  height:100,
3074                     //buttonAlign:"center",
3075                     closeClick : function(){
3076                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3077                             handleButton("no");
3078                         }else{
3079                             handleButton("cancel");
3080                         }
3081                     }
3082                 });
3083                 dlg.render();
3084                 dlg.on("hide", handleHide);
3085                 mask = dlg.mask;
3086                 //dlg.addKeyListener(27, handleEsc);
3087                 buttons = {};
3088                 this.buttons = buttons;
3089                 var bt = this.buttonText;
3090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3094                 //Roo.log(buttons);
3095                 bodyEl = dlg.bodyEl.createChild({
3096
3097                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3098                         '<textarea class="roo-mb-textarea"></textarea>' +
3099                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3100                 });
3101                 msgEl = bodyEl.dom.firstChild;
3102                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3103                 textboxEl.enableDisplayMode();
3104                 textboxEl.addKeyListener([10,13], function(){
3105                     if(dlg.isVisible() && opt && opt.buttons){
3106                         if(opt.buttons.ok){
3107                             handleButton("ok");
3108                         }else if(opt.buttons.yes){
3109                             handleButton("yes");
3110                         }
3111                     }
3112                 });
3113                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3114                 textareaEl.enableDisplayMode();
3115                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3116                 progressEl.enableDisplayMode();
3117                 
3118                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3119                 //var pf = progressEl.dom.firstChild;
3120                 //if (pf) {
3121                     //pp = Roo.get(pf.firstChild);
3122                     //pp.setHeight(pf.offsetHeight);
3123                 //}
3124                 
3125             }
3126             return dlg;
3127         },
3128
3129         /**
3130          * Updates the message box body text
3131          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3132          * the XHTML-compliant non-breaking space character '&amp;#160;')
3133          * @return {Roo.MessageBox} This message box
3134          */
3135         updateText : function(text)
3136         {
3137             if(!dlg.isVisible() && !opt.width){
3138                 dlg.dialogEl.setWidth(this.maxWidth);
3139                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3140             }
3141             msgEl.innerHTML = text || '&#160;';
3142       
3143             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3144             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3145             var w = Math.max(
3146                     Math.min(opt.width || cw , this.maxWidth), 
3147                     Math.max(opt.minWidth || this.minWidth, bwidth)
3148             );
3149             if(opt.prompt){
3150                 activeTextEl.setWidth(w);
3151             }
3152             if(dlg.isVisible()){
3153                 dlg.fixedcenter = false;
3154             }
3155             // to big, make it scroll. = But as usual stupid IE does not support
3156             // !important..
3157             
3158             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3159                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3160                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3161             } else {
3162                 bodyEl.dom.style.height = '';
3163                 bodyEl.dom.style.overflowY = '';
3164             }
3165             if (cw > w) {
3166                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3167             } else {
3168                 bodyEl.dom.style.overflowX = '';
3169             }
3170             
3171             dlg.setContentSize(w, bodyEl.getHeight());
3172             if(dlg.isVisible()){
3173                 dlg.fixedcenter = true;
3174             }
3175             return this;
3176         },
3177
3178         /**
3179          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3180          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3181          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3182          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3183          * @return {Roo.MessageBox} This message box
3184          */
3185         updateProgress : function(value, text){
3186             if(text){
3187                 this.updateText(text);
3188             }
3189             if (pp) { // weird bug on my firefox - for some reason this is not defined
3190                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3191             }
3192             return this;
3193         },        
3194
3195         /**
3196          * Returns true if the message box is currently displayed
3197          * @return {Boolean} True if the message box is visible, else false
3198          */
3199         isVisible : function(){
3200             return dlg && dlg.isVisible();  
3201         },
3202
3203         /**
3204          * Hides the message box if it is displayed
3205          */
3206         hide : function(){
3207             if(this.isVisible()){
3208                 dlg.hide();
3209             }  
3210         },
3211
3212         /**
3213          * Displays a new message box, or reinitializes an existing message box, based on the config options
3214          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3215          * The following config object properties are supported:
3216          * <pre>
3217 Property    Type             Description
3218 ----------  ---------------  ------------------------------------------------------------------------------------
3219 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3220                                    closes (defaults to undefined)
3221 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3222                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3223 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3224                                    progress and wait dialogs will ignore this property and always hide the
3225                                    close button as they can only be closed programmatically.
3226 cls               String           A custom CSS class to apply to the message box element
3227 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3228                                    displayed (defaults to 75)
3229 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3230                                    function will be btn (the name of the button that was clicked, if applicable,
3231                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3232                                    Progress and wait dialogs will ignore this option since they do not respond to
3233                                    user actions and can only be closed programmatically, so any required function
3234                                    should be called by the same code after it closes the dialog.
3235 icon              String           A CSS class that provides a background image to be used as an icon for
3236                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3237 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3238 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3239 modal             Boolean          False to allow user interaction with the page while the message box is
3240                                    displayed (defaults to true)
3241 msg               String           A string that will replace the existing message box body text (defaults
3242                                    to the XHTML-compliant non-breaking space character '&#160;')
3243 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3244 progress          Boolean          True to display a progress bar (defaults to false)
3245 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3246 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3247 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3248 title             String           The title text
3249 value             String           The string value to set into the active textbox element if displayed
3250 wait              Boolean          True to display a progress bar (defaults to false)
3251 width             Number           The width of the dialog in pixels
3252 </pre>
3253          *
3254          * Example usage:
3255          * <pre><code>
3256 Roo.Msg.show({
3257    title: 'Address',
3258    msg: 'Please enter your address:',
3259    width: 300,
3260    buttons: Roo.MessageBox.OKCANCEL,
3261    multiline: true,
3262    fn: saveAddress,
3263    animEl: 'addAddressBtn'
3264 });
3265 </code></pre>
3266          * @param {Object} config Configuration options
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         show : function(options)
3270         {
3271             
3272             // this causes nightmares if you show one dialog after another
3273             // especially on callbacks..
3274              
3275             if(this.isVisible()){
3276                 
3277                 this.hide();
3278                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3279                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3280                 Roo.log("New Dialog Message:" +  options.msg )
3281                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3282                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3283                 
3284             }
3285             var d = this.getDialog();
3286             opt = options;
3287             d.setTitle(opt.title || "&#160;");
3288             d.closeEl.setDisplayed(opt.closable !== false);
3289             activeTextEl = textboxEl;
3290             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3291             if(opt.prompt){
3292                 if(opt.multiline){
3293                     textboxEl.hide();
3294                     textareaEl.show();
3295                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3296                         opt.multiline : this.defaultTextHeight);
3297                     activeTextEl = textareaEl;
3298                 }else{
3299                     textboxEl.show();
3300                     textareaEl.hide();
3301                 }
3302             }else{
3303                 textboxEl.hide();
3304                 textareaEl.hide();
3305             }
3306             progressEl.setDisplayed(opt.progress === true);
3307             this.updateProgress(0);
3308             activeTextEl.dom.value = opt.value || "";
3309             if(opt.prompt){
3310                 dlg.setDefaultButton(activeTextEl);
3311             }else{
3312                 var bs = opt.buttons;
3313                 var db = null;
3314                 if(bs && bs.ok){
3315                     db = buttons["ok"];
3316                 }else if(bs && bs.yes){
3317                     db = buttons["yes"];
3318                 }
3319                 dlg.setDefaultButton(db);
3320             }
3321             bwidth = updateButtons(opt.buttons);
3322             this.updateText(opt.msg);
3323             if(opt.cls){
3324                 d.el.addClass(opt.cls);
3325             }
3326             d.proxyDrag = opt.proxyDrag === true;
3327             d.modal = opt.modal !== false;
3328             d.mask = opt.modal !== false ? mask : false;
3329             if(!d.isVisible()){
3330                 // force it to the end of the z-index stack so it gets a cursor in FF
3331                 document.body.appendChild(dlg.el.dom);
3332                 d.animateTarget = null;
3333                 d.show(options.animEl);
3334             }
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3340          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3341          * and closing the message box when the process is complete.
3342          * @param {String} title The title bar text
3343          * @param {String} msg The message box body text
3344          * @return {Roo.MessageBox} This message box
3345          */
3346         progress : function(title, msg){
3347             this.show({
3348                 title : title,
3349                 msg : msg,
3350                 buttons: false,
3351                 progress:true,
3352                 closable:false,
3353                 minWidth: this.minProgressWidth,
3354                 modal : true
3355             });
3356             return this;
3357         },
3358
3359         /**
3360          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3361          * If a callback function is passed it will be called after the user clicks the button, and the
3362          * id of the button that was clicked will be passed as the only parameter to the callback
3363          * (could also be the top-right close button).
3364          * @param {String} title The title bar text
3365          * @param {String} msg The message box body text
3366          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3367          * @param {Object} scope (optional) The scope of the callback function
3368          * @return {Roo.MessageBox} This message box
3369          */
3370         alert : function(title, msg, fn, scope)
3371         {
3372             this.show({
3373                 title : title,
3374                 msg : msg,
3375                 buttons: this.OK,
3376                 fn: fn,
3377                 closable : false,
3378                 scope : scope,
3379                 modal : true
3380             });
3381             return this;
3382         },
3383
3384         /**
3385          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3386          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3387          * You are responsible for closing the message box when the process is complete.
3388          * @param {String} msg The message box body text
3389          * @param {String} title (optional) The title bar text
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         wait : function(msg, title){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: false,
3397                 closable:false,
3398                 progress:true,
3399                 modal:true,
3400                 width:300,
3401                 wait:true
3402             });
3403             waitTimer = Roo.TaskMgr.start({
3404                 run: function(i){
3405                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3406                 },
3407                 interval: 1000
3408             });
3409             return this;
3410         },
3411
3412         /**
3413          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3414          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3415          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3416          * @param {String} title The title bar text
3417          * @param {String} msg The message box body text
3418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3419          * @param {Object} scope (optional) The scope of the callback function
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         confirm : function(title, msg, fn, scope){
3423             this.show({
3424                 title : title,
3425                 msg : msg,
3426                 buttons: this.YESNO,
3427                 fn: fn,
3428                 scope : scope,
3429                 modal : true
3430             });
3431             return this;
3432         },
3433
3434         /**
3435          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3436          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3437          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3438          * (could also be the top-right close button) and the text that was entered will be passed as the two
3439          * parameters to the callback.
3440          * @param {String} title The title bar text
3441          * @param {String} msg The message box body text
3442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3443          * @param {Object} scope (optional) The scope of the callback function
3444          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3445          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         prompt : function(title, msg, fn, scope, multiline){
3449             this.show({
3450                 title : title,
3451                 msg : msg,
3452                 buttons: this.OKCANCEL,
3453                 fn: fn,
3454                 minWidth:250,
3455                 scope : scope,
3456                 prompt:true,
3457                 multiline: multiline,
3458                 modal : true
3459             });
3460             return this;
3461         },
3462
3463         /**
3464          * Button config that displays a single OK button
3465          * @type Object
3466          */
3467         OK : {ok:true},
3468         /**
3469          * Button config that displays Yes and No buttons
3470          * @type Object
3471          */
3472         YESNO : {yes:true, no:true},
3473         /**
3474          * Button config that displays OK and Cancel buttons
3475          * @type Object
3476          */
3477         OKCANCEL : {ok:true, cancel:true},
3478         /**
3479          * Button config that displays Yes, No and Cancel buttons
3480          * @type Object
3481          */
3482         YESNOCANCEL : {yes:true, no:true, cancel:true},
3483
3484         /**
3485          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3486          * @type Number
3487          */
3488         defaultTextHeight : 75,
3489         /**
3490          * The maximum width in pixels of the message box (defaults to 600)
3491          * @type Number
3492          */
3493         maxWidth : 600,
3494         /**
3495          * The minimum width in pixels of the message box (defaults to 100)
3496          * @type Number
3497          */
3498         minWidth : 100,
3499         /**
3500          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3501          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3502          * @type Number
3503          */
3504         minProgressWidth : 250,
3505         /**
3506          * An object containing the default button text strings that can be overriden for localized language support.
3507          * Supported properties are: ok, cancel, yes and no.
3508          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3509          * @type Object
3510          */
3511         buttonText : {
3512             ok : "OK",
3513             cancel : "Cancel",
3514             yes : "Yes",
3515             no : "No"
3516         }
3517     };
3518 }();
3519
3520 /**
3521  * Shorthand for {@link Roo.MessageBox}
3522  */
3523 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3524 Roo.Msg = Roo.Msg || Roo.MessageBox;
3525 /*
3526  * - LGPL
3527  *
3528  * navbar
3529  * 
3530  */
3531
3532 /**
3533  * @class Roo.bootstrap.Navbar
3534  * @extends Roo.bootstrap.Component
3535  * Bootstrap Navbar class
3536
3537  * @constructor
3538  * Create a new Navbar
3539  * @param {Object} config The config object
3540  */
3541
3542
3543 Roo.bootstrap.Navbar = function(config){
3544     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3545     this.addEvents({
3546         // raw events
3547         /**
3548          * @event beforetoggle
3549          * Fire before toggle the menu
3550          * @param {Roo.EventObject} e
3551          */
3552         "beforetoggle" : true
3553     });
3554 };
3555
3556 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3557     
3558     
3559    
3560     // private
3561     navItems : false,
3562     loadMask : false,
3563     
3564     
3565     getAutoCreate : function(){
3566         
3567         
3568         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3569         
3570     },
3571     
3572     initEvents :function ()
3573     {
3574         //Roo.log(this.el.select('.navbar-toggle',true));
3575         this.el.select('.navbar-toggle',true).on('click', function() {
3576             if(this.fireEvent('beforetoggle', this) !== false){
3577                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3578             }
3579             
3580         }, this);
3581         
3582         var mark = {
3583             tag: "div",
3584             cls:"x-dlg-mask"
3585         };
3586         
3587         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3588         
3589         var size = this.el.getSize();
3590         this.maskEl.setSize(size.width, size.height);
3591         this.maskEl.enableDisplayMode("block");
3592         this.maskEl.hide();
3593         
3594         if(this.loadMask){
3595             this.maskEl.show();
3596         }
3597     },
3598     
3599     
3600     getChildContainer : function()
3601     {
3602         if (this.el.select('.collapse').getCount()) {
3603             return this.el.select('.collapse',true).first();
3604         }
3605         
3606         return this.el;
3607     },
3608     
3609     mask : function()
3610     {
3611         this.maskEl.show();
3612     },
3613     
3614     unmask : function()
3615     {
3616         this.maskEl.hide();
3617     } 
3618     
3619     
3620     
3621     
3622 });
3623
3624
3625
3626  
3627
3628  /*
3629  * - LGPL
3630  *
3631  * navbar
3632  * 
3633  */
3634
3635 /**
3636  * @class Roo.bootstrap.NavSimplebar
3637  * @extends Roo.bootstrap.Navbar
3638  * Bootstrap Sidebar class
3639  *
3640  * @cfg {Boolean} inverse is inverted color
3641  * 
3642  * @cfg {String} type (nav | pills | tabs)
3643  * @cfg {Boolean} arrangement stacked | justified
3644  * @cfg {String} align (left | right) alignment
3645  * 
3646  * @cfg {Boolean} main (true|false) main nav bar? default false
3647  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3648  * 
3649  * @cfg {String} tag (header|footer|nav|div) default is nav 
3650
3651  * 
3652  * 
3653  * 
3654  * @constructor
3655  * Create a new Sidebar
3656  * @param {Object} config The config object
3657  */
3658
3659
3660 Roo.bootstrap.NavSimplebar = function(config){
3661     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3662 };
3663
3664 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3665     
3666     inverse: false,
3667     
3668     type: false,
3669     arrangement: '',
3670     align : false,
3671     
3672     
3673     
3674     main : false,
3675     
3676     
3677     tag : false,
3678     
3679     
3680     getAutoCreate : function(){
3681         
3682         
3683         var cfg = {
3684             tag : this.tag || 'div',
3685             cls : 'navbar'
3686         };
3687           
3688         
3689         cfg.cn = [
3690             {
3691                 cls: 'nav',
3692                 tag : 'ul'
3693             }
3694         ];
3695         
3696          
3697         this.type = this.type || 'nav';
3698         if (['tabs','pills'].indexOf(this.type)!==-1) {
3699             cfg.cn[0].cls += ' nav-' + this.type
3700         
3701         
3702         } else {
3703             if (this.type!=='nav') {
3704                 Roo.log('nav type must be nav/tabs/pills')
3705             }
3706             cfg.cn[0].cls += ' navbar-nav'
3707         }
3708         
3709         
3710         
3711         
3712         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3713             cfg.cn[0].cls += ' nav-' + this.arrangement;
3714         }
3715         
3716         
3717         if (this.align === 'right') {
3718             cfg.cn[0].cls += ' navbar-right';
3719         }
3720         
3721         if (this.inverse) {
3722             cfg.cls += ' navbar-inverse';
3723             
3724         }
3725         
3726         
3727         return cfg;
3728     
3729         
3730     }
3731     
3732     
3733     
3734 });
3735
3736
3737
3738  
3739
3740  
3741        /*
3742  * - LGPL
3743  *
3744  * navbar
3745  * 
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavHeaderbar
3750  * @extends Roo.bootstrap.NavSimplebar
3751  * Bootstrap Sidebar class
3752  *
3753  * @cfg {String} brand what is brand
3754  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3755  * @cfg {String} brand_href href of the brand
3756  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3757  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3758  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3759  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3760  * 
3761  * @constructor
3762  * Create a new Sidebar
3763  * @param {Object} config The config object
3764  */
3765
3766
3767 Roo.bootstrap.NavHeaderbar = function(config){
3768     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3769       
3770 };
3771
3772 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3773     
3774     position: '',
3775     brand: '',
3776     brand_href: false,
3777     srButton : true,
3778     autohide : false,
3779     desktopCenter : false,
3780    
3781     
3782     getAutoCreate : function(){
3783         
3784         var   cfg = {
3785             tag: this.nav || 'nav',
3786             cls: 'navbar',
3787             role: 'navigation',
3788             cn: []
3789         };
3790         
3791         var cn = cfg.cn;
3792         if (this.desktopCenter) {
3793             cn.push({cls : 'container', cn : []});
3794             cn = cn[0].cn;
3795         }
3796         
3797         if(this.srButton){
3798             cn.push({
3799                 tag: 'div',
3800                 cls: 'navbar-header',
3801                 cn: [
3802                     {
3803                         tag: 'button',
3804                         type: 'button',
3805                         cls: 'navbar-toggle',
3806                         'data-toggle': 'collapse',
3807                         cn: [
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'sr-only',
3811                                 html: 'Toggle navigation'
3812                             },
3813                             {
3814                                 tag: 'span',
3815                                 cls: 'icon-bar'
3816                             },
3817                             {
3818                                 tag: 'span',
3819                                 cls: 'icon-bar'
3820                             },
3821                             {
3822                                 tag: 'span',
3823                                 cls: 'icon-bar'
3824                             }
3825                         ]
3826                     }
3827                 ]
3828             });
3829         }
3830         
3831         cn.push({
3832             tag: 'div',
3833             cls: 'collapse navbar-collapse',
3834             cn : []
3835         });
3836         
3837         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3838         
3839         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3840             cfg.cls += ' navbar-' + this.position;
3841             
3842             // tag can override this..
3843             
3844             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3845         }
3846         
3847         if (this.brand !== '') {
3848             cn[0].cn.push({
3849                 tag: 'a',
3850                 href: this.brand_href ? this.brand_href : '#',
3851                 cls: 'navbar-brand',
3852                 cn: [
3853                 this.brand
3854                 ]
3855             });
3856         }
3857         
3858         if(this.main){
3859             cfg.cls += ' main-nav';
3860         }
3861         
3862         
3863         return cfg;
3864
3865         
3866     },
3867     getHeaderChildContainer : function()
3868     {
3869         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3870             return this.el.select('.navbar-header',true).first();
3871         }
3872         
3873         return this.getChildContainer();
3874     },
3875     
3876     
3877     initEvents : function()
3878     {
3879         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3880         
3881         if (this.autohide) {
3882             
3883             var prevScroll = 0;
3884             var ft = this.el;
3885             
3886             Roo.get(document).on('scroll',function(e) {
3887                 var ns = Roo.get(document).getScroll().top;
3888                 var os = prevScroll;
3889                 prevScroll = ns;
3890                 
3891                 if(ns > os){
3892                     ft.removeClass('slideDown');
3893                     ft.addClass('slideUp');
3894                     return;
3895                 }
3896                 ft.removeClass('slideUp');
3897                 ft.addClass('slideDown');
3898                  
3899               
3900           },this);
3901         }
3902     }    
3903     
3904 });
3905
3906
3907
3908  
3909
3910  /*
3911  * - LGPL
3912  *
3913  * navbar
3914  * 
3915  */
3916
3917 /**
3918  * @class Roo.bootstrap.NavSidebar
3919  * @extends Roo.bootstrap.Navbar
3920  * Bootstrap Sidebar class
3921  * 
3922  * @constructor
3923  * Create a new Sidebar
3924  * @param {Object} config The config object
3925  */
3926
3927
3928 Roo.bootstrap.NavSidebar = function(config){
3929     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3930 };
3931
3932 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3933     
3934     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3935     
3936     getAutoCreate : function(){
3937         
3938         
3939         return  {
3940             tag: 'div',
3941             cls: 'sidebar sidebar-nav'
3942         };
3943     
3944         
3945     }
3946     
3947     
3948     
3949 });
3950
3951
3952
3953  
3954
3955  /*
3956  * - LGPL
3957  *
3958  * nav group
3959  * 
3960  */
3961
3962 /**
3963  * @class Roo.bootstrap.NavGroup
3964  * @extends Roo.bootstrap.Component
3965  * Bootstrap NavGroup class
3966  * @cfg {String} align (left|right)
3967  * @cfg {Boolean} inverse
3968  * @cfg {String} type (nav|pills|tab) default nav
3969  * @cfg {String} navId - reference Id for navbar.
3970
3971  * 
3972  * @constructor
3973  * Create a new nav group
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.NavGroup = function(config){
3978     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3979     this.navItems = [];
3980    
3981     Roo.bootstrap.NavGroup.register(this);
3982      this.addEvents({
3983         /**
3984              * @event changed
3985              * Fires when the active item changes
3986              * @param {Roo.bootstrap.NavGroup} this
3987              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3988              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3989          */
3990         'changed': true
3991      });
3992     
3993 };
3994
3995 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3996     
3997     align: '',
3998     inverse: false,
3999     form: false,
4000     type: 'nav',
4001     navId : '',
4002     // private
4003     
4004     navItems : false, 
4005     
4006     getAutoCreate : function()
4007     {
4008         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4009         
4010         cfg = {
4011             tag : 'ul',
4012             cls: 'nav' 
4013         };
4014         
4015         if (['tabs','pills'].indexOf(this.type)!==-1) {
4016             cfg.cls += ' nav-' + this.type
4017         } else {
4018             if (this.type!=='nav') {
4019                 Roo.log('nav type must be nav/tabs/pills')
4020             }
4021             cfg.cls += ' navbar-nav'
4022         }
4023         
4024         if (this.parent().sidebar) {
4025             cfg = {
4026                 tag: 'ul',
4027                 cls: 'dashboard-menu sidebar-menu'
4028             };
4029             
4030             return cfg;
4031         }
4032         
4033         if (this.form === true) {
4034             cfg = {
4035                 tag: 'form',
4036                 cls: 'navbar-form'
4037             };
4038             
4039             if (this.align === 'right') {
4040                 cfg.cls += ' navbar-right';
4041             } else {
4042                 cfg.cls += ' navbar-left';
4043             }
4044         }
4045         
4046         if (this.align === 'right') {
4047             cfg.cls += ' navbar-right';
4048         }
4049         
4050         if (this.inverse) {
4051             cfg.cls += ' navbar-inverse';
4052             
4053         }
4054         
4055         
4056         return cfg;
4057     },
4058     /**
4059     * sets the active Navigation item
4060     * @param {Roo.bootstrap.NavItem} the new current navitem
4061     */
4062     setActiveItem : function(item)
4063     {
4064         var prev = false;
4065         Roo.each(this.navItems, function(v){
4066             if (v == item) {
4067                 return ;
4068             }
4069             if (v.isActive()) {
4070                 v.setActive(false, true);
4071                 prev = v;
4072                 
4073             }
4074             
4075         });
4076
4077         item.setActive(true, true);
4078         this.fireEvent('changed', this, item, prev);
4079         
4080         
4081     },
4082     /**
4083     * gets the active Navigation item
4084     * @return {Roo.bootstrap.NavItem} the current navitem
4085     */
4086     getActive : function()
4087     {
4088         
4089         var prev = false;
4090         Roo.each(this.navItems, function(v){
4091             
4092             if (v.isActive()) {
4093                 prev = v;
4094                 
4095             }
4096             
4097         });
4098         return prev;
4099     },
4100     
4101     indexOfNav : function()
4102     {
4103         
4104         var prev = false;
4105         Roo.each(this.navItems, function(v,i){
4106             
4107             if (v.isActive()) {
4108                 prev = i;
4109                 
4110             }
4111             
4112         });
4113         return prev;
4114     },
4115     /**
4116     * adds a Navigation item
4117     * @param {Roo.bootstrap.NavItem} the navitem to add
4118     */
4119     addItem : function(cfg)
4120     {
4121         var cn = new Roo.bootstrap.NavItem(cfg);
4122         this.register(cn);
4123         cn.parentId = this.id;
4124         cn.onRender(this.el, null);
4125         return cn;
4126     },
4127     /**
4128     * register a Navigation item
4129     * @param {Roo.bootstrap.NavItem} the navitem to add
4130     */
4131     register : function(item)
4132     {
4133         this.navItems.push( item);
4134         item.navId = this.navId;
4135     
4136     },
4137     
4138     /**
4139     * clear all the Navigation item
4140     */
4141    
4142     clearAll : function()
4143     {
4144         this.navItems = [];
4145         this.el.dom.innerHTML = '';
4146     },
4147     
4148     getNavItem: function(tabId)
4149     {
4150         var ret = false;
4151         Roo.each(this.navItems, function(e) {
4152             if (e.tabId == tabId) {
4153                ret =  e;
4154                return false;
4155             }
4156             return true;
4157             
4158         });
4159         return ret;
4160     },
4161     
4162     setActiveNext : function()
4163     {
4164         var i = this.indexOfNav(this.getActive());
4165         if (i > this.navItems.length) {
4166             return;
4167         }
4168         this.setActiveItem(this.navItems[i+1]);
4169     },
4170     setActivePrev : function()
4171     {
4172         var i = this.indexOfNav(this.getActive());
4173         if (i  < 1) {
4174             return;
4175         }
4176         this.setActiveItem(this.navItems[i-1]);
4177     },
4178     clearWasActive : function(except) {
4179         Roo.each(this.navItems, function(e) {
4180             if (e.tabId != except.tabId && e.was_active) {
4181                e.was_active = false;
4182                return false;
4183             }
4184             return true;
4185             
4186         });
4187     },
4188     getWasActive : function ()
4189     {
4190         var r = false;
4191         Roo.each(this.navItems, function(e) {
4192             if (e.was_active) {
4193                r = e;
4194                return false;
4195             }
4196             return true;
4197             
4198         });
4199         return r;
4200     }
4201     
4202     
4203 });
4204
4205  
4206 Roo.apply(Roo.bootstrap.NavGroup, {
4207     
4208     groups: {},
4209      /**
4210     * register a Navigation Group
4211     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4212     */
4213     register : function(navgrp)
4214     {
4215         this.groups[navgrp.navId] = navgrp;
4216         
4217     },
4218     /**
4219     * fetch a Navigation Group based on the navigation ID
4220     * @param {string} the navgroup to add
4221     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4222     */
4223     get: function(navId) {
4224         if (typeof(this.groups[navId]) == 'undefined') {
4225             return false;
4226             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4227         }
4228         return this.groups[navId] ;
4229     }
4230     
4231     
4232     
4233 });
4234
4235  /*
4236  * - LGPL
4237  *
4238  * row
4239  * 
4240  */
4241
4242 /**
4243  * @class Roo.bootstrap.NavItem
4244  * @extends Roo.bootstrap.Component
4245  * Bootstrap Navbar.NavItem class
4246  * @cfg {String} href  link to
4247  * @cfg {String} html content of button
4248  * @cfg {String} badge text inside badge
4249  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4250  * @cfg {String} glyphicon name of glyphicon
4251  * @cfg {String} icon name of font awesome icon
4252  * @cfg {Boolean} active Is item active
4253  * @cfg {Boolean} disabled Is item disabled
4254  
4255  * @cfg {Boolean} preventDefault (true | false) default false
4256  * @cfg {String} tabId the tab that this item activates.
4257  * @cfg {String} tagtype (a|span) render as a href or span?
4258  * @cfg {Boolean} animateRef (true|false) link to element default false  
4259   
4260  * @constructor
4261  * Create a new Navbar Item
4262  * @param {Object} config The config object
4263  */
4264 Roo.bootstrap.NavItem = function(config){
4265     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4266     this.addEvents({
4267         // raw events
4268         /**
4269          * @event click
4270          * The raw click event for the entire grid.
4271          * @param {Roo.EventObject} e
4272          */
4273         "click" : true,
4274          /**
4275             * @event changed
4276             * Fires when the active item active state changes
4277             * @param {Roo.bootstrap.NavItem} this
4278             * @param {boolean} state the new state
4279              
4280          */
4281         'changed': true,
4282         /**
4283             * @event scrollto
4284             * Fires when scroll to element
4285             * @param {Roo.bootstrap.NavItem} this
4286             * @param {Object} options
4287             * @param {Roo.EventObject} e
4288              
4289          */
4290         'scrollto': true
4291     });
4292    
4293 };
4294
4295 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4296     
4297     href: false,
4298     html: '',
4299     badge: '',
4300     icon: false,
4301     glyphicon: false,
4302     active: false,
4303     preventDefault : false,
4304     tabId : false,
4305     tagtype : 'a',
4306     disabled : false,
4307     animateRef : false,
4308     was_active : false,
4309     
4310     getAutoCreate : function(){
4311          
4312         var cfg = {
4313             tag: 'li',
4314             cls: 'nav-item'
4315             
4316         };
4317         
4318         if (this.active) {
4319             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4320         }
4321         if (this.disabled) {
4322             cfg.cls += ' disabled';
4323         }
4324         
4325         if (this.href || this.html || this.glyphicon || this.icon) {
4326             cfg.cn = [
4327                 {
4328                     tag: this.tagtype,
4329                     href : this.href || "#",
4330                     html: this.html || ''
4331                 }
4332             ];
4333             
4334             if (this.icon) {
4335                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4336             }
4337
4338             if(this.glyphicon) {
4339                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4340             }
4341             
4342             if (this.menu) {
4343                 
4344                 cfg.cn[0].html += " <span class='caret'></span>";
4345              
4346             }
4347             
4348             if (this.badge !== '') {
4349                  
4350                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4351             }
4352         }
4353         
4354         
4355         
4356         return cfg;
4357     },
4358     initEvents: function() 
4359     {
4360         if (typeof (this.menu) != 'undefined') {
4361             this.menu.parentType = this.xtype;
4362             this.menu.triggerEl = this.el;
4363             this.menu = this.addxtype(Roo.apply({}, this.menu));
4364         }
4365         
4366         this.el.select('a',true).on('click', this.onClick, this);
4367         
4368         if(this.tagtype == 'span'){
4369             this.el.select('span',true).on('click', this.onClick, this);
4370         }
4371        
4372         // at this point parent should be available..
4373         this.parent().register(this);
4374     },
4375     
4376     onClick : function(e)
4377     {
4378         if (e.getTarget('.dropdown-menu-item')) {
4379             // did you click on a menu itemm.... - then don't trigger onclick..
4380             return;
4381         }
4382         
4383         if(
4384                 this.preventDefault || 
4385                 this.href == '#' 
4386         ){
4387             Roo.log("NavItem - prevent Default?");
4388             e.preventDefault();
4389         }
4390         
4391         if (this.disabled) {
4392             return;
4393         }
4394         
4395         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4396         if (tg && tg.transition) {
4397             Roo.log("waiting for the transitionend");
4398             return;
4399         }
4400         
4401         
4402         
4403         //Roo.log("fire event clicked");
4404         if(this.fireEvent('click', this, e) === false){
4405             return;
4406         };
4407         
4408         if(this.tagtype == 'span'){
4409             return;
4410         }
4411         
4412         //Roo.log(this.href);
4413         var ael = this.el.select('a',true).first();
4414         //Roo.log(ael);
4415         
4416         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4417             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4418             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4419                 return; // ignore... - it's a 'hash' to another page.
4420             }
4421             Roo.log("NavItem - prevent Default?");
4422             e.preventDefault();
4423             this.scrollToElement(e);
4424         }
4425         
4426         
4427         var p =  this.parent();
4428    
4429         if (['tabs','pills'].indexOf(p.type)!==-1) {
4430             if (typeof(p.setActiveItem) !== 'undefined') {
4431                 p.setActiveItem(this);
4432             }
4433         }
4434         
4435         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4436         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4437             // remove the collapsed menu expand...
4438             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4439         }
4440     },
4441     
4442     isActive: function () {
4443         return this.active
4444     },
4445     setActive : function(state, fire, is_was_active)
4446     {
4447         if (this.active && !state && this.navId) {
4448             this.was_active = true;
4449             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4450             if (nv) {
4451                 nv.clearWasActive(this);
4452             }
4453             
4454         }
4455         this.active = state;
4456         
4457         if (!state ) {
4458             this.el.removeClass('active');
4459         } else if (!this.el.hasClass('active')) {
4460             this.el.addClass('active');
4461         }
4462         if (fire) {
4463             this.fireEvent('changed', this, state);
4464         }
4465         
4466         // show a panel if it's registered and related..
4467         
4468         if (!this.navId || !this.tabId || !state || is_was_active) {
4469             return;
4470         }
4471         
4472         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4473         if (!tg) {
4474             return;
4475         }
4476         var pan = tg.getPanelByName(this.tabId);
4477         if (!pan) {
4478             return;
4479         }
4480         // if we can not flip to new panel - go back to old nav highlight..
4481         if (false == tg.showPanel(pan)) {
4482             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4483             if (nv) {
4484                 var onav = nv.getWasActive();
4485                 if (onav) {
4486                     onav.setActive(true, false, true);
4487                 }
4488             }
4489             
4490         }
4491         
4492         
4493         
4494     },
4495      // this should not be here...
4496     setDisabled : function(state)
4497     {
4498         this.disabled = state;
4499         if (!state ) {
4500             this.el.removeClass('disabled');
4501         } else if (!this.el.hasClass('disabled')) {
4502             this.el.addClass('disabled');
4503         }
4504         
4505     },
4506     
4507     /**
4508      * Fetch the element to display the tooltip on.
4509      * @return {Roo.Element} defaults to this.el
4510      */
4511     tooltipEl : function()
4512     {
4513         return this.el.select('' + this.tagtype + '', true).first();
4514     },
4515     
4516     scrollToElement : function(e)
4517     {
4518         var c = document.body;
4519         
4520         /*
4521          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4522          */
4523         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4524             c = document.documentElement;
4525         }
4526         
4527         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4528         
4529         if(!target){
4530             return;
4531         }
4532
4533         var o = target.calcOffsetsTo(c);
4534         
4535         var options = {
4536             target : target,
4537             value : o[1]
4538         };
4539         
4540         this.fireEvent('scrollto', this, options, e);
4541         
4542         Roo.get(c).scrollTo('top', options.value, true);
4543         
4544         return;
4545     }
4546 });
4547  
4548
4549  /*
4550  * - LGPL
4551  *
4552  * sidebar item
4553  *
4554  *  li
4555  *    <span> icon </span>
4556  *    <span> text </span>
4557  *    <span>badge </span>
4558  */
4559
4560 /**
4561  * @class Roo.bootstrap.NavSidebarItem
4562  * @extends Roo.bootstrap.NavItem
4563  * Bootstrap Navbar.NavSidebarItem class
4564  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4565  * {bool} open is the menu open
4566  * @constructor
4567  * Create a new Navbar Button
4568  * @param {Object} config The config object
4569  */
4570 Roo.bootstrap.NavSidebarItem = function(config){
4571     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4572     this.addEvents({
4573         // raw events
4574         /**
4575          * @event click
4576          * The raw click event for the entire grid.
4577          * @param {Roo.EventObject} e
4578          */
4579         "click" : true,
4580          /**
4581             * @event changed
4582             * Fires when the active item active state changes
4583             * @param {Roo.bootstrap.NavSidebarItem} this
4584             * @param {boolean} state the new state
4585              
4586          */
4587         'changed': true
4588     });
4589    
4590 };
4591
4592 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4593     
4594     badgeWeight : 'default',
4595     
4596     open: false,
4597     
4598     getAutoCreate : function(){
4599         
4600         
4601         var a = {
4602                 tag: 'a',
4603                 href : this.href || '#',
4604                 cls: '',
4605                 html : '',
4606                 cn : []
4607         };
4608         var cfg = {
4609             tag: 'li',
4610             cls: '',
4611             cn: [ a ]
4612         };
4613         var span = {
4614             tag: 'span',
4615             html : this.html || ''
4616         };
4617         
4618         
4619         if (this.active) {
4620             cfg.cls += ' active';
4621         }
4622         
4623         if (this.disabled) {
4624             cfg.cls += ' disabled';
4625         }
4626         if (this.open) {
4627             cfg.cls += ' open x-open';
4628         }
4629         // left icon..
4630         if (this.glyphicon || this.icon) {
4631             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4632             a.cn.push({ tag : 'i', cls : c }) ;
4633         }
4634         // html..
4635         a.cn.push(span);
4636         // then badge..
4637         if (this.badge !== '') {
4638             
4639             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4640         }
4641         // fi
4642         if (this.menu) {
4643             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4644             a.cls += 'dropdown-toggle treeview' ;
4645         }
4646         
4647         return cfg;
4648          
4649            
4650     },
4651     
4652     initEvents : function()
4653     { 
4654         if (typeof (this.menu) != 'undefined') {
4655             this.menu.parentType = this.xtype;
4656             this.menu.triggerEl = this.el;
4657             this.menu = this.addxtype(Roo.apply({}, this.menu));
4658         }
4659         
4660         this.el.on('click', this.onClick, this);
4661        
4662     
4663         if(this.badge !== ''){
4664  
4665             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4666         }
4667         
4668     },
4669     
4670     onClick : function(e)
4671     {
4672         if(this.disabled){
4673             e.preventDefault();
4674             return;
4675         }
4676         
4677         if(this.preventDefault){
4678             e.preventDefault();
4679         }
4680         
4681         this.fireEvent('click', this);
4682     },
4683     
4684     disable : function()
4685     {
4686         this.setDisabled(true);
4687     },
4688     
4689     enable : function()
4690     {
4691         this.setDisabled(false);
4692     },
4693     
4694     setDisabled : function(state)
4695     {
4696         if(this.disabled == state){
4697             return;
4698         }
4699         
4700         this.disabled = state;
4701         
4702         if (state) {
4703             this.el.addClass('disabled');
4704             return;
4705         }
4706         
4707         this.el.removeClass('disabled');
4708         
4709         return;
4710     },
4711     
4712     setActive : function(state)
4713     {
4714         if(this.active == state){
4715             return;
4716         }
4717         
4718         this.active = state;
4719         
4720         if (state) {
4721             this.el.addClass('active');
4722             return;
4723         }
4724         
4725         this.el.removeClass('active');
4726         
4727         return;
4728     },
4729     
4730     isActive: function () 
4731     {
4732         return this.active;
4733     },
4734     
4735     setBadge : function(str)
4736     {
4737         if(!this.badgeEl){
4738             return;
4739         }
4740         
4741         this.badgeEl.dom.innerHTML = str;
4742     }
4743     
4744    
4745      
4746  
4747 });
4748  
4749
4750  /*
4751  * - LGPL
4752  *
4753  * row
4754  * 
4755  */
4756
4757 /**
4758  * @class Roo.bootstrap.Row
4759  * @extends Roo.bootstrap.Component
4760  * Bootstrap Row class (contains columns...)
4761  * 
4762  * @constructor
4763  * Create a new Row
4764  * @param {Object} config The config object
4765  */
4766
4767 Roo.bootstrap.Row = function(config){
4768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4769 };
4770
4771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4772     
4773     getAutoCreate : function(){
4774        return {
4775             cls: 'row clearfix'
4776        };
4777     }
4778     
4779     
4780 });
4781
4782  
4783
4784  /*
4785  * - LGPL
4786  *
4787  * element
4788  * 
4789  */
4790
4791 /**
4792  * @class Roo.bootstrap.Element
4793  * @extends Roo.bootstrap.Component
4794  * Bootstrap Element class
4795  * @cfg {String} html contents of the element
4796  * @cfg {String} tag tag of the element
4797  * @cfg {String} cls class of the element
4798  * @cfg {Boolean} preventDefault (true|false) default false
4799  * @cfg {Boolean} clickable (true|false) default false
4800  * 
4801  * @constructor
4802  * Create a new Element
4803  * @param {Object} config The config object
4804  */
4805
4806 Roo.bootstrap.Element = function(config){
4807     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4808     
4809     this.addEvents({
4810         // raw events
4811         /**
4812          * @event click
4813          * When a element is chick
4814          * @param {Roo.bootstrap.Element} this
4815          * @param {Roo.EventObject} e
4816          */
4817         "click" : true
4818     });
4819 };
4820
4821 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4822     
4823     tag: 'div',
4824     cls: '',
4825     html: '',
4826     preventDefault: false, 
4827     clickable: false,
4828     
4829     getAutoCreate : function(){
4830         
4831         var cfg = {
4832             tag: this.tag,
4833             cls: this.cls,
4834             html: this.html
4835         };
4836         
4837         return cfg;
4838     },
4839     
4840     initEvents: function() 
4841     {
4842         Roo.bootstrap.Element.superclass.initEvents.call(this);
4843         
4844         if(this.clickable){
4845             this.el.on('click', this.onClick, this);
4846         }
4847         
4848     },
4849     
4850     onClick : function(e)
4851     {
4852         if(this.preventDefault){
4853             e.preventDefault();
4854         }
4855         
4856         this.fireEvent('click', this, e);
4857     },
4858     
4859     getValue : function()
4860     {
4861         return this.el.dom.innerHTML;
4862     },
4863     
4864     setValue : function(value)
4865     {
4866         this.el.dom.innerHTML = value;
4867     }
4868    
4869 });
4870
4871  
4872
4873  /*
4874  * - LGPL
4875  *
4876  * pagination
4877  * 
4878  */
4879
4880 /**
4881  * @class Roo.bootstrap.Pagination
4882  * @extends Roo.bootstrap.Component
4883  * Bootstrap Pagination class
4884  * @cfg {String} size xs | sm | md | lg
4885  * @cfg {Boolean} inverse false | true
4886  * 
4887  * @constructor
4888  * Create a new Pagination
4889  * @param {Object} config The config object
4890  */
4891
4892 Roo.bootstrap.Pagination = function(config){
4893     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4894 };
4895
4896 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4897     
4898     cls: false,
4899     size: false,
4900     inverse: false,
4901     
4902     getAutoCreate : function(){
4903         var cfg = {
4904             tag: 'ul',
4905                 cls: 'pagination'
4906         };
4907         if (this.inverse) {
4908             cfg.cls += ' inverse';
4909         }
4910         if (this.html) {
4911             cfg.html=this.html;
4912         }
4913         if (this.cls) {
4914             cfg.cls += " " + this.cls;
4915         }
4916         return cfg;
4917     }
4918    
4919 });
4920
4921  
4922
4923  /*
4924  * - LGPL
4925  *
4926  * Pagination item
4927  * 
4928  */
4929
4930
4931 /**
4932  * @class Roo.bootstrap.PaginationItem
4933  * @extends Roo.bootstrap.Component
4934  * Bootstrap PaginationItem class
4935  * @cfg {String} html text
4936  * @cfg {String} href the link
4937  * @cfg {Boolean} preventDefault (true | false) default true
4938  * @cfg {Boolean} active (true | false) default false
4939  * @cfg {Boolean} disabled default false
4940  * 
4941  * 
4942  * @constructor
4943  * Create a new PaginationItem
4944  * @param {Object} config The config object
4945  */
4946
4947
4948 Roo.bootstrap.PaginationItem = function(config){
4949     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4950     this.addEvents({
4951         // raw events
4952         /**
4953          * @event click
4954          * The raw click event for the entire grid.
4955          * @param {Roo.EventObject} e
4956          */
4957         "click" : true
4958     });
4959 };
4960
4961 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4962     
4963     href : false,
4964     html : false,
4965     preventDefault: true,
4966     active : false,
4967     cls : false,
4968     disabled: false,
4969     
4970     getAutoCreate : function(){
4971         var cfg= {
4972             tag: 'li',
4973             cn: [
4974                 {
4975                     tag : 'a',
4976                     href : this.href ? this.href : '#',
4977                     html : this.html ? this.html : ''
4978                 }
4979             ]
4980         };
4981         
4982         if(this.cls){
4983             cfg.cls = this.cls;
4984         }
4985         
4986         if(this.disabled){
4987             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4988         }
4989         
4990         if(this.active){
4991             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4992         }
4993         
4994         return cfg;
4995     },
4996     
4997     initEvents: function() {
4998         
4999         this.el.on('click', this.onClick, this);
5000         
5001     },
5002     onClick : function(e)
5003     {
5004         Roo.log('PaginationItem on click ');
5005         if(this.preventDefault){
5006             e.preventDefault();
5007         }
5008         
5009         if(this.disabled){
5010             return;
5011         }
5012         
5013         this.fireEvent('click', this, e);
5014     }
5015    
5016 });
5017
5018  
5019
5020  /*
5021  * - LGPL
5022  *
5023  * slider
5024  * 
5025  */
5026
5027
5028 /**
5029  * @class Roo.bootstrap.Slider
5030  * @extends Roo.bootstrap.Component
5031  * Bootstrap Slider class
5032  *    
5033  * @constructor
5034  * Create a new Slider
5035  * @param {Object} config The config object
5036  */
5037
5038 Roo.bootstrap.Slider = function(config){
5039     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5040 };
5041
5042 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5043     
5044     getAutoCreate : function(){
5045         
5046         var cfg = {
5047             tag: 'div',
5048             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5049             cn: [
5050                 {
5051                     tag: 'a',
5052                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5053                 }
5054             ]
5055         };
5056         
5057         return cfg;
5058     }
5059    
5060 });
5061
5062  /*
5063  * Based on:
5064  * Ext JS Library 1.1.1
5065  * Copyright(c) 2006-2007, Ext JS, LLC.
5066  *
5067  * Originally Released Under LGPL - original licence link has changed is not relivant.
5068  *
5069  * Fork - LGPL
5070  * <script type="text/javascript">
5071  */
5072  
5073
5074 /**
5075  * @class Roo.grid.ColumnModel
5076  * @extends Roo.util.Observable
5077  * This is the default implementation of a ColumnModel used by the Grid. It defines
5078  * the columns in the grid.
5079  * <br>Usage:<br>
5080  <pre><code>
5081  var colModel = new Roo.grid.ColumnModel([
5082         {header: "Ticker", width: 60, sortable: true, locked: true},
5083         {header: "Company Name", width: 150, sortable: true},
5084         {header: "Market Cap.", width: 100, sortable: true},
5085         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5086         {header: "Employees", width: 100, sortable: true, resizable: false}
5087  ]);
5088  </code></pre>
5089  * <p>
5090  
5091  * The config options listed for this class are options which may appear in each
5092  * individual column definition.
5093  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5094  * @constructor
5095  * @param {Object} config An Array of column config objects. See this class's
5096  * config objects for details.
5097 */
5098 Roo.grid.ColumnModel = function(config){
5099         /**
5100      * The config passed into the constructor
5101      */
5102     this.config = config;
5103     this.lookup = {};
5104
5105     // if no id, create one
5106     // if the column does not have a dataIndex mapping,
5107     // map it to the order it is in the config
5108     for(var i = 0, len = config.length; i < len; i++){
5109         var c = config[i];
5110         if(typeof c.dataIndex == "undefined"){
5111             c.dataIndex = i;
5112         }
5113         if(typeof c.renderer == "string"){
5114             c.renderer = Roo.util.Format[c.renderer];
5115         }
5116         if(typeof c.id == "undefined"){
5117             c.id = Roo.id();
5118         }
5119         if(c.editor && c.editor.xtype){
5120             c.editor  = Roo.factory(c.editor, Roo.grid);
5121         }
5122         if(c.editor && c.editor.isFormField){
5123             c.editor = new Roo.grid.GridEditor(c.editor);
5124         }
5125         this.lookup[c.id] = c;
5126     }
5127
5128     /**
5129      * The width of columns which have no width specified (defaults to 100)
5130      * @type Number
5131      */
5132     this.defaultWidth = 100;
5133
5134     /**
5135      * Default sortable of columns which have no sortable specified (defaults to false)
5136      * @type Boolean
5137      */
5138     this.defaultSortable = false;
5139
5140     this.addEvents({
5141         /**
5142              * @event widthchange
5143              * Fires when the width of a column changes.
5144              * @param {ColumnModel} this
5145              * @param {Number} columnIndex The column index
5146              * @param {Number} newWidth The new width
5147              */
5148             "widthchange": true,
5149         /**
5150              * @event headerchange
5151              * Fires when the text of a header changes.
5152              * @param {ColumnModel} this
5153              * @param {Number} columnIndex The column index
5154              * @param {Number} newText The new header text
5155              */
5156             "headerchange": true,
5157         /**
5158              * @event hiddenchange
5159              * Fires when a column is hidden or "unhidden".
5160              * @param {ColumnModel} this
5161              * @param {Number} columnIndex The column index
5162              * @param {Boolean} hidden true if hidden, false otherwise
5163              */
5164             "hiddenchange": true,
5165             /**
5166          * @event columnmoved
5167          * Fires when a column is moved.
5168          * @param {ColumnModel} this
5169          * @param {Number} oldIndex
5170          * @param {Number} newIndex
5171          */
5172         "columnmoved" : true,
5173         /**
5174          * @event columlockchange
5175          * Fires when a column's locked state is changed
5176          * @param {ColumnModel} this
5177          * @param {Number} colIndex
5178          * @param {Boolean} locked true if locked
5179          */
5180         "columnlockchange" : true
5181     });
5182     Roo.grid.ColumnModel.superclass.constructor.call(this);
5183 };
5184 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5185     /**
5186      * @cfg {String} header The header text to display in the Grid view.
5187      */
5188     /**
5189      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5190      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5191      * specified, the column's index is used as an index into the Record's data Array.
5192      */
5193     /**
5194      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5195      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5196      */
5197     /**
5198      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5199      * Defaults to the value of the {@link #defaultSortable} property.
5200      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5201      */
5202     /**
5203      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5204      */
5205     /**
5206      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5207      */
5208     /**
5209      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5210      */
5211     /**
5212      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5213      */
5214     /**
5215      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5216      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5217      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5218      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5219      */
5220        /**
5221      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5222      */
5223     /**
5224      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5225      */
5226     /**
5227      * @cfg {String} cursor (Optional)
5228      */
5229     /**
5230      * @cfg {String} tooltip (Optional)
5231      */
5232     /**
5233      * @cfg {Number} xs (Optional)
5234      */
5235     /**
5236      * @cfg {Number} sm (Optional)
5237      */
5238     /**
5239      * @cfg {Number} md (Optional)
5240      */
5241     /**
5242      * @cfg {Number} lg (Optional)
5243      */
5244     /**
5245      * Returns the id of the column at the specified index.
5246      * @param {Number} index The column index
5247      * @return {String} the id
5248      */
5249     getColumnId : function(index){
5250         return this.config[index].id;
5251     },
5252
5253     /**
5254      * Returns the column for a specified id.
5255      * @param {String} id The column id
5256      * @return {Object} the column
5257      */
5258     getColumnById : function(id){
5259         return this.lookup[id];
5260     },
5261
5262     
5263     /**
5264      * Returns the column for a specified dataIndex.
5265      * @param {String} dataIndex The column dataIndex
5266      * @return {Object|Boolean} the column or false if not found
5267      */
5268     getColumnByDataIndex: function(dataIndex){
5269         var index = this.findColumnIndex(dataIndex);
5270         return index > -1 ? this.config[index] : false;
5271     },
5272     
5273     /**
5274      * Returns the index for a specified column id.
5275      * @param {String} id The column id
5276      * @return {Number} the index, or -1 if not found
5277      */
5278     getIndexById : function(id){
5279         for(var i = 0, len = this.config.length; i < len; i++){
5280             if(this.config[i].id == id){
5281                 return i;
5282             }
5283         }
5284         return -1;
5285     },
5286     
5287     /**
5288      * Returns the index for a specified column dataIndex.
5289      * @param {String} dataIndex The column dataIndex
5290      * @return {Number} the index, or -1 if not found
5291      */
5292     
5293     findColumnIndex : function(dataIndex){
5294         for(var i = 0, len = this.config.length; i < len; i++){
5295             if(this.config[i].dataIndex == dataIndex){
5296                 return i;
5297             }
5298         }
5299         return -1;
5300     },
5301     
5302     
5303     moveColumn : function(oldIndex, newIndex){
5304         var c = this.config[oldIndex];
5305         this.config.splice(oldIndex, 1);
5306         this.config.splice(newIndex, 0, c);
5307         this.dataMap = null;
5308         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5309     },
5310
5311     isLocked : function(colIndex){
5312         return this.config[colIndex].locked === true;
5313     },
5314
5315     setLocked : function(colIndex, value, suppressEvent){
5316         if(this.isLocked(colIndex) == value){
5317             return;
5318         }
5319         this.config[colIndex].locked = value;
5320         if(!suppressEvent){
5321             this.fireEvent("columnlockchange", this, colIndex, value);
5322         }
5323     },
5324
5325     getTotalLockedWidth : function(){
5326         var totalWidth = 0;
5327         for(var i = 0; i < this.config.length; i++){
5328             if(this.isLocked(i) && !this.isHidden(i)){
5329                 this.totalWidth += this.getColumnWidth(i);
5330             }
5331         }
5332         return totalWidth;
5333     },
5334
5335     getLockedCount : function(){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(!this.isLocked(i)){
5338                 return i;
5339             }
5340         }
5341         
5342         return this.config.length;
5343     },
5344
5345     /**
5346      * Returns the number of columns.
5347      * @return {Number}
5348      */
5349     getColumnCount : function(visibleOnly){
5350         if(visibleOnly === true){
5351             var c = 0;
5352             for(var i = 0, len = this.config.length; i < len; i++){
5353                 if(!this.isHidden(i)){
5354                     c++;
5355                 }
5356             }
5357             return c;
5358         }
5359         return this.config.length;
5360     },
5361
5362     /**
5363      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5364      * @param {Function} fn
5365      * @param {Object} scope (optional)
5366      * @return {Array} result
5367      */
5368     getColumnsBy : function(fn, scope){
5369         var r = [];
5370         for(var i = 0, len = this.config.length; i < len; i++){
5371             var c = this.config[i];
5372             if(fn.call(scope||this, c, i) === true){
5373                 r[r.length] = c;
5374             }
5375         }
5376         return r;
5377     },
5378
5379     /**
5380      * Returns true if the specified column is sortable.
5381      * @param {Number} col The column index
5382      * @return {Boolean}
5383      */
5384     isSortable : function(col){
5385         if(typeof this.config[col].sortable == "undefined"){
5386             return this.defaultSortable;
5387         }
5388         return this.config[col].sortable;
5389     },
5390
5391     /**
5392      * Returns the rendering (formatting) function defined for the column.
5393      * @param {Number} col The column index.
5394      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5395      */
5396     getRenderer : function(col){
5397         if(!this.config[col].renderer){
5398             return Roo.grid.ColumnModel.defaultRenderer;
5399         }
5400         return this.config[col].renderer;
5401     },
5402
5403     /**
5404      * Sets the rendering (formatting) function for a column.
5405      * @param {Number} col The column index
5406      * @param {Function} fn The function to use to process the cell's raw data
5407      * to return HTML markup for the grid view. The render function is called with
5408      * the following parameters:<ul>
5409      * <li>Data value.</li>
5410      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5411      * <li>css A CSS style string to apply to the table cell.</li>
5412      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5413      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5414      * <li>Row index</li>
5415      * <li>Column index</li>
5416      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5417      */
5418     setRenderer : function(col, fn){
5419         this.config[col].renderer = fn;
5420     },
5421
5422     /**
5423      * Returns the width for the specified column.
5424      * @param {Number} col The column index
5425      * @return {Number}
5426      */
5427     getColumnWidth : function(col){
5428         return this.config[col].width * 1 || this.defaultWidth;
5429     },
5430
5431     /**
5432      * Sets the width for a column.
5433      * @param {Number} col The column index
5434      * @param {Number} width The new width
5435      */
5436     setColumnWidth : function(col, width, suppressEvent){
5437         this.config[col].width = width;
5438         this.totalWidth = null;
5439         if(!suppressEvent){
5440              this.fireEvent("widthchange", this, col, width);
5441         }
5442     },
5443
5444     /**
5445      * Returns the total width of all columns.
5446      * @param {Boolean} includeHidden True to include hidden column widths
5447      * @return {Number}
5448      */
5449     getTotalWidth : function(includeHidden){
5450         if(!this.totalWidth){
5451             this.totalWidth = 0;
5452             for(var i = 0, len = this.config.length; i < len; i++){
5453                 if(includeHidden || !this.isHidden(i)){
5454                     this.totalWidth += this.getColumnWidth(i);
5455                 }
5456             }
5457         }
5458         return this.totalWidth;
5459     },
5460
5461     /**
5462      * Returns the header for the specified column.
5463      * @param {Number} col The column index
5464      * @return {String}
5465      */
5466     getColumnHeader : function(col){
5467         return this.config[col].header;
5468     },
5469
5470     /**
5471      * Sets the header for a column.
5472      * @param {Number} col The column index
5473      * @param {String} header The new header
5474      */
5475     setColumnHeader : function(col, header){
5476         this.config[col].header = header;
5477         this.fireEvent("headerchange", this, col, header);
5478     },
5479
5480     /**
5481      * Returns the tooltip for the specified column.
5482      * @param {Number} col The column index
5483      * @return {String}
5484      */
5485     getColumnTooltip : function(col){
5486             return this.config[col].tooltip;
5487     },
5488     /**
5489      * Sets the tooltip for a column.
5490      * @param {Number} col The column index
5491      * @param {String} tooltip The new tooltip
5492      */
5493     setColumnTooltip : function(col, tooltip){
5494             this.config[col].tooltip = tooltip;
5495     },
5496
5497     /**
5498      * Returns the dataIndex for the specified column.
5499      * @param {Number} col The column index
5500      * @return {Number}
5501      */
5502     getDataIndex : function(col){
5503         return this.config[col].dataIndex;
5504     },
5505
5506     /**
5507      * Sets the dataIndex for a column.
5508      * @param {Number} col The column index
5509      * @param {Number} dataIndex The new dataIndex
5510      */
5511     setDataIndex : function(col, dataIndex){
5512         this.config[col].dataIndex = dataIndex;
5513     },
5514
5515     
5516     
5517     /**
5518      * Returns true if the cell is editable.
5519      * @param {Number} colIndex The column index
5520      * @param {Number} rowIndex The row index - this is nto actually used..?
5521      * @return {Boolean}
5522      */
5523     isCellEditable : function(colIndex, rowIndex){
5524         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5525     },
5526
5527     /**
5528      * Returns the editor defined for the cell/column.
5529      * return false or null to disable editing.
5530      * @param {Number} colIndex The column index
5531      * @param {Number} rowIndex The row index
5532      * @return {Object}
5533      */
5534     getCellEditor : function(colIndex, rowIndex){
5535         return this.config[colIndex].editor;
5536     },
5537
5538     /**
5539      * Sets if a column is editable.
5540      * @param {Number} col The column index
5541      * @param {Boolean} editable True if the column is editable
5542      */
5543     setEditable : function(col, editable){
5544         this.config[col].editable = editable;
5545     },
5546
5547
5548     /**
5549      * Returns true if the column is hidden.
5550      * @param {Number} colIndex The column index
5551      * @return {Boolean}
5552      */
5553     isHidden : function(colIndex){
5554         return this.config[colIndex].hidden;
5555     },
5556
5557
5558     /**
5559      * Returns true if the column width cannot be changed
5560      */
5561     isFixed : function(colIndex){
5562         return this.config[colIndex].fixed;
5563     },
5564
5565     /**
5566      * Returns true if the column can be resized
5567      * @return {Boolean}
5568      */
5569     isResizable : function(colIndex){
5570         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5571     },
5572     /**
5573      * Sets if a column is hidden.
5574      * @param {Number} colIndex The column index
5575      * @param {Boolean} hidden True if the column is hidden
5576      */
5577     setHidden : function(colIndex, hidden){
5578         this.config[colIndex].hidden = hidden;
5579         this.totalWidth = null;
5580         this.fireEvent("hiddenchange", this, colIndex, hidden);
5581     },
5582
5583     /**
5584      * Sets the editor for a column.
5585      * @param {Number} col The column index
5586      * @param {Object} editor The editor object
5587      */
5588     setEditor : function(col, editor){
5589         this.config[col].editor = editor;
5590     }
5591 });
5592
5593 Roo.grid.ColumnModel.defaultRenderer = function(value)
5594 {
5595     if(typeof value == "object") {
5596         return value;
5597     }
5598         if(typeof value == "string" && value.length < 1){
5599             return "&#160;";
5600         }
5601     
5602         return String.format("{0}", value);
5603 };
5604
5605 // Alias for backwards compatibility
5606 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5607 /*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 /**
5619  * @class Roo.LoadMask
5620  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5621  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5622  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5623  * element's UpdateManager load indicator and will be destroyed after the initial load.
5624  * @constructor
5625  * Create a new LoadMask
5626  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5627  * @param {Object} config The config object
5628  */
5629 Roo.LoadMask = function(el, config){
5630     this.el = Roo.get(el);
5631     Roo.apply(this, config);
5632     if(this.store){
5633         this.store.on('beforeload', this.onBeforeLoad, this);
5634         this.store.on('load', this.onLoad, this);
5635         this.store.on('loadexception', this.onLoadException, this);
5636         this.removeMask = false;
5637     }else{
5638         var um = this.el.getUpdateManager();
5639         um.showLoadIndicator = false; // disable the default indicator
5640         um.on('beforeupdate', this.onBeforeLoad, this);
5641         um.on('update', this.onLoad, this);
5642         um.on('failure', this.onLoad, this);
5643         this.removeMask = true;
5644     }
5645 };
5646
5647 Roo.LoadMask.prototype = {
5648     /**
5649      * @cfg {Boolean} removeMask
5650      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5651      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5652      */
5653     /**
5654      * @cfg {String} msg
5655      * The text to display in a centered loading message box (defaults to 'Loading...')
5656      */
5657     msg : 'Loading...',
5658     /**
5659      * @cfg {String} msgCls
5660      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5661      */
5662     msgCls : 'x-mask-loading',
5663
5664     /**
5665      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5666      * @type Boolean
5667      */
5668     disabled: false,
5669
5670     /**
5671      * Disables the mask to prevent it from being displayed
5672      */
5673     disable : function(){
5674        this.disabled = true;
5675     },
5676
5677     /**
5678      * Enables the mask so that it can be displayed
5679      */
5680     enable : function(){
5681         this.disabled = false;
5682     },
5683     
5684     onLoadException : function()
5685     {
5686         Roo.log(arguments);
5687         
5688         if (typeof(arguments[3]) != 'undefined') {
5689             Roo.MessageBox.alert("Error loading",arguments[3]);
5690         } 
5691         /*
5692         try {
5693             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5694                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5695             }   
5696         } catch(e) {
5697             
5698         }
5699         */
5700     
5701         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5702     },
5703     // private
5704     onLoad : function()
5705     {
5706         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5707     },
5708
5709     // private
5710     onBeforeLoad : function(){
5711         if(!this.disabled){
5712             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5713         }
5714     },
5715
5716     // private
5717     destroy : function(){
5718         if(this.store){
5719             this.store.un('beforeload', this.onBeforeLoad, this);
5720             this.store.un('load', this.onLoad, this);
5721             this.store.un('loadexception', this.onLoadException, this);
5722         }else{
5723             var um = this.el.getUpdateManager();
5724             um.un('beforeupdate', this.onBeforeLoad, this);
5725             um.un('update', this.onLoad, this);
5726             um.un('failure', this.onLoad, this);
5727         }
5728     }
5729 };/*
5730  * - LGPL
5731  *
5732  * table
5733  * 
5734  */
5735
5736 /**
5737  * @class Roo.bootstrap.Table
5738  * @extends Roo.bootstrap.Component
5739  * Bootstrap Table class
5740  * @cfg {String} cls table class
5741  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5742  * @cfg {String} bgcolor Specifies the background color for a table
5743  * @cfg {Number} border Specifies whether the table cells should have borders or not
5744  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5745  * @cfg {Number} cellspacing Specifies the space between cells
5746  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5747  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5748  * @cfg {String} sortable Specifies that the table should be sortable
5749  * @cfg {String} summary Specifies a summary of the content of a table
5750  * @cfg {Number} width Specifies the width of a table
5751  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5752  * 
5753  * @cfg {boolean} striped Should the rows be alternative striped
5754  * @cfg {boolean} bordered Add borders to the table
5755  * @cfg {boolean} hover Add hover highlighting
5756  * @cfg {boolean} condensed Format condensed
5757  * @cfg {boolean} responsive Format condensed
5758  * @cfg {Boolean} loadMask (true|false) default false
5759  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5760  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5761  * @cfg {Boolean} rowSelection (true|false) default false
5762  * @cfg {Boolean} cellSelection (true|false) default false
5763  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5764  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5765  
5766  * 
5767  * @constructor
5768  * Create a new Table
5769  * @param {Object} config The config object
5770  */
5771
5772 Roo.bootstrap.Table = function(config){
5773     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5774     
5775   
5776     
5777     // BC...
5778     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5779     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5780     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5781     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5782     
5783     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5784     if (this.sm) {
5785         this.sm.grid = this;
5786         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5787         this.sm = this.selModel;
5788         this.sm.xmodule = this.xmodule || false;
5789     }
5790     
5791     if (this.cm && typeof(this.cm.config) == 'undefined') {
5792         this.colModel = new Roo.grid.ColumnModel(this.cm);
5793         this.cm = this.colModel;
5794         this.cm.xmodule = this.xmodule || false;
5795     }
5796     if (this.store) {
5797         this.store= Roo.factory(this.store, Roo.data);
5798         this.ds = this.store;
5799         this.ds.xmodule = this.xmodule || false;
5800          
5801     }
5802     if (this.footer && this.store) {
5803         this.footer.dataSource = this.ds;
5804         this.footer = Roo.factory(this.footer);
5805     }
5806     
5807     /** @private */
5808     this.addEvents({
5809         /**
5810          * @event cellclick
5811          * Fires when a cell is clicked
5812          * @param {Roo.bootstrap.Table} this
5813          * @param {Roo.Element} el
5814          * @param {Number} rowIndex
5815          * @param {Number} columnIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "cellclick" : true,
5819         /**
5820          * @event celldblclick
5821          * Fires when a cell is double clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "celldblclick" : true,
5829         /**
5830          * @event rowclick
5831          * Fires when a row is clicked
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Roo.EventObject} e
5836          */
5837         "rowclick" : true,
5838         /**
5839          * @event rowdblclick
5840          * Fires when a row is double clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Roo.EventObject} e
5845          */
5846         "rowdblclick" : true,
5847         /**
5848          * @event mouseover
5849          * Fires when a mouseover occur
5850          * @param {Roo.bootstrap.Table} this
5851          * @param {Roo.Element} el
5852          * @param {Number} rowIndex
5853          * @param {Number} columnIndex
5854          * @param {Roo.EventObject} e
5855          */
5856         "mouseover" : true,
5857         /**
5858          * @event mouseout
5859          * Fires when a mouseout occur
5860          * @param {Roo.bootstrap.Table} this
5861          * @param {Roo.Element} el
5862          * @param {Number} rowIndex
5863          * @param {Number} columnIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "mouseout" : true,
5867         /**
5868          * @event rowclass
5869          * Fires when a row is rendered, so you can change add a style to it.
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5872          */
5873         'rowclass' : true,
5874           /**
5875          * @event rowsrendered
5876          * Fires when all the  rows have been rendered
5877          * @param {Roo.bootstrap.Table} this
5878          */
5879         'rowsrendered' : true,
5880         /**
5881          * @event contextmenu
5882          * The raw contextmenu event for the entire grid.
5883          * @param {Roo.EventObject} e
5884          */
5885         "contextmenu" : true,
5886         /**
5887          * @event rowcontextmenu
5888          * Fires when a row is right clicked
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Number} rowIndex
5891          * @param {Roo.EventObject} e
5892          */
5893         "rowcontextmenu" : true,
5894         /**
5895          * @event cellcontextmenu
5896          * Fires when a cell is right clicked
5897          * @param {Roo.bootstrap.Table} this
5898          * @param {Number} rowIndex
5899          * @param {Number} cellIndex
5900          * @param {Roo.EventObject} e
5901          */
5902          "cellcontextmenu" : true,
5903          /**
5904          * @event headercontextmenu
5905          * Fires when a header is right clicked
5906          * @param {Roo.bootstrap.Table} this
5907          * @param {Number} columnIndex
5908          * @param {Roo.EventObject} e
5909          */
5910         "headercontextmenu" : true
5911     });
5912 };
5913
5914 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5915     
5916     cls: false,
5917     align: false,
5918     bgcolor: false,
5919     border: false,
5920     cellpadding: false,
5921     cellspacing: false,
5922     frame: false,
5923     rules: false,
5924     sortable: false,
5925     summary: false,
5926     width: false,
5927     striped : false,
5928     scrollBody : false,
5929     bordered: false,
5930     hover:  false,
5931     condensed : false,
5932     responsive : false,
5933     sm : false,
5934     cm : false,
5935     store : false,
5936     loadMask : false,
5937     footerShow : true,
5938     headerShow : true,
5939   
5940     rowSelection : false,
5941     cellSelection : false,
5942     layout : false,
5943     
5944     // Roo.Element - the tbody
5945     mainBody: false,
5946     // Roo.Element - thead element
5947     mainHead: false,
5948     
5949     container: false, // used by gridpanel...
5950     
5951     getAutoCreate : function()
5952     {
5953         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5954         
5955         cfg = {
5956             tag: 'table',
5957             cls : 'table',
5958             cn : []
5959         };
5960         if (this.scrollBody) {
5961             cfg.cls += ' table-body-fixed';
5962         }    
5963         if (this.striped) {
5964             cfg.cls += ' table-striped';
5965         }
5966         
5967         if (this.hover) {
5968             cfg.cls += ' table-hover';
5969         }
5970         if (this.bordered) {
5971             cfg.cls += ' table-bordered';
5972         }
5973         if (this.condensed) {
5974             cfg.cls += ' table-condensed';
5975         }
5976         if (this.responsive) {
5977             cfg.cls += ' table-responsive';
5978         }
5979         
5980         if (this.cls) {
5981             cfg.cls+=  ' ' +this.cls;
5982         }
5983         
5984         // this lot should be simplifed...
5985         
5986         if (this.align) {
5987             cfg.align=this.align;
5988         }
5989         if (this.bgcolor) {
5990             cfg.bgcolor=this.bgcolor;
5991         }
5992         if (this.border) {
5993             cfg.border=this.border;
5994         }
5995         if (this.cellpadding) {
5996             cfg.cellpadding=this.cellpadding;
5997         }
5998         if (this.cellspacing) {
5999             cfg.cellspacing=this.cellspacing;
6000         }
6001         if (this.frame) {
6002             cfg.frame=this.frame;
6003         }
6004         if (this.rules) {
6005             cfg.rules=this.rules;
6006         }
6007         if (this.sortable) {
6008             cfg.sortable=this.sortable;
6009         }
6010         if (this.summary) {
6011             cfg.summary=this.summary;
6012         }
6013         if (this.width) {
6014             cfg.width=this.width;
6015         }
6016         if (this.layout) {
6017             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6018         }
6019         
6020         if(this.store || this.cm){
6021             if(this.headerShow){
6022                 cfg.cn.push(this.renderHeader());
6023             }
6024             
6025             cfg.cn.push(this.renderBody());
6026             
6027             if(this.footerShow){
6028                 cfg.cn.push(this.renderFooter());
6029             }
6030             // where does this come from?
6031             //cfg.cls+=  ' TableGrid';
6032         }
6033         
6034         return { cn : [ cfg ] };
6035     },
6036     
6037     initEvents : function()
6038     {   
6039         if(!this.store || !this.cm){
6040             return;
6041         }
6042         if (this.selModel) {
6043             this.selModel.initEvents();
6044         }
6045         
6046         
6047         //Roo.log('initEvents with ds!!!!');
6048         
6049         this.mainBody = this.el.select('tbody', true).first();
6050         this.mainHead = this.el.select('thead', true).first();
6051         
6052         
6053         
6054         
6055         var _this = this;
6056         
6057         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6058             e.on('click', _this.sort, _this);
6059         });
6060         
6061         this.mainBody.on("click", this.onClick, this);
6062         this.mainBody.on("dblclick", this.onDblClick, this);
6063         
6064         // why is this done????? = it breaks dialogs??
6065         //this.parent().el.setStyle('position', 'relative');
6066         
6067         
6068         if (this.footer) {
6069             this.footer.parentId = this.id;
6070             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6071         } 
6072         
6073         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6074         
6075         this.store.on('load', this.onLoad, this);
6076         this.store.on('beforeload', this.onBeforeLoad, this);
6077         this.store.on('update', this.onUpdate, this);
6078         this.store.on('add', this.onAdd, this);
6079         this.store.on("clear", this.clear, this);
6080         
6081         this.el.on("contextmenu", this.onContextMenu, this);
6082         
6083         this.mainBody.on('scroll', this.onBodyScroll, this);
6084         
6085         
6086     },
6087     
6088     onContextMenu : function(e, t)
6089     {
6090         this.processEvent("contextmenu", e);
6091     },
6092     
6093     processEvent : function(name, e)
6094     {
6095         if (name != 'touchstart' ) {
6096             this.fireEvent(name, e);    
6097         }
6098         
6099         var t = e.getTarget();
6100         
6101         var cell = Roo.get(t);
6102         
6103         if(!cell){
6104             return;
6105         }
6106         
6107         if(cell.findParent('tfoot', false, true)){
6108             return;
6109         }
6110         
6111         if(cell.findParent('thead', false, true)){
6112             
6113             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6114                 cell = Roo.get(t).findParent('th', false, true);
6115                 if (!cell) {
6116                     Roo.log("failed to find th in thead?");
6117                     Roo.log(e.getTarget());
6118                     return;
6119                 }
6120             }
6121             
6122             var cellIndex = cell.dom.cellIndex;
6123             
6124             var ename = name == 'touchstart' ? 'click' : name;
6125             this.fireEvent("header" + ename, this, cellIndex, e);
6126             
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = Roo.get(t).findParent('td', false, true);
6132             if (!cell) {
6133                 Roo.log("failed to find th in tbody?");
6134                 Roo.log(e.getTarget());
6135                 return;
6136             }
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1;
6142         
6143         if(row !== false){
6144             
6145             this.fireEvent("row" + name, this, rowIndex, e);
6146             
6147             if(cell !== false){
6148             
6149                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6150             }
6151         }
6152         
6153     },
6154     
6155     onMouseover : function(e, el)
6156     {
6157         var cell = Roo.get(el);
6158         
6159         if(!cell){
6160             return;
6161         }
6162         
6163         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6164             cell = cell.findParent('td', false, true);
6165         }
6166         
6167         var row = cell.findParent('tr', false, true);
6168         var cellIndex = cell.dom.cellIndex;
6169         var rowIndex = row.dom.rowIndex - 1; // start from 0
6170         
6171         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6172         
6173     },
6174     
6175     onMouseout : function(e, el)
6176     {
6177         var cell = Roo.get(el);
6178         
6179         if(!cell){
6180             return;
6181         }
6182         
6183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6184             cell = cell.findParent('td', false, true);
6185         }
6186         
6187         var row = cell.findParent('tr', false, true);
6188         var cellIndex = cell.dom.cellIndex;
6189         var rowIndex = row.dom.rowIndex - 1; // start from 0
6190         
6191         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6192         
6193     },
6194     
6195     onClick : function(e, el)
6196     {
6197         var cell = Roo.get(el);
6198         
6199         if(!cell || (!this.cellSelection && !this.rowSelection)){
6200             return;
6201         }
6202         
6203         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6204             cell = cell.findParent('td', false, true);
6205         }
6206         
6207         if(!cell || typeof(cell) == 'undefined'){
6208             return;
6209         }
6210         
6211         var row = cell.findParent('tr', false, true);
6212         
6213         if(!row || typeof(row) == 'undefined'){
6214             return;
6215         }
6216         
6217         var cellIndex = cell.dom.cellIndex;
6218         var rowIndex = this.getRowIndex(row);
6219         
6220         // why??? - should these not be based on SelectionModel?
6221         if(this.cellSelection){
6222             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6223         }
6224         
6225         if(this.rowSelection){
6226             this.fireEvent('rowclick', this, row, rowIndex, e);
6227         }
6228         
6229         
6230     },
6231         
6232     onDblClick : function(e,el)
6233     {
6234         var cell = Roo.get(el);
6235         
6236         if(!cell || (!this.cellSelection && !this.rowSelection)){
6237             return;
6238         }
6239         
6240         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6241             cell = cell.findParent('td', false, true);
6242         }
6243         
6244         if(!cell || typeof(cell) == 'undefined'){
6245             return;
6246         }
6247         
6248         var row = cell.findParent('tr', false, true);
6249         
6250         if(!row || typeof(row) == 'undefined'){
6251             return;
6252         }
6253         
6254         var cellIndex = cell.dom.cellIndex;
6255         var rowIndex = this.getRowIndex(row);
6256         
6257         if(this.cellSelection){
6258             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6259         }
6260         
6261         if(this.rowSelection){
6262             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6263         }
6264     },
6265     
6266     sort : function(e,el)
6267     {
6268         var col = Roo.get(el);
6269         
6270         if(!col.hasClass('sortable')){
6271             return;
6272         }
6273         
6274         var sort = col.attr('sort');
6275         var dir = 'ASC';
6276         
6277         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6278             dir = 'DESC';
6279         }
6280         
6281         this.store.sortInfo = {field : sort, direction : dir};
6282         
6283         if (this.footer) {
6284             Roo.log("calling footer first");
6285             this.footer.onClick('first');
6286         } else {
6287         
6288             this.store.load({ params : { start : 0 } });
6289         }
6290     },
6291     
6292     renderHeader : function()
6293     {
6294         var header = {
6295             tag: 'thead',
6296             cn : []
6297         };
6298         
6299         var cm = this.cm;
6300         this.totalWidth = 0;
6301         
6302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6303             
6304             var config = cm.config[i];
6305             
6306             var c = {
6307                 tag: 'th',
6308                 style : '',
6309                 html: cm.getColumnHeader(i)
6310             };
6311             
6312             var hh = '';
6313             
6314             if(typeof(config.sortable) != 'undefined' && config.sortable){
6315                 c.cls = 'sortable';
6316                 c.html = '<i class="glyphicon"></i>' + c.html;
6317             }
6318             
6319             if(typeof(config.lgHeader) != 'undefined'){
6320                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6321             }
6322             
6323             if(typeof(config.mdHeader) != 'undefined'){
6324                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6325             }
6326             
6327             if(typeof(config.smHeader) != 'undefined'){
6328                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6329             }
6330             
6331             if(typeof(config.xsHeader) != 'undefined'){
6332                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6333             }
6334             
6335             if(hh.length){
6336                 c.html = hh;
6337             }
6338             
6339             if(typeof(config.tooltip) != 'undefined'){
6340                 c.tooltip = config.tooltip;
6341             }
6342             
6343             if(typeof(config.colspan) != 'undefined'){
6344                 c.colspan = config.colspan;
6345             }
6346             
6347             if(typeof(config.hidden) != 'undefined' && config.hidden){
6348                 c.style += ' display:none;';
6349             }
6350             
6351             if(typeof(config.dataIndex) != 'undefined'){
6352                 c.sort = config.dataIndex;
6353             }
6354             
6355            
6356             
6357             if(typeof(config.align) != 'undefined' && config.align.length){
6358                 c.style += ' text-align:' + config.align + ';';
6359             }
6360             
6361             if(typeof(config.width) != 'undefined'){
6362                 c.style += ' width:' + config.width + 'px;';
6363                 this.totalWidth += config.width;
6364             } else {
6365                 this.totalWidth += 100; // assume minimum of 100 per column?
6366             }
6367             
6368             if(typeof(config.cls) != 'undefined'){
6369                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6370             }
6371             
6372             ['xs','sm','md','lg'].map(function(size){
6373                 
6374                 if(typeof(config[size]) == 'undefined'){
6375                     return;
6376                 }
6377                 
6378                 if (!config[size]) { // 0 = hidden
6379                     c.cls += ' hidden-' + size;
6380                     return;
6381                 }
6382                 
6383                 c.cls += ' col-' + size + '-' + config[size];
6384
6385             });
6386             
6387             header.cn.push(c)
6388         }
6389         
6390         return header;
6391     },
6392     
6393     renderBody : function()
6394     {
6395         var body = {
6396             tag: 'tbody',
6397             cn : [
6398                 {
6399                     tag: 'tr',
6400                     cn : [
6401                         {
6402                             tag : 'td',
6403                             colspan :  this.cm.getColumnCount()
6404                         }
6405                     ]
6406                 }
6407             ]
6408         };
6409         
6410         return body;
6411     },
6412     
6413     renderFooter : function()
6414     {
6415         var footer = {
6416             tag: 'tfoot',
6417             cn : [
6418                 {
6419                     tag: 'tr',
6420                     cn : [
6421                         {
6422                             tag : 'td',
6423                             colspan :  this.cm.getColumnCount()
6424                         }
6425                     ]
6426                 }
6427             ]
6428         };
6429         
6430         return footer;
6431     },
6432     
6433     
6434     
6435     onLoad : function()
6436     {
6437 //        Roo.log('ds onload');
6438         this.clear();
6439         
6440         var _this = this;
6441         var cm = this.cm;
6442         var ds = this.store;
6443         
6444         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6445             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6446             if (_this.store.sortInfo) {
6447                     
6448                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6449                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6450                 }
6451                 
6452                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6453                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6454                 }
6455             }
6456         });
6457         
6458         var tbody =  this.mainBody;
6459               
6460         if(ds.getCount() > 0){
6461             ds.data.each(function(d,rowIndex){
6462                 var row =  this.renderRow(cm, ds, rowIndex);
6463                 
6464                 tbody.createChild(row);
6465                 
6466                 var _this = this;
6467                 
6468                 if(row.cellObjects.length){
6469                     Roo.each(row.cellObjects, function(r){
6470                         _this.renderCellObject(r);
6471                     })
6472                 }
6473                 
6474             }, this);
6475         }
6476         
6477         Roo.each(this.el.select('tbody td', true).elements, function(e){
6478             e.on('mouseover', _this.onMouseover, _this);
6479         });
6480         
6481         Roo.each(this.el.select('tbody td', true).elements, function(e){
6482             e.on('mouseout', _this.onMouseout, _this);
6483         });
6484         this.fireEvent('rowsrendered', this);
6485         //if(this.loadMask){
6486         //    this.maskEl.hide();
6487         //}
6488         
6489         this.autoSize();
6490     },
6491     
6492     
6493     onUpdate : function(ds,record)
6494     {
6495         this.refreshRow(record);
6496         this.autoSize();
6497     },
6498     
6499     onRemove : function(ds, record, index, isUpdate){
6500         if(isUpdate !== true){
6501             this.fireEvent("beforerowremoved", this, index, record);
6502         }
6503         var bt = this.mainBody.dom;
6504         
6505         var rows = this.el.select('tbody > tr', true).elements;
6506         
6507         if(typeof(rows[index]) != 'undefined'){
6508             bt.removeChild(rows[index].dom);
6509         }
6510         
6511 //        if(bt.rows[index]){
6512 //            bt.removeChild(bt.rows[index]);
6513 //        }
6514         
6515         if(isUpdate !== true){
6516             //this.stripeRows(index);
6517             //this.syncRowHeights(index, index);
6518             //this.layout();
6519             this.fireEvent("rowremoved", this, index, record);
6520         }
6521     },
6522     
6523     onAdd : function(ds, records, rowIndex)
6524     {
6525         //Roo.log('on Add called');
6526         // - note this does not handle multiple adding very well..
6527         var bt = this.mainBody.dom;
6528         for (var i =0 ; i < records.length;i++) {
6529             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6530             //Roo.log(records[i]);
6531             //Roo.log(this.store.getAt(rowIndex+i));
6532             this.insertRow(this.store, rowIndex + i, false);
6533             return;
6534         }
6535         
6536     },
6537     
6538     
6539     refreshRow : function(record){
6540         var ds = this.store, index;
6541         if(typeof record == 'number'){
6542             index = record;
6543             record = ds.getAt(index);
6544         }else{
6545             index = ds.indexOf(record);
6546         }
6547         this.insertRow(ds, index, true);
6548         this.autoSize();
6549         this.onRemove(ds, record, index+1, true);
6550         this.autoSize();
6551         //this.syncRowHeights(index, index);
6552         //this.layout();
6553         this.fireEvent("rowupdated", this, index, record);
6554     },
6555     
6556     insertRow : function(dm, rowIndex, isUpdate){
6557         
6558         if(!isUpdate){
6559             this.fireEvent("beforerowsinserted", this, rowIndex);
6560         }
6561             //var s = this.getScrollState();
6562         var row = this.renderRow(this.cm, this.store, rowIndex);
6563         // insert before rowIndex..
6564         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6565         
6566         var _this = this;
6567                 
6568         if(row.cellObjects.length){
6569             Roo.each(row.cellObjects, function(r){
6570                 _this.renderCellObject(r);
6571             })
6572         }
6573             
6574         if(!isUpdate){
6575             this.fireEvent("rowsinserted", this, rowIndex);
6576             //this.syncRowHeights(firstRow, lastRow);
6577             //this.stripeRows(firstRow);
6578             //this.layout();
6579         }
6580         
6581     },
6582     
6583     
6584     getRowDom : function(rowIndex)
6585     {
6586         var rows = this.el.select('tbody > tr', true).elements;
6587         
6588         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6589         
6590     },
6591     // returns the object tree for a tr..
6592   
6593     
6594     renderRow : function(cm, ds, rowIndex) 
6595     {
6596         
6597         var d = ds.getAt(rowIndex);
6598         
6599         var row = {
6600             tag : 'tr',
6601             cn : []
6602         };
6603             
6604         var cellObjects = [];
6605         
6606         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6607             var config = cm.config[i];
6608             
6609             var renderer = cm.getRenderer(i);
6610             var value = '';
6611             var id = false;
6612             
6613             if(typeof(renderer) !== 'undefined'){
6614                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6615             }
6616             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6617             // and are rendered into the cells after the row is rendered - using the id for the element.
6618             
6619             if(typeof(value) === 'object'){
6620                 id = Roo.id();
6621                 cellObjects.push({
6622                     container : id,
6623                     cfg : value 
6624                 })
6625             }
6626             
6627             var rowcfg = {
6628                 record: d,
6629                 rowIndex : rowIndex,
6630                 colIndex : i,
6631                 rowClass : ''
6632             };
6633
6634             this.fireEvent('rowclass', this, rowcfg);
6635             
6636             var td = {
6637                 tag: 'td',
6638                 cls : rowcfg.rowClass,
6639                 style: '',
6640                 html: (typeof(value) === 'object') ? '' : value
6641             };
6642             
6643             if (id) {
6644                 td.id = id;
6645             }
6646             
6647             if(typeof(config.colspan) != 'undefined'){
6648                 td.colspan = config.colspan;
6649             }
6650             
6651             if(typeof(config.hidden) != 'undefined' && config.hidden){
6652                 td.style += ' display:none;';
6653             }
6654             
6655             if(typeof(config.align) != 'undefined' && config.align.length){
6656                 td.style += ' text-align:' + config.align + ';';
6657             }
6658             
6659             if(typeof(config.width) != 'undefined'){
6660                 td.style += ' width:' +  config.width + 'px;';
6661             }
6662             
6663             if(typeof(config.cursor) != 'undefined'){
6664                 td.style += ' cursor:' +  config.cursor + ';';
6665             }
6666             
6667             if(typeof(config.cls) != 'undefined'){
6668                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6669             }
6670             
6671             ['xs','sm','md','lg'].map(function(size){
6672                 
6673                 if(typeof(config[size]) == 'undefined'){
6674                     return;
6675                 }
6676                 
6677                 if (!config[size]) { // 0 = hidden
6678                     td.cls += ' hidden-' + size;
6679                     return;
6680                 }
6681                 
6682                 td.cls += ' col-' + size + '-' + config[size];
6683
6684             });
6685              
6686             row.cn.push(td);
6687            
6688         }
6689         
6690         row.cellObjects = cellObjects;
6691         
6692         return row;
6693           
6694     },
6695     
6696     
6697     
6698     onBeforeLoad : function()
6699     {
6700         //Roo.log('ds onBeforeLoad');
6701         
6702         //this.clear();
6703         
6704         //if(this.loadMask){
6705         //    this.maskEl.show();
6706         //}
6707     },
6708      /**
6709      * Remove all rows
6710      */
6711     clear : function()
6712     {
6713         this.el.select('tbody', true).first().dom.innerHTML = '';
6714     },
6715     /**
6716      * Show or hide a row.
6717      * @param {Number} rowIndex to show or hide
6718      * @param {Boolean} state hide
6719      */
6720     setRowVisibility : function(rowIndex, state)
6721     {
6722         var bt = this.mainBody.dom;
6723         
6724         var rows = this.el.select('tbody > tr', true).elements;
6725         
6726         if(typeof(rows[rowIndex]) == 'undefined'){
6727             return;
6728         }
6729         rows[rowIndex].dom.style.display = state ? '' : 'none';
6730     },
6731     
6732     
6733     getSelectionModel : function(){
6734         if(!this.selModel){
6735             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6736         }
6737         return this.selModel;
6738     },
6739     /*
6740      * Render the Roo.bootstrap object from renderder
6741      */
6742     renderCellObject : function(r)
6743     {
6744         var _this = this;
6745         
6746         var t = r.cfg.render(r.container);
6747         
6748         if(r.cfg.cn){
6749             Roo.each(r.cfg.cn, function(c){
6750                 var child = {
6751                     container: t.getChildContainer(),
6752                     cfg: c
6753                 };
6754                 _this.renderCellObject(child);
6755             })
6756         }
6757     },
6758     
6759     getRowIndex : function(row)
6760     {
6761         var rowIndex = -1;
6762         
6763         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6764             if(el != row){
6765                 return;
6766             }
6767             
6768             rowIndex = index;
6769         });
6770         
6771         return rowIndex;
6772     },
6773      /**
6774      * Returns the grid's underlying element = used by panel.Grid
6775      * @return {Element} The element
6776      */
6777     getGridEl : function(){
6778         return this.el;
6779     },
6780      /**
6781      * Forces a resize - used by panel.Grid
6782      * @return {Element} The element
6783      */
6784     autoSize : function()
6785     {
6786         //var ctr = Roo.get(this.container.dom.parentElement);
6787         var ctr = Roo.get(this.el.dom);
6788         
6789         var thd = this.getGridEl().select('thead',true).first();
6790         var tbd = this.getGridEl().select('tbody', true).first();
6791         var tfd = this.getGridEl().select('tfoot', true).first();
6792         
6793         var cw = ctr.getWidth();
6794         
6795         if (tbd) {
6796             
6797             tbd.setSize(ctr.getWidth(),
6798                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6799             );
6800             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6801             cw -= barsize;
6802         }
6803         cw = Math.max(cw, this.totalWidth);
6804         this.getGridEl().select('tr',true).setWidth(cw);
6805         // resize 'expandable coloumn?
6806         
6807         return; // we doe not have a view in this design..
6808         
6809     },
6810     onBodyScroll: function()
6811     {
6812         
6813         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6814         this.mainHead.setStyle({
6815                     'position' : 'relative',
6816                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6817         });
6818         
6819         
6820     }
6821 });
6822
6823  
6824
6825  /*
6826  * - LGPL
6827  *
6828  * table cell
6829  * 
6830  */
6831
6832 /**
6833  * @class Roo.bootstrap.TableCell
6834  * @extends Roo.bootstrap.Component
6835  * Bootstrap TableCell class
6836  * @cfg {String} html cell contain text
6837  * @cfg {String} cls cell class
6838  * @cfg {String} tag cell tag (td|th) default td
6839  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6840  * @cfg {String} align Aligns the content in a cell
6841  * @cfg {String} axis Categorizes cells
6842  * @cfg {String} bgcolor Specifies the background color of a cell
6843  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6844  * @cfg {Number} colspan Specifies the number of columns a cell should span
6845  * @cfg {String} headers Specifies one or more header cells a cell is related to
6846  * @cfg {Number} height Sets the height of a cell
6847  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6848  * @cfg {Number} rowspan Sets the number of rows a cell should span
6849  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6850  * @cfg {String} valign Vertical aligns the content in a cell
6851  * @cfg {Number} width Specifies the width of a cell
6852  * 
6853  * @constructor
6854  * Create a new TableCell
6855  * @param {Object} config The config object
6856  */
6857
6858 Roo.bootstrap.TableCell = function(config){
6859     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6860 };
6861
6862 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6863     
6864     html: false,
6865     cls: false,
6866     tag: false,
6867     abbr: false,
6868     align: false,
6869     axis: false,
6870     bgcolor: false,
6871     charoff: false,
6872     colspan: false,
6873     headers: false,
6874     height: false,
6875     nowrap: false,
6876     rowspan: false,
6877     scope: false,
6878     valign: false,
6879     width: false,
6880     
6881     
6882     getAutoCreate : function(){
6883         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6884         
6885         cfg = {
6886             tag: 'td'
6887         };
6888         
6889         if(this.tag){
6890             cfg.tag = this.tag;
6891         }
6892         
6893         if (this.html) {
6894             cfg.html=this.html
6895         }
6896         if (this.cls) {
6897             cfg.cls=this.cls
6898         }
6899         if (this.abbr) {
6900             cfg.abbr=this.abbr
6901         }
6902         if (this.align) {
6903             cfg.align=this.align
6904         }
6905         if (this.axis) {
6906             cfg.axis=this.axis
6907         }
6908         if (this.bgcolor) {
6909             cfg.bgcolor=this.bgcolor
6910         }
6911         if (this.charoff) {
6912             cfg.charoff=this.charoff
6913         }
6914         if (this.colspan) {
6915             cfg.colspan=this.colspan
6916         }
6917         if (this.headers) {
6918             cfg.headers=this.headers
6919         }
6920         if (this.height) {
6921             cfg.height=this.height
6922         }
6923         if (this.nowrap) {
6924             cfg.nowrap=this.nowrap
6925         }
6926         if (this.rowspan) {
6927             cfg.rowspan=this.rowspan
6928         }
6929         if (this.scope) {
6930             cfg.scope=this.scope
6931         }
6932         if (this.valign) {
6933             cfg.valign=this.valign
6934         }
6935         if (this.width) {
6936             cfg.width=this.width
6937         }
6938         
6939         
6940         return cfg;
6941     }
6942    
6943 });
6944
6945  
6946
6947  /*
6948  * - LGPL
6949  *
6950  * table row
6951  * 
6952  */
6953
6954 /**
6955  * @class Roo.bootstrap.TableRow
6956  * @extends Roo.bootstrap.Component
6957  * Bootstrap TableRow class
6958  * @cfg {String} cls row class
6959  * @cfg {String} align Aligns the content in a table row
6960  * @cfg {String} bgcolor Specifies a background color for a table row
6961  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6962  * @cfg {String} valign Vertical aligns the content in a table row
6963  * 
6964  * @constructor
6965  * Create a new TableRow
6966  * @param {Object} config The config object
6967  */
6968
6969 Roo.bootstrap.TableRow = function(config){
6970     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6971 };
6972
6973 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6974     
6975     cls: false,
6976     align: false,
6977     bgcolor: false,
6978     charoff: false,
6979     valign: false,
6980     
6981     getAutoCreate : function(){
6982         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6983         
6984         cfg = {
6985             tag: 'tr'
6986         };
6987             
6988         if(this.cls){
6989             cfg.cls = this.cls;
6990         }
6991         if(this.align){
6992             cfg.align = this.align;
6993         }
6994         if(this.bgcolor){
6995             cfg.bgcolor = this.bgcolor;
6996         }
6997         if(this.charoff){
6998             cfg.charoff = this.charoff;
6999         }
7000         if(this.valign){
7001             cfg.valign = this.valign;
7002         }
7003         
7004         return cfg;
7005     }
7006    
7007 });
7008
7009  
7010
7011  /*
7012  * - LGPL
7013  *
7014  * table body
7015  * 
7016  */
7017
7018 /**
7019  * @class Roo.bootstrap.TableBody
7020  * @extends Roo.bootstrap.Component
7021  * Bootstrap TableBody class
7022  * @cfg {String} cls element class
7023  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7024  * @cfg {String} align Aligns the content inside the element
7025  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7026  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7027  * 
7028  * @constructor
7029  * Create a new TableBody
7030  * @param {Object} config The config object
7031  */
7032
7033 Roo.bootstrap.TableBody = function(config){
7034     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7035 };
7036
7037 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7038     
7039     cls: false,
7040     tag: false,
7041     align: false,
7042     charoff: false,
7043     valign: false,
7044     
7045     getAutoCreate : function(){
7046         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7047         
7048         cfg = {
7049             tag: 'tbody'
7050         };
7051             
7052         if (this.cls) {
7053             cfg.cls=this.cls
7054         }
7055         if(this.tag){
7056             cfg.tag = this.tag;
7057         }
7058         
7059         if(this.align){
7060             cfg.align = this.align;
7061         }
7062         if(this.charoff){
7063             cfg.charoff = this.charoff;
7064         }
7065         if(this.valign){
7066             cfg.valign = this.valign;
7067         }
7068         
7069         return cfg;
7070     }
7071     
7072     
7073 //    initEvents : function()
7074 //    {
7075 //        
7076 //        if(!this.store){
7077 //            return;
7078 //        }
7079 //        
7080 //        this.store = Roo.factory(this.store, Roo.data);
7081 //        this.store.on('load', this.onLoad, this);
7082 //        
7083 //        this.store.load();
7084 //        
7085 //    },
7086 //    
7087 //    onLoad: function () 
7088 //    {   
7089 //        this.fireEvent('load', this);
7090 //    }
7091 //    
7092 //   
7093 });
7094
7095  
7096
7097  /*
7098  * Based on:
7099  * Ext JS Library 1.1.1
7100  * Copyright(c) 2006-2007, Ext JS, LLC.
7101  *
7102  * Originally Released Under LGPL - original licence link has changed is not relivant.
7103  *
7104  * Fork - LGPL
7105  * <script type="text/javascript">
7106  */
7107
7108 // as we use this in bootstrap.
7109 Roo.namespace('Roo.form');
7110  /**
7111  * @class Roo.form.Action
7112  * Internal Class used to handle form actions
7113  * @constructor
7114  * @param {Roo.form.BasicForm} el The form element or its id
7115  * @param {Object} config Configuration options
7116  */
7117
7118  
7119  
7120 // define the action interface
7121 Roo.form.Action = function(form, options){
7122     this.form = form;
7123     this.options = options || {};
7124 };
7125 /**
7126  * Client Validation Failed
7127  * @const 
7128  */
7129 Roo.form.Action.CLIENT_INVALID = 'client';
7130 /**
7131  * Server Validation Failed
7132  * @const 
7133  */
7134 Roo.form.Action.SERVER_INVALID = 'server';
7135  /**
7136  * Connect to Server Failed
7137  * @const 
7138  */
7139 Roo.form.Action.CONNECT_FAILURE = 'connect';
7140 /**
7141  * Reading Data from Server Failed
7142  * @const 
7143  */
7144 Roo.form.Action.LOAD_FAILURE = 'load';
7145
7146 Roo.form.Action.prototype = {
7147     type : 'default',
7148     failureType : undefined,
7149     response : undefined,
7150     result : undefined,
7151
7152     // interface method
7153     run : function(options){
7154
7155     },
7156
7157     // interface method
7158     success : function(response){
7159
7160     },
7161
7162     // interface method
7163     handleResponse : function(response){
7164
7165     },
7166
7167     // default connection failure
7168     failure : function(response){
7169         
7170         this.response = response;
7171         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7172         this.form.afterAction(this, false);
7173     },
7174
7175     processResponse : function(response){
7176         this.response = response;
7177         if(!response.responseText){
7178             return true;
7179         }
7180         this.result = this.handleResponse(response);
7181         return this.result;
7182     },
7183
7184     // utility functions used internally
7185     getUrl : function(appendParams){
7186         var url = this.options.url || this.form.url || this.form.el.dom.action;
7187         if(appendParams){
7188             var p = this.getParams();
7189             if(p){
7190                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7191             }
7192         }
7193         return url;
7194     },
7195
7196     getMethod : function(){
7197         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7198     },
7199
7200     getParams : function(){
7201         var bp = this.form.baseParams;
7202         var p = this.options.params;
7203         if(p){
7204             if(typeof p == "object"){
7205                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7206             }else if(typeof p == 'string' && bp){
7207                 p += '&' + Roo.urlEncode(bp);
7208             }
7209         }else if(bp){
7210             p = Roo.urlEncode(bp);
7211         }
7212         return p;
7213     },
7214
7215     createCallback : function(){
7216         return {
7217             success: this.success,
7218             failure: this.failure,
7219             scope: this,
7220             timeout: (this.form.timeout*1000),
7221             upload: this.form.fileUpload ? this.success : undefined
7222         };
7223     }
7224 };
7225
7226 Roo.form.Action.Submit = function(form, options){
7227     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7228 };
7229
7230 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7231     type : 'submit',
7232
7233     haveProgress : false,
7234     uploadComplete : false,
7235     
7236     // uploadProgress indicator.
7237     uploadProgress : function()
7238     {
7239         if (!this.form.progressUrl) {
7240             return;
7241         }
7242         
7243         if (!this.haveProgress) {
7244             Roo.MessageBox.progress("Uploading", "Uploading");
7245         }
7246         if (this.uploadComplete) {
7247            Roo.MessageBox.hide();
7248            return;
7249         }
7250         
7251         this.haveProgress = true;
7252    
7253         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7254         
7255         var c = new Roo.data.Connection();
7256         c.request({
7257             url : this.form.progressUrl,
7258             params: {
7259                 id : uid
7260             },
7261             method: 'GET',
7262             success : function(req){
7263                //console.log(data);
7264                 var rdata = false;
7265                 var edata;
7266                 try  {
7267                    rdata = Roo.decode(req.responseText)
7268                 } catch (e) {
7269                     Roo.log("Invalid data from server..");
7270                     Roo.log(edata);
7271                     return;
7272                 }
7273                 if (!rdata || !rdata.success) {
7274                     Roo.log(rdata);
7275                     Roo.MessageBox.alert(Roo.encode(rdata));
7276                     return;
7277                 }
7278                 var data = rdata.data;
7279                 
7280                 if (this.uploadComplete) {
7281                    Roo.MessageBox.hide();
7282                    return;
7283                 }
7284                    
7285                 if (data){
7286                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7287                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7288                     );
7289                 }
7290                 this.uploadProgress.defer(2000,this);
7291             },
7292        
7293             failure: function(data) {
7294                 Roo.log('progress url failed ');
7295                 Roo.log(data);
7296             },
7297             scope : this
7298         });
7299            
7300     },
7301     
7302     
7303     run : function()
7304     {
7305         // run get Values on the form, so it syncs any secondary forms.
7306         this.form.getValues();
7307         
7308         var o = this.options;
7309         var method = this.getMethod();
7310         var isPost = method == 'POST';
7311         if(o.clientValidation === false || this.form.isValid()){
7312             
7313             if (this.form.progressUrl) {
7314                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7315                     (new Date() * 1) + '' + Math.random());
7316                     
7317             } 
7318             
7319             
7320             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7321                 form:this.form.el.dom,
7322                 url:this.getUrl(!isPost),
7323                 method: method,
7324                 params:isPost ? this.getParams() : null,
7325                 isUpload: this.form.fileUpload
7326             }));
7327             
7328             this.uploadProgress();
7329
7330         }else if (o.clientValidation !== false){ // client validation failed
7331             this.failureType = Roo.form.Action.CLIENT_INVALID;
7332             this.form.afterAction(this, false);
7333         }
7334     },
7335
7336     success : function(response)
7337     {
7338         this.uploadComplete= true;
7339         if (this.haveProgress) {
7340             Roo.MessageBox.hide();
7341         }
7342         
7343         
7344         var result = this.processResponse(response);
7345         if(result === true || result.success){
7346             this.form.afterAction(this, true);
7347             return;
7348         }
7349         if(result.errors){
7350             this.form.markInvalid(result.errors);
7351             this.failureType = Roo.form.Action.SERVER_INVALID;
7352         }
7353         this.form.afterAction(this, false);
7354     },
7355     failure : function(response)
7356     {
7357         this.uploadComplete= true;
7358         if (this.haveProgress) {
7359             Roo.MessageBox.hide();
7360         }
7361         
7362         this.response = response;
7363         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7364         this.form.afterAction(this, false);
7365     },
7366     
7367     handleResponse : function(response){
7368         if(this.form.errorReader){
7369             var rs = this.form.errorReader.read(response);
7370             var errors = [];
7371             if(rs.records){
7372                 for(var i = 0, len = rs.records.length; i < len; i++) {
7373                     var r = rs.records[i];
7374                     errors[i] = r.data;
7375                 }
7376             }
7377             if(errors.length < 1){
7378                 errors = null;
7379             }
7380             return {
7381                 success : rs.success,
7382                 errors : errors
7383             };
7384         }
7385         var ret = false;
7386         try {
7387             ret = Roo.decode(response.responseText);
7388         } catch (e) {
7389             ret = {
7390                 success: false,
7391                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7392                 errors : []
7393             };
7394         }
7395         return ret;
7396         
7397     }
7398 });
7399
7400
7401 Roo.form.Action.Load = function(form, options){
7402     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7403     this.reader = this.form.reader;
7404 };
7405
7406 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7407     type : 'load',
7408
7409     run : function(){
7410         
7411         Roo.Ajax.request(Roo.apply(
7412                 this.createCallback(), {
7413                     method:this.getMethod(),
7414                     url:this.getUrl(false),
7415                     params:this.getParams()
7416         }));
7417     },
7418
7419     success : function(response){
7420         
7421         var result = this.processResponse(response);
7422         if(result === true || !result.success || !result.data){
7423             this.failureType = Roo.form.Action.LOAD_FAILURE;
7424             this.form.afterAction(this, false);
7425             return;
7426         }
7427         this.form.clearInvalid();
7428         this.form.setValues(result.data);
7429         this.form.afterAction(this, true);
7430     },
7431
7432     handleResponse : function(response){
7433         if(this.form.reader){
7434             var rs = this.form.reader.read(response);
7435             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7436             return {
7437                 success : rs.success,
7438                 data : data
7439             };
7440         }
7441         return Roo.decode(response.responseText);
7442     }
7443 });
7444
7445 Roo.form.Action.ACTION_TYPES = {
7446     'load' : Roo.form.Action.Load,
7447     'submit' : Roo.form.Action.Submit
7448 };/*
7449  * - LGPL
7450  *
7451  * form
7452  *
7453  */
7454
7455 /**
7456  * @class Roo.bootstrap.Form
7457  * @extends Roo.bootstrap.Component
7458  * Bootstrap Form class
7459  * @cfg {String} method  GET | POST (default POST)
7460  * @cfg {String} labelAlign top | left (default top)
7461  * @cfg {String} align left  | right - for navbars
7462  * @cfg {Boolean} loadMask load mask when submit (default true)
7463
7464  *
7465  * @constructor
7466  * Create a new Form
7467  * @param {Object} config The config object
7468  */
7469
7470
7471 Roo.bootstrap.Form = function(config){
7472     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7473     
7474     Roo.bootstrap.Form.popover.apply();
7475     
7476     this.addEvents({
7477         /**
7478          * @event clientvalidation
7479          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7480          * @param {Form} this
7481          * @param {Boolean} valid true if the form has passed client-side validation
7482          */
7483         clientvalidation: true,
7484         /**
7485          * @event beforeaction
7486          * Fires before any action is performed. Return false to cancel the action.
7487          * @param {Form} this
7488          * @param {Action} action The action to be performed
7489          */
7490         beforeaction: true,
7491         /**
7492          * @event actionfailed
7493          * Fires when an action fails.
7494          * @param {Form} this
7495          * @param {Action} action The action that failed
7496          */
7497         actionfailed : true,
7498         /**
7499          * @event actioncomplete
7500          * Fires when an action is completed.
7501          * @param {Form} this
7502          * @param {Action} action The action that completed
7503          */
7504         actioncomplete : true
7505     });
7506
7507 };
7508
7509 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7510
7511      /**
7512      * @cfg {String} method
7513      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7514      */
7515     method : 'POST',
7516     /**
7517      * @cfg {String} url
7518      * The URL to use for form actions if one isn't supplied in the action options.
7519      */
7520     /**
7521      * @cfg {Boolean} fileUpload
7522      * Set to true if this form is a file upload.
7523      */
7524
7525     /**
7526      * @cfg {Object} baseParams
7527      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7528      */
7529
7530     /**
7531      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7532      */
7533     timeout: 30,
7534     /**
7535      * @cfg {Sting} align (left|right) for navbar forms
7536      */
7537     align : 'left',
7538
7539     // private
7540     activeAction : null,
7541
7542     /**
7543      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7544      * element by passing it or its id or mask the form itself by passing in true.
7545      * @type Mixed
7546      */
7547     waitMsgTarget : false,
7548
7549     loadMask : true,
7550     
7551     /**
7552      * @cfg {Boolean} errorMask (true|false) default false
7553      */
7554     errorMask : false,
7555
7556     getAutoCreate : function(){
7557
7558         var cfg = {
7559             tag: 'form',
7560             method : this.method || 'POST',
7561             id : this.id || Roo.id(),
7562             cls : ''
7563         };
7564         if (this.parent().xtype.match(/^Nav/)) {
7565             cfg.cls = 'navbar-form navbar-' + this.align;
7566
7567         }
7568
7569         if (this.labelAlign == 'left' ) {
7570             cfg.cls += ' form-horizontal';
7571         }
7572
7573
7574         return cfg;
7575     },
7576     initEvents : function()
7577     {
7578         this.el.on('submit', this.onSubmit, this);
7579         // this was added as random key presses on the form where triggering form submit.
7580         this.el.on('keypress', function(e) {
7581             if (e.getCharCode() != 13) {
7582                 return true;
7583             }
7584             // we might need to allow it for textareas.. and some other items.
7585             // check e.getTarget().
7586
7587             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7588                 return true;
7589             }
7590
7591             Roo.log("keypress blocked");
7592
7593             e.preventDefault();
7594             return false;
7595         });
7596         
7597     },
7598     // private
7599     onSubmit : function(e){
7600         e.stopEvent();
7601     },
7602
7603      /**
7604      * Returns true if client-side validation on the form is successful.
7605      * @return Boolean
7606      */
7607     isValid : function(){
7608         var items = this.getItems();
7609         var valid = true;
7610         var target = false;
7611         
7612         items.each(function(f){
7613             
7614             if(f.validate()){
7615                 return;
7616             }
7617             valid = false;
7618
7619             if(!target && f.el.isVisible(true)){
7620                 target = f;
7621             }
7622            
7623         });
7624         
7625         if(this.errorMask && !valid){
7626             Roo.bootstrap.Form.popover.mask(this, target);
7627         }
7628         
7629         return valid;
7630     },
7631     
7632     /**
7633      * Returns true if any fields in this form have changed since their original load.
7634      * @return Boolean
7635      */
7636     isDirty : function(){
7637         var dirty = false;
7638         var items = this.getItems();
7639         items.each(function(f){
7640            if(f.isDirty()){
7641                dirty = true;
7642                return false;
7643            }
7644            return true;
7645         });
7646         return dirty;
7647     },
7648      /**
7649      * Performs a predefined action (submit or load) or custom actions you define on this form.
7650      * @param {String} actionName The name of the action type
7651      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7652      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7653      * accept other config options):
7654      * <pre>
7655 Property          Type             Description
7656 ----------------  ---------------  ----------------------------------------------------------------------------------
7657 url               String           The url for the action (defaults to the form's url)
7658 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7659 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7660 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7661                                    validate the form on the client (defaults to false)
7662      * </pre>
7663      * @return {BasicForm} this
7664      */
7665     doAction : function(action, options){
7666         if(typeof action == 'string'){
7667             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7668         }
7669         if(this.fireEvent('beforeaction', this, action) !== false){
7670             this.beforeAction(action);
7671             action.run.defer(100, action);
7672         }
7673         return this;
7674     },
7675
7676     // private
7677     beforeAction : function(action){
7678         var o = action.options;
7679
7680         if(this.loadMask){
7681             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7682         }
7683         // not really supported yet.. ??
7684
7685         //if(this.waitMsgTarget === true){
7686         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7687         //}else if(this.waitMsgTarget){
7688         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7689         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7690         //}else {
7691         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7692        // }
7693
7694     },
7695
7696     // private
7697     afterAction : function(action, success){
7698         this.activeAction = null;
7699         var o = action.options;
7700
7701         //if(this.waitMsgTarget === true){
7702             this.el.unmask();
7703         //}else if(this.waitMsgTarget){
7704         //    this.waitMsgTarget.unmask();
7705         //}else{
7706         //    Roo.MessageBox.updateProgress(1);
7707         //    Roo.MessageBox.hide();
7708        // }
7709         //
7710         if(success){
7711             if(o.reset){
7712                 this.reset();
7713             }
7714             Roo.callback(o.success, o.scope, [this, action]);
7715             this.fireEvent('actioncomplete', this, action);
7716
7717         }else{
7718
7719             // failure condition..
7720             // we have a scenario where updates need confirming.
7721             // eg. if a locking scenario exists..
7722             // we look for { errors : { needs_confirm : true }} in the response.
7723             if (
7724                 (typeof(action.result) != 'undefined')  &&
7725                 (typeof(action.result.errors) != 'undefined')  &&
7726                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7727            ){
7728                 var _t = this;
7729                 Roo.log("not supported yet");
7730                  /*
7731
7732                 Roo.MessageBox.confirm(
7733                     "Change requires confirmation",
7734                     action.result.errorMsg,
7735                     function(r) {
7736                         if (r != 'yes') {
7737                             return;
7738                         }
7739                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7740                     }
7741
7742                 );
7743                 */
7744
7745
7746                 return;
7747             }
7748
7749             Roo.callback(o.failure, o.scope, [this, action]);
7750             // show an error message if no failed handler is set..
7751             if (!this.hasListener('actionfailed')) {
7752                 Roo.log("need to add dialog support");
7753                 /*
7754                 Roo.MessageBox.alert("Error",
7755                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7756                         action.result.errorMsg :
7757                         "Saving Failed, please check your entries or try again"
7758                 );
7759                 */
7760             }
7761
7762             this.fireEvent('actionfailed', this, action);
7763         }
7764
7765     },
7766     /**
7767      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7768      * @param {String} id The value to search for
7769      * @return Field
7770      */
7771     findField : function(id){
7772         var items = this.getItems();
7773         var field = items.get(id);
7774         if(!field){
7775              items.each(function(f){
7776                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7777                     field = f;
7778                     return false;
7779                 }
7780                 return true;
7781             });
7782         }
7783         return field || null;
7784     },
7785      /**
7786      * Mark fields in this form invalid in bulk.
7787      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7788      * @return {BasicForm} this
7789      */
7790     markInvalid : function(errors){
7791         if(errors instanceof Array){
7792             for(var i = 0, len = errors.length; i < len; i++){
7793                 var fieldError = errors[i];
7794                 var f = this.findField(fieldError.id);
7795                 if(f){
7796                     f.markInvalid(fieldError.msg);
7797                 }
7798             }
7799         }else{
7800             var field, id;
7801             for(id in errors){
7802                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7803                     field.markInvalid(errors[id]);
7804                 }
7805             }
7806         }
7807         //Roo.each(this.childForms || [], function (f) {
7808         //    f.markInvalid(errors);
7809         //});
7810
7811         return this;
7812     },
7813
7814     /**
7815      * Set values for fields in this form in bulk.
7816      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7817      * @return {BasicForm} this
7818      */
7819     setValues : function(values){
7820         if(values instanceof Array){ // array of objects
7821             for(var i = 0, len = values.length; i < len; i++){
7822                 var v = values[i];
7823                 var f = this.findField(v.id);
7824                 if(f){
7825                     f.setValue(v.value);
7826                     if(this.trackResetOnLoad){
7827                         f.originalValue = f.getValue();
7828                     }
7829                 }
7830             }
7831         }else{ // object hash
7832             var field, id;
7833             for(id in values){
7834                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7835
7836                     if (field.setFromData &&
7837                         field.valueField &&
7838                         field.displayField &&
7839                         // combos' with local stores can
7840                         // be queried via setValue()
7841                         // to set their value..
7842                         (field.store && !field.store.isLocal)
7843                         ) {
7844                         // it's a combo
7845                         var sd = { };
7846                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7847                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7848                         field.setFromData(sd);
7849
7850                     } else {
7851                         field.setValue(values[id]);
7852                     }
7853
7854
7855                     if(this.trackResetOnLoad){
7856                         field.originalValue = field.getValue();
7857                     }
7858                 }
7859             }
7860         }
7861
7862         //Roo.each(this.childForms || [], function (f) {
7863         //    f.setValues(values);
7864         //});
7865
7866         return this;
7867     },
7868
7869     /**
7870      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7871      * they are returned as an array.
7872      * @param {Boolean} asString
7873      * @return {Object}
7874      */
7875     getValues : function(asString){
7876         //if (this.childForms) {
7877             // copy values from the child forms
7878         //    Roo.each(this.childForms, function (f) {
7879         //        this.setValues(f.getValues());
7880         //    }, this);
7881         //}
7882
7883
7884
7885         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7886         if(asString === true){
7887             return fs;
7888         }
7889         return Roo.urlDecode(fs);
7890     },
7891
7892     /**
7893      * Returns the fields in this form as an object with key/value pairs.
7894      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7895      * @return {Object}
7896      */
7897     getFieldValues : function(with_hidden)
7898     {
7899         var items = this.getItems();
7900         var ret = {};
7901         items.each(function(f){
7902             if (!f.getName()) {
7903                 return;
7904             }
7905             var v = f.getValue();
7906             if (f.inputType =='radio') {
7907                 if (typeof(ret[f.getName()]) == 'undefined') {
7908                     ret[f.getName()] = ''; // empty..
7909                 }
7910
7911                 if (!f.el.dom.checked) {
7912                     return;
7913
7914                 }
7915                 v = f.el.dom.value;
7916
7917             }
7918
7919             // not sure if this supported any more..
7920             if ((typeof(v) == 'object') && f.getRawValue) {
7921                 v = f.getRawValue() ; // dates..
7922             }
7923             // combo boxes where name != hiddenName...
7924             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7925                 ret[f.name] = f.getRawValue();
7926             }
7927             ret[f.getName()] = v;
7928         });
7929
7930         return ret;
7931     },
7932
7933     /**
7934      * Clears all invalid messages in this form.
7935      * @return {BasicForm} this
7936      */
7937     clearInvalid : function(){
7938         var items = this.getItems();
7939
7940         items.each(function(f){
7941            f.clearInvalid();
7942         });
7943
7944
7945
7946         return this;
7947     },
7948
7949     /**
7950      * Resets this form.
7951      * @return {BasicForm} this
7952      */
7953     reset : function(){
7954         var items = this.getItems();
7955         items.each(function(f){
7956             f.reset();
7957         });
7958
7959         Roo.each(this.childForms || [], function (f) {
7960             f.reset();
7961         });
7962
7963
7964         return this;
7965     },
7966     getItems : function()
7967     {
7968         var r=new Roo.util.MixedCollection(false, function(o){
7969             return o.id || (o.id = Roo.id());
7970         });
7971         var iter = function(el) {
7972             if (el.inputEl) {
7973                 r.add(el);
7974             }
7975             if (!el.items) {
7976                 return;
7977             }
7978             Roo.each(el.items,function(e) {
7979                 iter(e);
7980             });
7981
7982
7983         };
7984
7985         iter(this);
7986         return r;
7987
7988
7989
7990
7991     }
7992
7993 });
7994
7995 Roo.apply(Roo.bootstrap.Form, {
7996     
7997     popover : {
7998         
7999         padding : 5,
8000         
8001         isApplied : false,
8002         
8003         isMasked : false,
8004         
8005         form : false,
8006         
8007         target : false,
8008         
8009         toolTip : false,
8010         
8011         intervalID : false,
8012         
8013         maskEl : false,
8014         
8015         apply : function()
8016         {
8017             if(this.isApplied){
8018                 return;
8019             }
8020             
8021             this.maskEl = {
8022                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8023                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8024                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8025                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8026             };
8027             
8028             this.maskEl.top.enableDisplayMode("block");
8029             this.maskEl.left.enableDisplayMode("block");
8030             this.maskEl.bottom.enableDisplayMode("block");
8031             this.maskEl.right.enableDisplayMode("block");
8032             
8033             this.toolTip = new Roo.bootstrap.Tooltip({
8034                 cls : 'roo-form-error-popover',
8035                 alignment : {
8036                     'left' : ['r-l', [-2,0], 'right'],
8037                     'right' : ['l-r', [2,0], 'left'],
8038                     'bottom' : ['tl-bl', [0,2], 'top'],
8039                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8040                 }
8041             });
8042             
8043             this.toolTip.render(Roo.get(document.body));
8044
8045             this.toolTip.el.enableDisplayMode("block");
8046             
8047             Roo.get(document.body).on('click', function(){
8048                 this.unmask();
8049             }, this);
8050             
8051             this.isApplied = true
8052         },
8053         
8054         mask : function(form, target)
8055         {
8056             this.form = form;
8057             
8058             this.target = target;
8059             
8060             if(!this.form.errorMask || !target.el){
8061                 return;
8062             }
8063             
8064             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8065             
8066             var scrolled = scrollable.getScroll();
8067             
8068             var ot = this.target.el.calcOffsetsTo(scrollable);
8069             
8070             scrollTo = ot[1] - 100;
8071             
8072             scrollable.scrollTo('top', scrollTo);
8073             
8074             var box = this.target.el.getBox();
8075
8076             var zIndex = Roo.bootstrap.Modal.zIndex++;
8077
8078             this.maskEl.top.setStyle('position', 'fixed');
8079             this.maskEl.top.setStyle('z-index', zIndex);
8080             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8081             this.maskEl.top.setXY([0, 0]);
8082             this.maskEl.top.show();
8083
8084             this.maskEl.left.setStyle('position', 'fixed');
8085             this.maskEl.left.setStyle('z-index', zIndex);
8086             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8087             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8088             this.maskEl.left.show();
8089
8090             this.maskEl.bottom.setStyle('position', 'fixed');
8091             this.maskEl.bottom.setStyle('z-index', zIndex);
8092             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8093             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8094             this.maskEl.bottom.show();
8095
8096             this.maskEl.right.setStyle('position', 'fixed');
8097             this.maskEl.right.setStyle('z-index', zIndex);
8098             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8099             this.maskEl.right.setXY([0, box.y - this.padding]);
8100             this.maskEl.right.show();
8101
8102
8103             this.toolTip.bindEl = this.target.el;
8104
8105             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8106
8107             var tip = this.target.blankText;
8108
8109             if(this.target.getValue() !== '' && this.target.regexText.length){
8110                 tip = this.target.regexText;
8111             }
8112
8113             this.toolTip.show(tip);
8114
8115             this.intervalID = window.setInterval(function() {
8116                 Roo.bootstrap.Form.popover.unmask();
8117             }, 10000);
8118
8119             window.onwheel = function(){ return false;};
8120             
8121             (function(){ this.isMasked = true; }).defer(500, this);
8122                 
8123             
8124             
8125         },
8126         
8127         unmask : function()
8128         {
8129             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8130                 return;
8131             }
8132             
8133             this.maskEl.top.setStyle('position', 'absolute');
8134             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8135             this.maskEl.top.hide();
8136
8137             this.maskEl.left.setStyle('position', 'absolute');
8138             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8139             this.maskEl.left.hide();
8140
8141             this.maskEl.bottom.setStyle('position', 'absolute');
8142             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8143             this.maskEl.bottom.hide();
8144
8145             this.maskEl.right.setStyle('position', 'absolute');
8146             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8147             this.maskEl.right.hide();
8148             
8149             this.toolTip.hide();
8150             
8151             this.toolTip.el.hide();
8152             
8153             window.onwheel = function(){ return true;};
8154             
8155             if(this.intervalID){
8156                 window.clearInterval(this.intervalID);
8157                 this.intervalID = false;
8158             }
8159             
8160             this.isMasked = false;
8161             
8162         }
8163         
8164     }
8165     
8166 });
8167
8168 /*
8169  * Based on:
8170  * Ext JS Library 1.1.1
8171  * Copyright(c) 2006-2007, Ext JS, LLC.
8172  *
8173  * Originally Released Under LGPL - original licence link has changed is not relivant.
8174  *
8175  * Fork - LGPL
8176  * <script type="text/javascript">
8177  */
8178 /**
8179  * @class Roo.form.VTypes
8180  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8181  * @singleton
8182  */
8183 Roo.form.VTypes = function(){
8184     // closure these in so they are only created once.
8185     var alpha = /^[a-zA-Z_]+$/;
8186     var alphanum = /^[a-zA-Z0-9_]+$/;
8187     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8188     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8189
8190     // All these messages and functions are configurable
8191     return {
8192         /**
8193          * The function used to validate email addresses
8194          * @param {String} value The email address
8195          */
8196         'email' : function(v){
8197             return email.test(v);
8198         },
8199         /**
8200          * The error text to display when the email validation function returns false
8201          * @type String
8202          */
8203         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8204         /**
8205          * The keystroke filter mask to be applied on email input
8206          * @type RegExp
8207          */
8208         'emailMask' : /[a-z0-9_\.\-@]/i,
8209
8210         /**
8211          * The function used to validate URLs
8212          * @param {String} value The URL
8213          */
8214         'url' : function(v){
8215             return url.test(v);
8216         },
8217         /**
8218          * The error text to display when the url validation function returns false
8219          * @type String
8220          */
8221         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8222         
8223         /**
8224          * The function used to validate alpha values
8225          * @param {String} value The value
8226          */
8227         'alpha' : function(v){
8228             return alpha.test(v);
8229         },
8230         /**
8231          * The error text to display when the alpha validation function returns false
8232          * @type String
8233          */
8234         'alphaText' : 'This field should only contain letters and _',
8235         /**
8236          * The keystroke filter mask to be applied on alpha input
8237          * @type RegExp
8238          */
8239         'alphaMask' : /[a-z_]/i,
8240
8241         /**
8242          * The function used to validate alphanumeric values
8243          * @param {String} value The value
8244          */
8245         'alphanum' : function(v){
8246             return alphanum.test(v);
8247         },
8248         /**
8249          * The error text to display when the alphanumeric validation function returns false
8250          * @type String
8251          */
8252         'alphanumText' : 'This field should only contain letters, numbers and _',
8253         /**
8254          * The keystroke filter mask to be applied on alphanumeric input
8255          * @type RegExp
8256          */
8257         'alphanumMask' : /[a-z0-9_]/i
8258     };
8259 }();/*
8260  * - LGPL
8261  *
8262  * Input
8263  * 
8264  */
8265
8266 /**
8267  * @class Roo.bootstrap.Input
8268  * @extends Roo.bootstrap.Component
8269  * Bootstrap Input class
8270  * @cfg {Boolean} disabled is it disabled
8271  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8272  * @cfg {String} name name of the input
8273  * @cfg {string} fieldLabel - the label associated
8274  * @cfg {string} placeholder - placeholder to put in text.
8275  * @cfg {string}  before - input group add on before
8276  * @cfg {string} after - input group add on after
8277  * @cfg {string} size - (lg|sm) or leave empty..
8278  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8279  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8280  * @cfg {Number} md colspan out of 12 for computer-sized screens
8281  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8282  * @cfg {string} value default value of the input
8283  * @cfg {Number} labelWidth set the width of label 
8284  * @cfg {Number} labellg set the width of label (1-12)
8285  * @cfg {Number} labelmd set the width of label (1-12)
8286  * @cfg {Number} labelsm set the width of label (1-12)
8287  * @cfg {Number} labelxs set the width of label (1-12)
8288  * @cfg {String} labelAlign (top|left)
8289  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8290  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8291  * @cfg {String} indicatorpos (left|right) default left
8292
8293  * @cfg {String} align (left|center|right) Default left
8294  * @cfg {Boolean} forceFeedback (true|false) Default false
8295  * 
8296  * 
8297  * 
8298  * 
8299  * @constructor
8300  * Create a new Input
8301  * @param {Object} config The config object
8302  */
8303
8304 Roo.bootstrap.Input = function(config){
8305     
8306     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8307     
8308     this.addEvents({
8309         /**
8310          * @event focus
8311          * Fires when this field receives input focus.
8312          * @param {Roo.form.Field} this
8313          */
8314         focus : true,
8315         /**
8316          * @event blur
8317          * Fires when this field loses input focus.
8318          * @param {Roo.form.Field} this
8319          */
8320         blur : true,
8321         /**
8322          * @event specialkey
8323          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8324          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8325          * @param {Roo.form.Field} this
8326          * @param {Roo.EventObject} e The event object
8327          */
8328         specialkey : true,
8329         /**
8330          * @event change
8331          * Fires just before the field blurs if the field value has changed.
8332          * @param {Roo.form.Field} this
8333          * @param {Mixed} newValue The new value
8334          * @param {Mixed} oldValue The original value
8335          */
8336         change : true,
8337         /**
8338          * @event invalid
8339          * Fires after the field has been marked as invalid.
8340          * @param {Roo.form.Field} this
8341          * @param {String} msg The validation message
8342          */
8343         invalid : true,
8344         /**
8345          * @event valid
8346          * Fires after the field has been validated with no errors.
8347          * @param {Roo.form.Field} this
8348          */
8349         valid : true,
8350          /**
8351          * @event keyup
8352          * Fires after the key up
8353          * @param {Roo.form.Field} this
8354          * @param {Roo.EventObject}  e The event Object
8355          */
8356         keyup : true
8357     });
8358 };
8359
8360 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8361      /**
8362      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8363       automatic validation (defaults to "keyup").
8364      */
8365     validationEvent : "keyup",
8366      /**
8367      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8368      */
8369     validateOnBlur : true,
8370     /**
8371      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8372      */
8373     validationDelay : 250,
8374      /**
8375      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8376      */
8377     focusClass : "x-form-focus",  // not needed???
8378     
8379        
8380     /**
8381      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8382      */
8383     invalidClass : "has-warning",
8384     
8385     /**
8386      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8387      */
8388     validClass : "has-success",
8389     
8390     /**
8391      * @cfg {Boolean} hasFeedback (true|false) default true
8392      */
8393     hasFeedback : true,
8394     
8395     /**
8396      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8397      */
8398     invalidFeedbackClass : "glyphicon-warning-sign",
8399     
8400     /**
8401      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8402      */
8403     validFeedbackClass : "glyphicon-ok",
8404     
8405     /**
8406      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8407      */
8408     selectOnFocus : false,
8409     
8410      /**
8411      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8412      */
8413     maskRe : null,
8414        /**
8415      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8416      */
8417     vtype : null,
8418     
8419       /**
8420      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8421      */
8422     disableKeyFilter : false,
8423     
8424        /**
8425      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8426      */
8427     disabled : false,
8428      /**
8429      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8430      */
8431     allowBlank : true,
8432     /**
8433      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8434      */
8435     blankText : "Please complete this mandatory field",
8436     
8437      /**
8438      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8439      */
8440     minLength : 0,
8441     /**
8442      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8443      */
8444     maxLength : Number.MAX_VALUE,
8445     /**
8446      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8447      */
8448     minLengthText : "The minimum length for this field is {0}",
8449     /**
8450      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8451      */
8452     maxLengthText : "The maximum length for this field is {0}",
8453   
8454     
8455     /**
8456      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8457      * If available, this function will be called only after the basic validators all return true, and will be passed the
8458      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8459      */
8460     validator : null,
8461     /**
8462      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8463      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8464      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8465      */
8466     regex : null,
8467     /**
8468      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8469      */
8470     regexText : "",
8471     
8472     autocomplete: false,
8473     
8474     
8475     fieldLabel : '',
8476     inputType : 'text',
8477     
8478     name : false,
8479     placeholder: false,
8480     before : false,
8481     after : false,
8482     size : false,
8483     hasFocus : false,
8484     preventMark: false,
8485     isFormField : true,
8486     value : '',
8487     labelWidth : 2,
8488     labelAlign : false,
8489     readOnly : false,
8490     align : false,
8491     formatedValue : false,
8492     forceFeedback : false,
8493     
8494     indicatorpos : 'left',
8495     
8496     labellg : 0,
8497     labelmd : 0,
8498     labelsm : 0,
8499     labelxs : 0,
8500     
8501     parentLabelAlign : function()
8502     {
8503         var parent = this;
8504         while (parent.parent()) {
8505             parent = parent.parent();
8506             if (typeof(parent.labelAlign) !='undefined') {
8507                 return parent.labelAlign;
8508             }
8509         }
8510         return 'left';
8511         
8512     },
8513     
8514     getAutoCreate : function()
8515     {
8516         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8517         
8518         var id = Roo.id();
8519         
8520         var cfg = {};
8521         
8522         if(this.inputType != 'hidden'){
8523             cfg.cls = 'form-group' //input-group
8524         }
8525         
8526         var input =  {
8527             tag: 'input',
8528             id : id,
8529             type : this.inputType,
8530             value : this.value,
8531             cls : 'form-control',
8532             placeholder : this.placeholder || '',
8533             autocomplete : this.autocomplete || 'new-password'
8534         };
8535         
8536         if(this.align){
8537             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8538         }
8539         
8540         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8541             input.maxLength = this.maxLength;
8542         }
8543         
8544         if (this.disabled) {
8545             input.disabled=true;
8546         }
8547         
8548         if (this.readOnly) {
8549             input.readonly=true;
8550         }
8551         
8552         if (this.name) {
8553             input.name = this.name;
8554         }
8555         
8556         if (this.size) {
8557             input.cls += ' input-' + this.size;
8558         }
8559         
8560         var settings=this;
8561         ['xs','sm','md','lg'].map(function(size){
8562             if (settings[size]) {
8563                 cfg.cls += ' col-' + size + '-' + settings[size];
8564             }
8565         });
8566         
8567         var inputblock = input;
8568         
8569         var feedback = {
8570             tag: 'span',
8571             cls: 'glyphicon form-control-feedback'
8572         };
8573             
8574         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8575             
8576             inputblock = {
8577                 cls : 'has-feedback',
8578                 cn :  [
8579                     input,
8580                     feedback
8581                 ] 
8582             };  
8583         }
8584         
8585         if (this.before || this.after) {
8586             
8587             inputblock = {
8588                 cls : 'input-group',
8589                 cn :  [] 
8590             };
8591             
8592             if (this.before && typeof(this.before) == 'string') {
8593                 
8594                 inputblock.cn.push({
8595                     tag :'span',
8596                     cls : 'roo-input-before input-group-addon',
8597                     html : this.before
8598                 });
8599             }
8600             if (this.before && typeof(this.before) == 'object') {
8601                 this.before = Roo.factory(this.before);
8602                 
8603                 inputblock.cn.push({
8604                     tag :'span',
8605                     cls : 'roo-input-before input-group-' +
8606                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8607                 });
8608             }
8609             
8610             inputblock.cn.push(input);
8611             
8612             if (this.after && typeof(this.after) == 'string') {
8613                 inputblock.cn.push({
8614                     tag :'span',
8615                     cls : 'roo-input-after input-group-addon',
8616                     html : this.after
8617                 });
8618             }
8619             if (this.after && typeof(this.after) == 'object') {
8620                 this.after = Roo.factory(this.after);
8621                 
8622                 inputblock.cn.push({
8623                     tag :'span',
8624                     cls : 'roo-input-after input-group-' +
8625                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8626                 });
8627             }
8628             
8629             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8630                 inputblock.cls += ' has-feedback';
8631                 inputblock.cn.push(feedback);
8632             }
8633         };
8634         
8635         if (align ==='left' && this.fieldLabel.length) {
8636             
8637             cfg.cls += ' roo-form-group-label-left';
8638             
8639             cfg.cn = [
8640                 {
8641                     tag : 'i',
8642                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8643                     tooltip : 'This field is required'
8644                 },
8645                 {
8646                     tag: 'label',
8647                     'for' :  id,
8648                     cls : 'control-label',
8649                     html : this.fieldLabel
8650
8651                 },
8652                 {
8653                     cls : "", 
8654                     cn: [
8655                         inputblock
8656                     ]
8657                 }
8658             ];
8659             
8660             var labelCfg = cfg.cn[1];
8661             var contentCfg = cfg.cn[2];
8662             
8663             if(this.indicatorpos == 'right'){
8664                 cfg.cn = [
8665                     {
8666                         tag: 'label',
8667                         'for' :  id,
8668                         cls : 'control-label',
8669                         html : this.fieldLabel
8670
8671                     },
8672                     {
8673                         tag : 'i',
8674                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8675                         tooltip : 'This field is required'
8676                     },
8677                     {
8678                         cls : "",
8679                         cn: [
8680                             inputblock
8681                         ]
8682                     }
8683
8684                 ];
8685                 
8686                 labelCfg = cfg.cn[0];
8687                 contentCfg = cfg.cn[2];
8688             
8689             }
8690             
8691             if(this.labelWidth > 12){
8692                 labelCfg.style = "width: " + this.labelWidth + 'px';
8693             }
8694             
8695             if(this.labelWidth < 13 && this.labelmd == 0){
8696                 this.labelmd = this.labelWidth;
8697             }
8698             
8699             if(this.labellg > 0){
8700                 labelCfg.cls += ' col-lg-' + this.labellg;
8701                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8702             }
8703             
8704             if(this.labelmd > 0){
8705                 labelCfg.cls += ' col-md-' + this.labelmd;
8706                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8707             }
8708             
8709             if(this.labelsm > 0){
8710                 labelCfg.cls += ' col-sm-' + this.labelsm;
8711                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8712             }
8713             
8714             if(this.labelxs > 0){
8715                 labelCfg.cls += ' col-xs-' + this.labelxs;
8716                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8717             }
8718             
8719             
8720         } else if ( this.fieldLabel.length) {
8721                 
8722             cfg.cn = [
8723                 {
8724                     tag : 'i',
8725                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8726                     tooltip : 'This field is required'
8727                 },
8728                 {
8729                     tag: 'label',
8730                    //cls : 'input-group-addon',
8731                     html : this.fieldLabel
8732
8733                 },
8734
8735                inputblock
8736
8737            ];
8738            
8739            if(this.indicatorpos == 'right'){
8740                 
8741                 cfg.cn = [
8742                     {
8743                         tag: 'label',
8744                        //cls : 'input-group-addon',
8745                         html : this.fieldLabel
8746
8747                     },
8748                     {
8749                         tag : 'i',
8750                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8751                         tooltip : 'This field is required'
8752                     },
8753
8754                    inputblock
8755
8756                ];
8757
8758             }
8759
8760         } else {
8761             
8762             cfg.cn = [
8763
8764                     inputblock
8765
8766             ];
8767                 
8768                 
8769         };
8770         
8771         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8772            cfg.cls += ' navbar-form';
8773         }
8774         
8775         if (this.parentType === 'NavGroup') {
8776            cfg.cls += ' navbar-form';
8777            cfg.tag = 'li';
8778         }
8779         
8780         return cfg;
8781         
8782     },
8783     /**
8784      * return the real input element.
8785      */
8786     inputEl: function ()
8787     {
8788         return this.el.select('input.form-control',true).first();
8789     },
8790     
8791     tooltipEl : function()
8792     {
8793         return this.inputEl();
8794     },
8795     
8796     indicatorEl : function()
8797     {
8798         var indicator = this.el.select('i.roo-required-indicator',true).first();
8799         
8800         if(!indicator){
8801             return false;
8802         }
8803         
8804         return indicator;
8805         
8806     },
8807     
8808     setDisabled : function(v)
8809     {
8810         var i  = this.inputEl().dom;
8811         if (!v) {
8812             i.removeAttribute('disabled');
8813             return;
8814             
8815         }
8816         i.setAttribute('disabled','true');
8817     },
8818     initEvents : function()
8819     {
8820           
8821         this.inputEl().on("keydown" , this.fireKey,  this);
8822         this.inputEl().on("focus", this.onFocus,  this);
8823         this.inputEl().on("blur", this.onBlur,  this);
8824         
8825         this.inputEl().relayEvent('keyup', this);
8826         
8827         this.indicator = this.indicatorEl();
8828         
8829         if(this.indicator){
8830             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8831             this.indicator.hide();
8832         }
8833  
8834         // reference to original value for reset
8835         this.originalValue = this.getValue();
8836         //Roo.form.TextField.superclass.initEvents.call(this);
8837         if(this.validationEvent == 'keyup'){
8838             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8839             this.inputEl().on('keyup', this.filterValidation, this);
8840         }
8841         else if(this.validationEvent !== false){
8842             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8843         }
8844         
8845         if(this.selectOnFocus){
8846             this.on("focus", this.preFocus, this);
8847             
8848         }
8849         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8850             this.inputEl().on("keypress", this.filterKeys, this);
8851         } else {
8852             this.inputEl().relayEvent('keypress', this);
8853         }
8854        /* if(this.grow){
8855             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8856             this.el.on("click", this.autoSize,  this);
8857         }
8858         */
8859         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8860             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8861         }
8862         
8863         if (typeof(this.before) == 'object') {
8864             this.before.render(this.el.select('.roo-input-before',true).first());
8865         }
8866         if (typeof(this.after) == 'object') {
8867             this.after.render(this.el.select('.roo-input-after',true).first());
8868         }
8869         
8870         
8871     },
8872     filterValidation : function(e){
8873         if(!e.isNavKeyPress()){
8874             this.validationTask.delay(this.validationDelay);
8875         }
8876     },
8877      /**
8878      * Validates the field value
8879      * @return {Boolean} True if the value is valid, else false
8880      */
8881     validate : function(){
8882         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8883         if(this.disabled || this.validateValue(this.getRawValue())){
8884             this.markValid();
8885             return true;
8886         }
8887         
8888         this.markInvalid();
8889         return false;
8890     },
8891     
8892     
8893     /**
8894      * Validates a value according to the field's validation rules and marks the field as invalid
8895      * if the validation fails
8896      * @param {Mixed} value The value to validate
8897      * @return {Boolean} True if the value is valid, else false
8898      */
8899     validateValue : function(value){
8900         if(value.length < 1)  { // if it's blank
8901             if(this.allowBlank){
8902                 return true;
8903             }
8904             return false;
8905         }
8906         
8907         if(value.length < this.minLength){
8908             return false;
8909         }
8910         if(value.length > this.maxLength){
8911             return false;
8912         }
8913         if(this.vtype){
8914             var vt = Roo.form.VTypes;
8915             if(!vt[this.vtype](value, this)){
8916                 return false;
8917             }
8918         }
8919         if(typeof this.validator == "function"){
8920             var msg = this.validator(value);
8921             if(msg !== true){
8922                 return false;
8923             }
8924         }
8925         
8926         if(this.regex && !this.regex.test(value)){
8927             return false;
8928         }
8929         
8930         return true;
8931     },
8932
8933     
8934     
8935      // private
8936     fireKey : function(e){
8937         //Roo.log('field ' + e.getKey());
8938         if(e.isNavKeyPress()){
8939             this.fireEvent("specialkey", this, e);
8940         }
8941     },
8942     focus : function (selectText){
8943         if(this.rendered){
8944             this.inputEl().focus();
8945             if(selectText === true){
8946                 this.inputEl().dom.select();
8947             }
8948         }
8949         return this;
8950     } ,
8951     
8952     onFocus : function(){
8953         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8954            // this.el.addClass(this.focusClass);
8955         }
8956         if(!this.hasFocus){
8957             this.hasFocus = true;
8958             this.startValue = this.getValue();
8959             this.fireEvent("focus", this);
8960         }
8961     },
8962     
8963     beforeBlur : Roo.emptyFn,
8964
8965     
8966     // private
8967     onBlur : function(){
8968         this.beforeBlur();
8969         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8970             //this.el.removeClass(this.focusClass);
8971         }
8972         this.hasFocus = false;
8973         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8974             this.validate();
8975         }
8976         var v = this.getValue();
8977         if(String(v) !== String(this.startValue)){
8978             this.fireEvent('change', this, v, this.startValue);
8979         }
8980         this.fireEvent("blur", this);
8981     },
8982     
8983     /**
8984      * Resets the current field value to the originally loaded value and clears any validation messages
8985      */
8986     reset : function(){
8987         this.setValue(this.originalValue);
8988         this.validate();
8989     },
8990      /**
8991      * Returns the name of the field
8992      * @return {Mixed} name The name field
8993      */
8994     getName: function(){
8995         return this.name;
8996     },
8997      /**
8998      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8999      * @return {Mixed} value The field value
9000      */
9001     getValue : function(){
9002         
9003         var v = this.inputEl().getValue();
9004         
9005         return v;
9006     },
9007     /**
9008      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9009      * @return {Mixed} value The field value
9010      */
9011     getRawValue : function(){
9012         var v = this.inputEl().getValue();
9013         
9014         return v;
9015     },
9016     
9017     /**
9018      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9019      * @param {Mixed} value The value to set
9020      */
9021     setRawValue : function(v){
9022         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9023     },
9024     
9025     selectText : function(start, end){
9026         var v = this.getRawValue();
9027         if(v.length > 0){
9028             start = start === undefined ? 0 : start;
9029             end = end === undefined ? v.length : end;
9030             var d = this.inputEl().dom;
9031             if(d.setSelectionRange){
9032                 d.setSelectionRange(start, end);
9033             }else if(d.createTextRange){
9034                 var range = d.createTextRange();
9035                 range.moveStart("character", start);
9036                 range.moveEnd("character", v.length-end);
9037                 range.select();
9038             }
9039         }
9040     },
9041     
9042     /**
9043      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9044      * @param {Mixed} value The value to set
9045      */
9046     setValue : function(v){
9047         this.value = v;
9048         if(this.rendered){
9049             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9050             this.validate();
9051         }
9052     },
9053     
9054     /*
9055     processValue : function(value){
9056         if(this.stripCharsRe){
9057             var newValue = value.replace(this.stripCharsRe, '');
9058             if(newValue !== value){
9059                 this.setRawValue(newValue);
9060                 return newValue;
9061             }
9062         }
9063         return value;
9064     },
9065   */
9066     preFocus : function(){
9067         
9068         if(this.selectOnFocus){
9069             this.inputEl().dom.select();
9070         }
9071     },
9072     filterKeys : function(e){
9073         var k = e.getKey();
9074         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9075             return;
9076         }
9077         var c = e.getCharCode(), cc = String.fromCharCode(c);
9078         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9079             return;
9080         }
9081         if(!this.maskRe.test(cc)){
9082             e.stopEvent();
9083         }
9084     },
9085      /**
9086      * Clear any invalid styles/messages for this field
9087      */
9088     clearInvalid : function(){
9089         
9090         if(!this.el || this.preventMark){ // not rendered
9091             return;
9092         }
9093         
9094         if(this.indicator){
9095             this.indicator.hide();
9096         }
9097         
9098         this.el.removeClass(this.invalidClass);
9099         
9100         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9101             
9102             var feedback = this.el.select('.form-control-feedback', true).first();
9103             
9104             if(feedback){
9105                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9106             }
9107             
9108         }
9109         
9110         this.fireEvent('valid', this);
9111     },
9112     
9113      /**
9114      * Mark this field as valid
9115      */
9116     markValid : function()
9117     {
9118         if(!this.el  || this.preventMark){ // not rendered...
9119             return;
9120         }
9121         
9122         this.el.removeClass([this.invalidClass, this.validClass]);
9123         
9124         var feedback = this.el.select('.form-control-feedback', true).first();
9125             
9126         if(feedback){
9127             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9128         }
9129
9130         if(this.disabled){
9131             return;
9132         }
9133         
9134         if(this.allowBlank && !this.getRawValue().length){
9135             return;
9136         }
9137         
9138         if(this.indicator){
9139             this.indicator.hide();
9140         }
9141         
9142         this.el.addClass(this.validClass);
9143         
9144         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9145             
9146             var feedback = this.el.select('.form-control-feedback', true).first();
9147             
9148             if(feedback){
9149                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9150                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9151             }
9152             
9153         }
9154         
9155         this.fireEvent('valid', this);
9156     },
9157     
9158      /**
9159      * Mark this field as invalid
9160      * @param {String} msg The validation message
9161      */
9162     markInvalid : function(msg)
9163     {
9164         if(!this.el  || this.preventMark){ // not rendered
9165             return;
9166         }
9167         
9168         this.el.removeClass([this.invalidClass, this.validClass]);
9169         
9170         var feedback = this.el.select('.form-control-feedback', true).first();
9171             
9172         if(feedback){
9173             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9174         }
9175
9176         if(this.disabled){
9177             return;
9178         }
9179         
9180         if(this.allowBlank && !this.getRawValue().length){
9181             return;
9182         }
9183         
9184         if(this.indicator){
9185             this.indicator.show();
9186         }
9187         
9188         this.el.addClass(this.invalidClass);
9189         
9190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9191             
9192             var feedback = this.el.select('.form-control-feedback', true).first();
9193             
9194             if(feedback){
9195                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9196                 
9197                 if(this.getValue().length || this.forceFeedback){
9198                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9199                 }
9200                 
9201             }
9202             
9203         }
9204         
9205         this.fireEvent('invalid', this, msg);
9206     },
9207     // private
9208     SafariOnKeyDown : function(event)
9209     {
9210         // this is a workaround for a password hang bug on chrome/ webkit.
9211         if (this.inputEl().dom.type != 'password') {
9212             return;
9213         }
9214         
9215         var isSelectAll = false;
9216         
9217         if(this.inputEl().dom.selectionEnd > 0){
9218             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9219         }
9220         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9221             event.preventDefault();
9222             this.setValue('');
9223             return;
9224         }
9225         
9226         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9227             
9228             event.preventDefault();
9229             // this is very hacky as keydown always get's upper case.
9230             //
9231             var cc = String.fromCharCode(event.getCharCode());
9232             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9233             
9234         }
9235     },
9236     adjustWidth : function(tag, w){
9237         tag = tag.toLowerCase();
9238         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9239             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9240                 if(tag == 'input'){
9241                     return w + 2;
9242                 }
9243                 if(tag == 'textarea'){
9244                     return w-2;
9245                 }
9246             }else if(Roo.isOpera){
9247                 if(tag == 'input'){
9248                     return w + 2;
9249                 }
9250                 if(tag == 'textarea'){
9251                     return w-2;
9252                 }
9253             }
9254         }
9255         return w;
9256     }
9257     
9258 });
9259
9260  
9261 /*
9262  * - LGPL
9263  *
9264  * Input
9265  * 
9266  */
9267
9268 /**
9269  * @class Roo.bootstrap.TextArea
9270  * @extends Roo.bootstrap.Input
9271  * Bootstrap TextArea class
9272  * @cfg {Number} cols Specifies the visible width of a text area
9273  * @cfg {Number} rows Specifies the visible number of lines in a text area
9274  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9275  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9276  * @cfg {string} html text
9277  * 
9278  * @constructor
9279  * Create a new TextArea
9280  * @param {Object} config The config object
9281  */
9282
9283 Roo.bootstrap.TextArea = function(config){
9284     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9285    
9286 };
9287
9288 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9289      
9290     cols : false,
9291     rows : 5,
9292     readOnly : false,
9293     warp : 'soft',
9294     resize : false,
9295     value: false,
9296     html: false,
9297     
9298     getAutoCreate : function(){
9299         
9300         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9301         
9302         var id = Roo.id();
9303         
9304         var cfg = {};
9305         
9306         var input =  {
9307             tag: 'textarea',
9308             id : id,
9309             warp : this.warp,
9310             rows : this.rows,
9311             value : this.value || '',
9312             html: this.html || '',
9313             cls : 'form-control',
9314             placeholder : this.placeholder || '' 
9315             
9316         };
9317         
9318         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9319             input.maxLength = this.maxLength;
9320         }
9321         
9322         if(this.resize){
9323             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9324         }
9325         
9326         if(this.cols){
9327             input.cols = this.cols;
9328         }
9329         
9330         if (this.readOnly) {
9331             input.readonly = true;
9332         }
9333         
9334         if (this.name) {
9335             input.name = this.name;
9336         }
9337         
9338         if (this.size) {
9339             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9340         }
9341         
9342         var settings=this;
9343         ['xs','sm','md','lg'].map(function(size){
9344             if (settings[size]) {
9345                 cfg.cls += ' col-' + size + '-' + settings[size];
9346             }
9347         });
9348         
9349         var inputblock = input;
9350         
9351         if(this.hasFeedback && !this.allowBlank){
9352             
9353             var feedback = {
9354                 tag: 'span',
9355                 cls: 'glyphicon form-control-feedback'
9356             };
9357
9358             inputblock = {
9359                 cls : 'has-feedback',
9360                 cn :  [
9361                     input,
9362                     feedback
9363                 ] 
9364             };  
9365         }
9366         
9367         
9368         if (this.before || this.after) {
9369             
9370             inputblock = {
9371                 cls : 'input-group',
9372                 cn :  [] 
9373             };
9374             if (this.before) {
9375                 inputblock.cn.push({
9376                     tag :'span',
9377                     cls : 'input-group-addon',
9378                     html : this.before
9379                 });
9380             }
9381             
9382             inputblock.cn.push(input);
9383             
9384             if(this.hasFeedback && !this.allowBlank){
9385                 inputblock.cls += ' has-feedback';
9386                 inputblock.cn.push(feedback);
9387             }
9388             
9389             if (this.after) {
9390                 inputblock.cn.push({
9391                     tag :'span',
9392                     cls : 'input-group-addon',
9393                     html : this.after
9394                 });
9395             }
9396             
9397         }
9398         
9399         if (align ==='left' && this.fieldLabel.length) {
9400             cfg.cn = [
9401                 {
9402                     tag: 'label',
9403                     'for' :  id,
9404                     cls : 'control-label',
9405                     html : this.fieldLabel
9406                 },
9407                 {
9408                     cls : "",
9409                     cn: [
9410                         inputblock
9411                     ]
9412                 }
9413
9414             ];
9415             
9416             if(this.labelWidth > 12){
9417                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9418             }
9419
9420             if(this.labelWidth < 13 && this.labelmd == 0){
9421                 this.labelmd = this.labelWidth;
9422             }
9423
9424             if(this.labellg > 0){
9425                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9426                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9427             }
9428
9429             if(this.labelmd > 0){
9430                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9431                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9432             }
9433
9434             if(this.labelsm > 0){
9435                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9436                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9437             }
9438
9439             if(this.labelxs > 0){
9440                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9441                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9442             }
9443             
9444         } else if ( this.fieldLabel.length) {
9445             cfg.cn = [
9446
9447                {
9448                    tag: 'label',
9449                    //cls : 'input-group-addon',
9450                    html : this.fieldLabel
9451
9452                },
9453
9454                inputblock
9455
9456            ];
9457
9458         } else {
9459
9460             cfg.cn = [
9461
9462                 inputblock
9463
9464             ];
9465                 
9466         }
9467         
9468         if (this.disabled) {
9469             input.disabled=true;
9470         }
9471         
9472         return cfg;
9473         
9474     },
9475     /**
9476      * return the real textarea element.
9477      */
9478     inputEl: function ()
9479     {
9480         return this.el.select('textarea.form-control',true).first();
9481     },
9482     
9483     /**
9484      * Clear any invalid styles/messages for this field
9485      */
9486     clearInvalid : function()
9487     {
9488         
9489         if(!this.el || this.preventMark){ // not rendered
9490             return;
9491         }
9492         
9493         var label = this.el.select('label', true).first();
9494         var icon = this.el.select('i.fa-star', true).first();
9495         
9496         if(label && icon){
9497             icon.remove();
9498         }
9499         
9500         this.el.removeClass(this.invalidClass);
9501         
9502         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9503             
9504             var feedback = this.el.select('.form-control-feedback', true).first();
9505             
9506             if(feedback){
9507                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9508             }
9509             
9510         }
9511         
9512         this.fireEvent('valid', this);
9513     },
9514     
9515      /**
9516      * Mark this field as valid
9517      */
9518     markValid : function()
9519     {
9520         if(!this.el  || this.preventMark){ // not rendered
9521             return;
9522         }
9523         
9524         this.el.removeClass([this.invalidClass, this.validClass]);
9525         
9526         var feedback = this.el.select('.form-control-feedback', true).first();
9527             
9528         if(feedback){
9529             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9530         }
9531
9532         if(this.disabled || this.allowBlank){
9533             return;
9534         }
9535         
9536         var label = this.el.select('label', true).first();
9537         var icon = this.el.select('i.fa-star', true).first();
9538         
9539         if(label && icon){
9540             icon.remove();
9541         }
9542         
9543         this.el.addClass(this.validClass);
9544         
9545         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9546             
9547             var feedback = this.el.select('.form-control-feedback', true).first();
9548             
9549             if(feedback){
9550                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9551                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9552             }
9553             
9554         }
9555         
9556         this.fireEvent('valid', this);
9557     },
9558     
9559      /**
9560      * Mark this field as invalid
9561      * @param {String} msg The validation message
9562      */
9563     markInvalid : function(msg)
9564     {
9565         if(!this.el  || this.preventMark){ // not rendered
9566             return;
9567         }
9568         
9569         this.el.removeClass([this.invalidClass, this.validClass]);
9570         
9571         var feedback = this.el.select('.form-control-feedback', true).first();
9572             
9573         if(feedback){
9574             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9575         }
9576
9577         if(this.disabled || this.allowBlank){
9578             return;
9579         }
9580         
9581         var label = this.el.select('label', true).first();
9582         var icon = this.el.select('i.fa-star', true).first();
9583         
9584         if(!this.getValue().length && label && !icon){
9585             this.el.createChild({
9586                 tag : 'i',
9587                 cls : 'text-danger fa fa-lg fa-star',
9588                 tooltip : 'This field is required',
9589                 style : 'margin-right:5px;'
9590             }, label, true);
9591         }
9592
9593         this.el.addClass(this.invalidClass);
9594         
9595         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9596             
9597             var feedback = this.el.select('.form-control-feedback', true).first();
9598             
9599             if(feedback){
9600                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9601                 
9602                 if(this.getValue().length || this.forceFeedback){
9603                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9604                 }
9605                 
9606             }
9607             
9608         }
9609         
9610         this.fireEvent('invalid', this, msg);
9611     }
9612 });
9613
9614  
9615 /*
9616  * - LGPL
9617  *
9618  * trigger field - base class for combo..
9619  * 
9620  */
9621  
9622 /**
9623  * @class Roo.bootstrap.TriggerField
9624  * @extends Roo.bootstrap.Input
9625  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9626  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9627  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9628  * for which you can provide a custom implementation.  For example:
9629  * <pre><code>
9630 var trigger = new Roo.bootstrap.TriggerField();
9631 trigger.onTriggerClick = myTriggerFn;
9632 trigger.applyTo('my-field');
9633 </code></pre>
9634  *
9635  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9636  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9637  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9638  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9639  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9640
9641  * @constructor
9642  * Create a new TriggerField.
9643  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9644  * to the base TextField)
9645  */
9646 Roo.bootstrap.TriggerField = function(config){
9647     this.mimicing = false;
9648     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9649 };
9650
9651 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9652     /**
9653      * @cfg {String} triggerClass A CSS class to apply to the trigger
9654      */
9655      /**
9656      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9657      */
9658     hideTrigger:false,
9659
9660     /**
9661      * @cfg {Boolean} removable (true|false) special filter default false
9662      */
9663     removable : false,
9664     
9665     /** @cfg {Boolean} grow @hide */
9666     /** @cfg {Number} growMin @hide */
9667     /** @cfg {Number} growMax @hide */
9668
9669     /**
9670      * @hide 
9671      * @method
9672      */
9673     autoSize: Roo.emptyFn,
9674     // private
9675     monitorTab : true,
9676     // private
9677     deferHeight : true,
9678
9679     
9680     actionMode : 'wrap',
9681     
9682     caret : false,
9683     
9684     
9685     getAutoCreate : function(){
9686        
9687         var align = this.labelAlign || this.parentLabelAlign();
9688         
9689         var id = Roo.id();
9690         
9691         var cfg = {
9692             cls: 'form-group' //input-group
9693         };
9694         
9695         
9696         var input =  {
9697             tag: 'input',
9698             id : id,
9699             type : this.inputType,
9700             cls : 'form-control',
9701             autocomplete: 'new-password',
9702             placeholder : this.placeholder || '' 
9703             
9704         };
9705         if (this.name) {
9706             input.name = this.name;
9707         }
9708         if (this.size) {
9709             input.cls += ' input-' + this.size;
9710         }
9711         
9712         if (this.disabled) {
9713             input.disabled=true;
9714         }
9715         
9716         var inputblock = input;
9717         
9718         if(this.hasFeedback && !this.allowBlank){
9719             
9720             var feedback = {
9721                 tag: 'span',
9722                 cls: 'glyphicon form-control-feedback'
9723             };
9724             
9725             if(this.removable && !this.editable && !this.tickable){
9726                 inputblock = {
9727                     cls : 'has-feedback',
9728                     cn :  [
9729                         inputblock,
9730                         {
9731                             tag: 'button',
9732                             html : 'x',
9733                             cls : 'roo-combo-removable-btn close'
9734                         },
9735                         feedback
9736                     ] 
9737                 };
9738             } else {
9739                 inputblock = {
9740                     cls : 'has-feedback',
9741                     cn :  [
9742                         inputblock,
9743                         feedback
9744                     ] 
9745                 };
9746             }
9747
9748         } else {
9749             if(this.removable && !this.editable && !this.tickable){
9750                 inputblock = {
9751                     cls : 'roo-removable',
9752                     cn :  [
9753                         inputblock,
9754                         {
9755                             tag: 'button',
9756                             html : 'x',
9757                             cls : 'roo-combo-removable-btn close'
9758                         }
9759                     ] 
9760                 };
9761             }
9762         }
9763         
9764         if (this.before || this.after) {
9765             
9766             inputblock = {
9767                 cls : 'input-group',
9768                 cn :  [] 
9769             };
9770             if (this.before) {
9771                 inputblock.cn.push({
9772                     tag :'span',
9773                     cls : 'input-group-addon',
9774                     html : this.before
9775                 });
9776             }
9777             
9778             inputblock.cn.push(input);
9779             
9780             if(this.hasFeedback && !this.allowBlank){
9781                 inputblock.cls += ' has-feedback';
9782                 inputblock.cn.push(feedback);
9783             }
9784             
9785             if (this.after) {
9786                 inputblock.cn.push({
9787                     tag :'span',
9788                     cls : 'input-group-addon',
9789                     html : this.after
9790                 });
9791             }
9792             
9793         };
9794         
9795         var box = {
9796             tag: 'div',
9797             cn: [
9798                 {
9799                     tag: 'input',
9800                     type : 'hidden',
9801                     cls: 'form-hidden-field'
9802                 },
9803                 inputblock
9804             ]
9805             
9806         };
9807         
9808         if(this.multiple){
9809             box = {
9810                 tag: 'div',
9811                 cn: [
9812                     {
9813                         tag: 'input',
9814                         type : 'hidden',
9815                         cls: 'form-hidden-field'
9816                     },
9817                     {
9818                         tag: 'ul',
9819                         cls: 'roo-select2-choices',
9820                         cn:[
9821                             {
9822                                 tag: 'li',
9823                                 cls: 'roo-select2-search-field',
9824                                 cn: [
9825
9826                                     inputblock
9827                                 ]
9828                             }
9829                         ]
9830                     }
9831                 ]
9832             }
9833         };
9834         
9835         var combobox = {
9836             cls: 'roo-select2-container input-group',
9837             cn: [
9838                 box
9839 //                {
9840 //                    tag: 'ul',
9841 //                    cls: 'typeahead typeahead-long dropdown-menu',
9842 //                    style: 'display:none'
9843 //                }
9844             ]
9845         };
9846         
9847         if(!this.multiple && this.showToggleBtn){
9848             
9849             var caret = {
9850                         tag: 'span',
9851                         cls: 'caret'
9852              };
9853             if (this.caret != false) {
9854                 caret = {
9855                      tag: 'i',
9856                      cls: 'fa fa-' + this.caret
9857                 };
9858                 
9859             }
9860             
9861             combobox.cn.push({
9862                 tag :'span',
9863                 cls : 'input-group-addon btn dropdown-toggle',
9864                 cn : [
9865                     caret,
9866                     {
9867                         tag: 'span',
9868                         cls: 'combobox-clear',
9869                         cn  : [
9870                             {
9871                                 tag : 'i',
9872                                 cls: 'icon-remove'
9873                             }
9874                         ]
9875                     }
9876                 ]
9877
9878             })
9879         }
9880         
9881         if(this.multiple){
9882             combobox.cls += ' roo-select2-container-multi';
9883         }
9884         
9885         if (align ==='left' && this.fieldLabel.length) {
9886             
9887             cfg.cls += ' roo-form-group-label-left';
9888
9889             cfg.cn = [
9890                 {
9891                     tag : 'i',
9892                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9893                     tooltip : 'This field is required'
9894                 },
9895                 {
9896                     tag: 'label',
9897                     'for' :  id,
9898                     cls : 'control-label',
9899                     html : this.fieldLabel
9900
9901                 },
9902                 {
9903                     cls : "", 
9904                     cn: [
9905                         combobox
9906                     ]
9907                 }
9908
9909             ];
9910             
9911             var labelCfg = cfg.cn[1];
9912             var contentCfg = cfg.cn[2];
9913             
9914             if(this.indicatorpos == 'right'){
9915                 cfg.cn = [
9916                     {
9917                         tag: 'label',
9918                         'for' :  id,
9919                         cls : 'control-label',
9920                         cn : [
9921                             {
9922                                 tag : 'span',
9923                                 html : this.fieldLabel
9924                             },
9925                             {
9926                                 tag : 'i',
9927                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9928                                 tooltip : 'This field is required'
9929                             }
9930                         ]
9931                     },
9932                     {
9933                         cls : "", 
9934                         cn: [
9935                             combobox
9936                         ]
9937                     }
9938
9939                 ];
9940                 
9941                 labelCfg = cfg.cn[0];
9942                 contentCfg = cfg.cn[1];
9943             }
9944             
9945             if(this.labelWidth > 12){
9946                 labelCfg.style = "width: " + this.labelWidth + 'px';
9947             }
9948             
9949             if(this.labelWidth < 13 && this.labelmd == 0){
9950                 this.labelmd = this.labelWidth;
9951             }
9952             
9953             if(this.labellg > 0){
9954                 labelCfg.cls += ' col-lg-' + this.labellg;
9955                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9956             }
9957             
9958             if(this.labelmd > 0){
9959                 labelCfg.cls += ' col-md-' + this.labelmd;
9960                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9961             }
9962             
9963             if(this.labelsm > 0){
9964                 labelCfg.cls += ' col-sm-' + this.labelsm;
9965                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9966             }
9967             
9968             if(this.labelxs > 0){
9969                 labelCfg.cls += ' col-xs-' + this.labelxs;
9970                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9971             }
9972             
9973         } else if ( this.fieldLabel.length) {
9974 //                Roo.log(" label");
9975             cfg.cn = [
9976                 {
9977                    tag : 'i',
9978                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9979                    tooltip : 'This field is required'
9980                },
9981                {
9982                    tag: 'label',
9983                    //cls : 'input-group-addon',
9984                    html : this.fieldLabel
9985
9986                },
9987
9988                combobox
9989
9990             ];
9991             
9992             if(this.indicatorpos == 'right'){
9993                 
9994                 cfg.cn = [
9995                     {
9996                        tag: 'label',
9997                        cn : [
9998                            {
9999                                tag : 'span',
10000                                html : this.fieldLabel
10001                            },
10002                            {
10003                               tag : 'i',
10004                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10005                               tooltip : 'This field is required'
10006                            }
10007                        ]
10008
10009                     },
10010                     combobox
10011
10012                 ];
10013
10014             }
10015
10016         } else {
10017             
10018 //                Roo.log(" no label && no align");
10019                 cfg = combobox
10020                      
10021                 
10022         }
10023         
10024         var settings=this;
10025         ['xs','sm','md','lg'].map(function(size){
10026             if (settings[size]) {
10027                 cfg.cls += ' col-' + size + '-' + settings[size];
10028             }
10029         });
10030         
10031         return cfg;
10032         
10033     },
10034     
10035     
10036     
10037     // private
10038     onResize : function(w, h){
10039 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10040 //        if(typeof w == 'number'){
10041 //            var x = w - this.trigger.getWidth();
10042 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10043 //            this.trigger.setStyle('left', x+'px');
10044 //        }
10045     },
10046
10047     // private
10048     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10049
10050     // private
10051     getResizeEl : function(){
10052         return this.inputEl();
10053     },
10054
10055     // private
10056     getPositionEl : function(){
10057         return this.inputEl();
10058     },
10059
10060     // private
10061     alignErrorIcon : function(){
10062         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10063     },
10064
10065     // private
10066     initEvents : function(){
10067         
10068         this.createList();
10069         
10070         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10071         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10072         if(!this.multiple && this.showToggleBtn){
10073             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10074             if(this.hideTrigger){
10075                 this.trigger.setDisplayed(false);
10076             }
10077             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10078         }
10079         
10080         if(this.multiple){
10081             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10082         }
10083         
10084         if(this.removable && !this.editable && !this.tickable){
10085             var close = this.closeTriggerEl();
10086             
10087             if(close){
10088                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10089                 close.on('click', this.removeBtnClick, this, close);
10090             }
10091         }
10092         
10093         //this.trigger.addClassOnOver('x-form-trigger-over');
10094         //this.trigger.addClassOnClick('x-form-trigger-click');
10095         
10096         //if(!this.width){
10097         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10098         //}
10099     },
10100     
10101     closeTriggerEl : function()
10102     {
10103         var close = this.el.select('.roo-combo-removable-btn', true).first();
10104         return close ? close : false;
10105     },
10106     
10107     removeBtnClick : function(e, h, el)
10108     {
10109         e.preventDefault();
10110         
10111         if(this.fireEvent("remove", this) !== false){
10112             this.reset();
10113             this.fireEvent("afterremove", this)
10114         }
10115     },
10116     
10117     createList : function()
10118     {
10119         this.list = Roo.get(document.body).createChild({
10120             tag: 'ul',
10121             cls: 'typeahead typeahead-long dropdown-menu',
10122             style: 'display:none'
10123         });
10124         
10125         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10126         
10127     },
10128
10129     // private
10130     initTrigger : function(){
10131        
10132     },
10133
10134     // private
10135     onDestroy : function(){
10136         if(this.trigger){
10137             this.trigger.removeAllListeners();
10138           //  this.trigger.remove();
10139         }
10140         //if(this.wrap){
10141         //    this.wrap.remove();
10142         //}
10143         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10144     },
10145
10146     // private
10147     onFocus : function(){
10148         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10149         /*
10150         if(!this.mimicing){
10151             this.wrap.addClass('x-trigger-wrap-focus');
10152             this.mimicing = true;
10153             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10154             if(this.monitorTab){
10155                 this.el.on("keydown", this.checkTab, this);
10156             }
10157         }
10158         */
10159     },
10160
10161     // private
10162     checkTab : function(e){
10163         if(e.getKey() == e.TAB){
10164             this.triggerBlur();
10165         }
10166     },
10167
10168     // private
10169     onBlur : function(){
10170         // do nothing
10171     },
10172
10173     // private
10174     mimicBlur : function(e, t){
10175         /*
10176         if(!this.wrap.contains(t) && this.validateBlur()){
10177             this.triggerBlur();
10178         }
10179         */
10180     },
10181
10182     // private
10183     triggerBlur : function(){
10184         this.mimicing = false;
10185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10186         if(this.monitorTab){
10187             this.el.un("keydown", this.checkTab, this);
10188         }
10189         //this.wrap.removeClass('x-trigger-wrap-focus');
10190         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10191     },
10192
10193     // private
10194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10195     validateBlur : function(e, t){
10196         return true;
10197     },
10198
10199     // private
10200     onDisable : function(){
10201         this.inputEl().dom.disabled = true;
10202         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10203         //if(this.wrap){
10204         //    this.wrap.addClass('x-item-disabled');
10205         //}
10206     },
10207
10208     // private
10209     onEnable : function(){
10210         this.inputEl().dom.disabled = false;
10211         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10212         //if(this.wrap){
10213         //    this.el.removeClass('x-item-disabled');
10214         //}
10215     },
10216
10217     // private
10218     onShow : function(){
10219         var ae = this.getActionEl();
10220         
10221         if(ae){
10222             ae.dom.style.display = '';
10223             ae.dom.style.visibility = 'visible';
10224         }
10225     },
10226
10227     // private
10228     
10229     onHide : function(){
10230         var ae = this.getActionEl();
10231         ae.dom.style.display = 'none';
10232     },
10233
10234     /**
10235      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10236      * by an implementing function.
10237      * @method
10238      * @param {EventObject} e
10239      */
10240     onTriggerClick : Roo.emptyFn
10241 });
10242  /*
10243  * Based on:
10244  * Ext JS Library 1.1.1
10245  * Copyright(c) 2006-2007, Ext JS, LLC.
10246  *
10247  * Originally Released Under LGPL - original licence link has changed is not relivant.
10248  *
10249  * Fork - LGPL
10250  * <script type="text/javascript">
10251  */
10252
10253
10254 /**
10255  * @class Roo.data.SortTypes
10256  * @singleton
10257  * Defines the default sorting (casting?) comparison functions used when sorting data.
10258  */
10259 Roo.data.SortTypes = {
10260     /**
10261      * Default sort that does nothing
10262      * @param {Mixed} s The value being converted
10263      * @return {Mixed} The comparison value
10264      */
10265     none : function(s){
10266         return s;
10267     },
10268     
10269     /**
10270      * The regular expression used to strip tags
10271      * @type {RegExp}
10272      * @property
10273      */
10274     stripTagsRE : /<\/?[^>]+>/gi,
10275     
10276     /**
10277      * Strips all HTML tags to sort on text only
10278      * @param {Mixed} s The value being converted
10279      * @return {String} The comparison value
10280      */
10281     asText : function(s){
10282         return String(s).replace(this.stripTagsRE, "");
10283     },
10284     
10285     /**
10286      * Strips all HTML tags to sort on text only - Case insensitive
10287      * @param {Mixed} s The value being converted
10288      * @return {String} The comparison value
10289      */
10290     asUCText : function(s){
10291         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10292     },
10293     
10294     /**
10295      * Case insensitive string
10296      * @param {Mixed} s The value being converted
10297      * @return {String} The comparison value
10298      */
10299     asUCString : function(s) {
10300         return String(s).toUpperCase();
10301     },
10302     
10303     /**
10304      * Date sorting
10305      * @param {Mixed} s The value being converted
10306      * @return {Number} The comparison value
10307      */
10308     asDate : function(s) {
10309         if(!s){
10310             return 0;
10311         }
10312         if(s instanceof Date){
10313             return s.getTime();
10314         }
10315         return Date.parse(String(s));
10316     },
10317     
10318     /**
10319      * Float sorting
10320      * @param {Mixed} s The value being converted
10321      * @return {Float} The comparison value
10322      */
10323     asFloat : function(s) {
10324         var val = parseFloat(String(s).replace(/,/g, ""));
10325         if(isNaN(val)) {
10326             val = 0;
10327         }
10328         return val;
10329     },
10330     
10331     /**
10332      * Integer sorting
10333      * @param {Mixed} s The value being converted
10334      * @return {Number} The comparison value
10335      */
10336     asInt : function(s) {
10337         var val = parseInt(String(s).replace(/,/g, ""));
10338         if(isNaN(val)) {
10339             val = 0;
10340         }
10341         return val;
10342     }
10343 };/*
10344  * Based on:
10345  * Ext JS Library 1.1.1
10346  * Copyright(c) 2006-2007, Ext JS, LLC.
10347  *
10348  * Originally Released Under LGPL - original licence link has changed is not relivant.
10349  *
10350  * Fork - LGPL
10351  * <script type="text/javascript">
10352  */
10353
10354 /**
10355 * @class Roo.data.Record
10356  * Instances of this class encapsulate both record <em>definition</em> information, and record
10357  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10358  * to access Records cached in an {@link Roo.data.Store} object.<br>
10359  * <p>
10360  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10361  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10362  * objects.<br>
10363  * <p>
10364  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10365  * @constructor
10366  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10367  * {@link #create}. The parameters are the same.
10368  * @param {Array} data An associative Array of data values keyed by the field name.
10369  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10370  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10371  * not specified an integer id is generated.
10372  */
10373 Roo.data.Record = function(data, id){
10374     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10375     this.data = data;
10376 };
10377
10378 /**
10379  * Generate a constructor for a specific record layout.
10380  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10381  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10382  * Each field definition object may contain the following properties: <ul>
10383  * <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,
10384  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10385  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10386  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10387  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10388  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10389  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10390  * this may be omitted.</p></li>
10391  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10392  * <ul><li>auto (Default, implies no conversion)</li>
10393  * <li>string</li>
10394  * <li>int</li>
10395  * <li>float</li>
10396  * <li>boolean</li>
10397  * <li>date</li></ul></p></li>
10398  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10399  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10400  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10401  * by the Reader into an object that will be stored in the Record. It is passed the
10402  * following parameters:<ul>
10403  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10404  * </ul></p></li>
10405  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10406  * </ul>
10407  * <br>usage:<br><pre><code>
10408 var TopicRecord = Roo.data.Record.create(
10409     {name: 'title', mapping: 'topic_title'},
10410     {name: 'author', mapping: 'username'},
10411     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10412     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10413     {name: 'lastPoster', mapping: 'user2'},
10414     {name: 'excerpt', mapping: 'post_text'}
10415 );
10416
10417 var myNewRecord = new TopicRecord({
10418     title: 'Do my job please',
10419     author: 'noobie',
10420     totalPosts: 1,
10421     lastPost: new Date(),
10422     lastPoster: 'Animal',
10423     excerpt: 'No way dude!'
10424 });
10425 myStore.add(myNewRecord);
10426 </code></pre>
10427  * @method create
10428  * @static
10429  */
10430 Roo.data.Record.create = function(o){
10431     var f = function(){
10432         f.superclass.constructor.apply(this, arguments);
10433     };
10434     Roo.extend(f, Roo.data.Record);
10435     var p = f.prototype;
10436     p.fields = new Roo.util.MixedCollection(false, function(field){
10437         return field.name;
10438     });
10439     for(var i = 0, len = o.length; i < len; i++){
10440         p.fields.add(new Roo.data.Field(o[i]));
10441     }
10442     f.getField = function(name){
10443         return p.fields.get(name);  
10444     };
10445     return f;
10446 };
10447
10448 Roo.data.Record.AUTO_ID = 1000;
10449 Roo.data.Record.EDIT = 'edit';
10450 Roo.data.Record.REJECT = 'reject';
10451 Roo.data.Record.COMMIT = 'commit';
10452
10453 Roo.data.Record.prototype = {
10454     /**
10455      * Readonly flag - true if this record has been modified.
10456      * @type Boolean
10457      */
10458     dirty : false,
10459     editing : false,
10460     error: null,
10461     modified: null,
10462
10463     // private
10464     join : function(store){
10465         this.store = store;
10466     },
10467
10468     /**
10469      * Set the named field to the specified value.
10470      * @param {String} name The name of the field to set.
10471      * @param {Object} value The value to set the field to.
10472      */
10473     set : function(name, value){
10474         if(this.data[name] == value){
10475             return;
10476         }
10477         this.dirty = true;
10478         if(!this.modified){
10479             this.modified = {};
10480         }
10481         if(typeof this.modified[name] == 'undefined'){
10482             this.modified[name] = this.data[name];
10483         }
10484         this.data[name] = value;
10485         if(!this.editing && this.store){
10486             this.store.afterEdit(this);
10487         }       
10488     },
10489
10490     /**
10491      * Get the value of the named field.
10492      * @param {String} name The name of the field to get the value of.
10493      * @return {Object} The value of the field.
10494      */
10495     get : function(name){
10496         return this.data[name]; 
10497     },
10498
10499     // private
10500     beginEdit : function(){
10501         this.editing = true;
10502         this.modified = {}; 
10503     },
10504
10505     // private
10506     cancelEdit : function(){
10507         this.editing = false;
10508         delete this.modified;
10509     },
10510
10511     // private
10512     endEdit : function(){
10513         this.editing = false;
10514         if(this.dirty && this.store){
10515             this.store.afterEdit(this);
10516         }
10517     },
10518
10519     /**
10520      * Usually called by the {@link Roo.data.Store} which owns the Record.
10521      * Rejects all changes made to the Record since either creation, or the last commit operation.
10522      * Modified fields are reverted to their original values.
10523      * <p>
10524      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10525      * of reject operations.
10526      */
10527     reject : function(){
10528         var m = this.modified;
10529         for(var n in m){
10530             if(typeof m[n] != "function"){
10531                 this.data[n] = m[n];
10532             }
10533         }
10534         this.dirty = false;
10535         delete this.modified;
10536         this.editing = false;
10537         if(this.store){
10538             this.store.afterReject(this);
10539         }
10540     },
10541
10542     /**
10543      * Usually called by the {@link Roo.data.Store} which owns the Record.
10544      * Commits all changes made to the Record since either creation, or the last commit operation.
10545      * <p>
10546      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10547      * of commit operations.
10548      */
10549     commit : function(){
10550         this.dirty = false;
10551         delete this.modified;
10552         this.editing = false;
10553         if(this.store){
10554             this.store.afterCommit(this);
10555         }
10556     },
10557
10558     // private
10559     hasError : function(){
10560         return this.error != null;
10561     },
10562
10563     // private
10564     clearError : function(){
10565         this.error = null;
10566     },
10567
10568     /**
10569      * Creates a copy of this record.
10570      * @param {String} id (optional) A new record id if you don't want to use this record's id
10571      * @return {Record}
10572      */
10573     copy : function(newId) {
10574         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10575     }
10576 };/*
10577  * Based on:
10578  * Ext JS Library 1.1.1
10579  * Copyright(c) 2006-2007, Ext JS, LLC.
10580  *
10581  * Originally Released Under LGPL - original licence link has changed is not relivant.
10582  *
10583  * Fork - LGPL
10584  * <script type="text/javascript">
10585  */
10586
10587
10588
10589 /**
10590  * @class Roo.data.Store
10591  * @extends Roo.util.Observable
10592  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10593  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10594  * <p>
10595  * 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
10596  * has no knowledge of the format of the data returned by the Proxy.<br>
10597  * <p>
10598  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10599  * instances from the data object. These records are cached and made available through accessor functions.
10600  * @constructor
10601  * Creates a new Store.
10602  * @param {Object} config A config object containing the objects needed for the Store to access data,
10603  * and read the data into Records.
10604  */
10605 Roo.data.Store = function(config){
10606     this.data = new Roo.util.MixedCollection(false);
10607     this.data.getKey = function(o){
10608         return o.id;
10609     };
10610     this.baseParams = {};
10611     // private
10612     this.paramNames = {
10613         "start" : "start",
10614         "limit" : "limit",
10615         "sort" : "sort",
10616         "dir" : "dir",
10617         "multisort" : "_multisort"
10618     };
10619
10620     if(config && config.data){
10621         this.inlineData = config.data;
10622         delete config.data;
10623     }
10624
10625     Roo.apply(this, config);
10626     
10627     if(this.reader){ // reader passed
10628         this.reader = Roo.factory(this.reader, Roo.data);
10629         this.reader.xmodule = this.xmodule || false;
10630         if(!this.recordType){
10631             this.recordType = this.reader.recordType;
10632         }
10633         if(this.reader.onMetaChange){
10634             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10635         }
10636     }
10637
10638     if(this.recordType){
10639         this.fields = this.recordType.prototype.fields;
10640     }
10641     this.modified = [];
10642
10643     this.addEvents({
10644         /**
10645          * @event datachanged
10646          * Fires when the data cache has changed, and a widget which is using this Store
10647          * as a Record cache should refresh its view.
10648          * @param {Store} this
10649          */
10650         datachanged : true,
10651         /**
10652          * @event metachange
10653          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10654          * @param {Store} this
10655          * @param {Object} meta The JSON metadata
10656          */
10657         metachange : true,
10658         /**
10659          * @event add
10660          * Fires when Records have been added to the Store
10661          * @param {Store} this
10662          * @param {Roo.data.Record[]} records The array of Records added
10663          * @param {Number} index The index at which the record(s) were added
10664          */
10665         add : true,
10666         /**
10667          * @event remove
10668          * Fires when a Record has been removed from the Store
10669          * @param {Store} this
10670          * @param {Roo.data.Record} record The Record that was removed
10671          * @param {Number} index The index at which the record was removed
10672          */
10673         remove : true,
10674         /**
10675          * @event update
10676          * Fires when a Record has been updated
10677          * @param {Store} this
10678          * @param {Roo.data.Record} record The Record that was updated
10679          * @param {String} operation The update operation being performed.  Value may be one of:
10680          * <pre><code>
10681  Roo.data.Record.EDIT
10682  Roo.data.Record.REJECT
10683  Roo.data.Record.COMMIT
10684          * </code></pre>
10685          */
10686         update : true,
10687         /**
10688          * @event clear
10689          * Fires when the data cache has been cleared.
10690          * @param {Store} this
10691          */
10692         clear : true,
10693         /**
10694          * @event beforeload
10695          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10696          * the load action will be canceled.
10697          * @param {Store} this
10698          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10699          */
10700         beforeload : true,
10701         /**
10702          * @event beforeloadadd
10703          * Fires after a new set of Records has been loaded.
10704          * @param {Store} this
10705          * @param {Roo.data.Record[]} records The Records that were loaded
10706          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10707          */
10708         beforeloadadd : true,
10709         /**
10710          * @event load
10711          * Fires after a new set of Records has been loaded, before they are added to the store.
10712          * @param {Store} this
10713          * @param {Roo.data.Record[]} records The Records that were loaded
10714          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10715          * @params {Object} return from reader
10716          */
10717         load : true,
10718         /**
10719          * @event loadexception
10720          * Fires if an exception occurs in the Proxy during loading.
10721          * Called with the signature of the Proxy's "loadexception" event.
10722          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10723          * 
10724          * @param {Proxy} 
10725          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10726          * @param {Object} load options 
10727          * @param {Object} jsonData from your request (normally this contains the Exception)
10728          */
10729         loadexception : true
10730     });
10731     
10732     if(this.proxy){
10733         this.proxy = Roo.factory(this.proxy, Roo.data);
10734         this.proxy.xmodule = this.xmodule || false;
10735         this.relayEvents(this.proxy,  ["loadexception"]);
10736     }
10737     this.sortToggle = {};
10738     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10739
10740     Roo.data.Store.superclass.constructor.call(this);
10741
10742     if(this.inlineData){
10743         this.loadData(this.inlineData);
10744         delete this.inlineData;
10745     }
10746 };
10747
10748 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10749      /**
10750     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10751     * without a remote query - used by combo/forms at present.
10752     */
10753     
10754     /**
10755     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10756     */
10757     /**
10758     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10759     */
10760     /**
10761     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10762     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10763     */
10764     /**
10765     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10766     * on any HTTP request
10767     */
10768     /**
10769     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10770     */
10771     /**
10772     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10773     */
10774     multiSort: false,
10775     /**
10776     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10777     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10778     */
10779     remoteSort : false,
10780
10781     /**
10782     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10783      * loaded or when a record is removed. (defaults to false).
10784     */
10785     pruneModifiedRecords : false,
10786
10787     // private
10788     lastOptions : null,
10789
10790     /**
10791      * Add Records to the Store and fires the add event.
10792      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10793      */
10794     add : function(records){
10795         records = [].concat(records);
10796         for(var i = 0, len = records.length; i < len; i++){
10797             records[i].join(this);
10798         }
10799         var index = this.data.length;
10800         this.data.addAll(records);
10801         this.fireEvent("add", this, records, index);
10802     },
10803
10804     /**
10805      * Remove a Record from the Store and fires the remove event.
10806      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10807      */
10808     remove : function(record){
10809         var index = this.data.indexOf(record);
10810         this.data.removeAt(index);
10811         if(this.pruneModifiedRecords){
10812             this.modified.remove(record);
10813         }
10814         this.fireEvent("remove", this, record, index);
10815     },
10816
10817     /**
10818      * Remove all Records from the Store and fires the clear event.
10819      */
10820     removeAll : function(){
10821         this.data.clear();
10822         if(this.pruneModifiedRecords){
10823             this.modified = [];
10824         }
10825         this.fireEvent("clear", this);
10826     },
10827
10828     /**
10829      * Inserts Records to the Store at the given index and fires the add event.
10830      * @param {Number} index The start index at which to insert the passed Records.
10831      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10832      */
10833     insert : function(index, records){
10834         records = [].concat(records);
10835         for(var i = 0, len = records.length; i < len; i++){
10836             this.data.insert(index, records[i]);
10837             records[i].join(this);
10838         }
10839         this.fireEvent("add", this, records, index);
10840     },
10841
10842     /**
10843      * Get the index within the cache of the passed Record.
10844      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10845      * @return {Number} The index of the passed Record. Returns -1 if not found.
10846      */
10847     indexOf : function(record){
10848         return this.data.indexOf(record);
10849     },
10850
10851     /**
10852      * Get the index within the cache of the Record with the passed id.
10853      * @param {String} id The id of the Record to find.
10854      * @return {Number} The index of the Record. Returns -1 if not found.
10855      */
10856     indexOfId : function(id){
10857         return this.data.indexOfKey(id);
10858     },
10859
10860     /**
10861      * Get the Record with the specified id.
10862      * @param {String} id The id of the Record to find.
10863      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10864      */
10865     getById : function(id){
10866         return this.data.key(id);
10867     },
10868
10869     /**
10870      * Get the Record at the specified index.
10871      * @param {Number} index The index of the Record to find.
10872      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10873      */
10874     getAt : function(index){
10875         return this.data.itemAt(index);
10876     },
10877
10878     /**
10879      * Returns a range of Records between specified indices.
10880      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10881      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10882      * @return {Roo.data.Record[]} An array of Records
10883      */
10884     getRange : function(start, end){
10885         return this.data.getRange(start, end);
10886     },
10887
10888     // private
10889     storeOptions : function(o){
10890         o = Roo.apply({}, o);
10891         delete o.callback;
10892         delete o.scope;
10893         this.lastOptions = o;
10894     },
10895
10896     /**
10897      * Loads the Record cache from the configured Proxy using the configured Reader.
10898      * <p>
10899      * If using remote paging, then the first load call must specify the <em>start</em>
10900      * and <em>limit</em> properties in the options.params property to establish the initial
10901      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10902      * <p>
10903      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10904      * and this call will return before the new data has been loaded. Perform any post-processing
10905      * in a callback function, or in a "load" event handler.</strong>
10906      * <p>
10907      * @param {Object} options An object containing properties which control loading options:<ul>
10908      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10909      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10910      * passed the following arguments:<ul>
10911      * <li>r : Roo.data.Record[]</li>
10912      * <li>options: Options object from the load call</li>
10913      * <li>success: Boolean success indicator</li></ul></li>
10914      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10915      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10916      * </ul>
10917      */
10918     load : function(options){
10919         options = options || {};
10920         if(this.fireEvent("beforeload", this, options) !== false){
10921             this.storeOptions(options);
10922             var p = Roo.apply(options.params || {}, this.baseParams);
10923             // if meta was not loaded from remote source.. try requesting it.
10924             if (!this.reader.metaFromRemote) {
10925                 p._requestMeta = 1;
10926             }
10927             if(this.sortInfo && this.remoteSort){
10928                 var pn = this.paramNames;
10929                 p[pn["sort"]] = this.sortInfo.field;
10930                 p[pn["dir"]] = this.sortInfo.direction;
10931             }
10932             if (this.multiSort) {
10933                 var pn = this.paramNames;
10934                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10935             }
10936             
10937             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10938         }
10939     },
10940
10941     /**
10942      * Reloads the Record cache from the configured Proxy using the configured Reader and
10943      * the options from the last load operation performed.
10944      * @param {Object} options (optional) An object containing properties which may override the options
10945      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10946      * the most recently used options are reused).
10947      */
10948     reload : function(options){
10949         this.load(Roo.applyIf(options||{}, this.lastOptions));
10950     },
10951
10952     // private
10953     // Called as a callback by the Reader during a load operation.
10954     loadRecords : function(o, options, success){
10955         if(!o || success === false){
10956             if(success !== false){
10957                 this.fireEvent("load", this, [], options, o);
10958             }
10959             if(options.callback){
10960                 options.callback.call(options.scope || this, [], options, false);
10961             }
10962             return;
10963         }
10964         // if data returned failure - throw an exception.
10965         if (o.success === false) {
10966             // show a message if no listener is registered.
10967             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10968                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10969             }
10970             // loadmask wil be hooked into this..
10971             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10972             return;
10973         }
10974         var r = o.records, t = o.totalRecords || r.length;
10975         
10976         this.fireEvent("beforeloadadd", this, r, options, o);
10977         
10978         if(!options || options.add !== true){
10979             if(this.pruneModifiedRecords){
10980                 this.modified = [];
10981             }
10982             for(var i = 0, len = r.length; i < len; i++){
10983                 r[i].join(this);
10984             }
10985             if(this.snapshot){
10986                 this.data = this.snapshot;
10987                 delete this.snapshot;
10988             }
10989             this.data.clear();
10990             this.data.addAll(r);
10991             this.totalLength = t;
10992             this.applySort();
10993             this.fireEvent("datachanged", this);
10994         }else{
10995             this.totalLength = Math.max(t, this.data.length+r.length);
10996             this.add(r);
10997         }
10998         this.fireEvent("load", this, r, options, o);
10999         if(options.callback){
11000             options.callback.call(options.scope || this, r, options, true);
11001         }
11002     },
11003
11004
11005     /**
11006      * Loads data from a passed data block. A Reader which understands the format of the data
11007      * must have been configured in the constructor.
11008      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11009      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11010      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11011      */
11012     loadData : function(o, append){
11013         var r = this.reader.readRecords(o);
11014         this.loadRecords(r, {add: append}, true);
11015     },
11016
11017     /**
11018      * Gets the number of cached records.
11019      * <p>
11020      * <em>If using paging, this may not be the total size of the dataset. If the data object
11021      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11022      * the data set size</em>
11023      */
11024     getCount : function(){
11025         return this.data.length || 0;
11026     },
11027
11028     /**
11029      * Gets the total number of records in the dataset as returned by the server.
11030      * <p>
11031      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11032      * the dataset size</em>
11033      */
11034     getTotalCount : function(){
11035         return this.totalLength || 0;
11036     },
11037
11038     /**
11039      * Returns the sort state of the Store as an object with two properties:
11040      * <pre><code>
11041  field {String} The name of the field by which the Records are sorted
11042  direction {String} The sort order, "ASC" or "DESC"
11043      * </code></pre>
11044      */
11045     getSortState : function(){
11046         return this.sortInfo;
11047     },
11048
11049     // private
11050     applySort : function(){
11051         if(this.sortInfo && !this.remoteSort){
11052             var s = this.sortInfo, f = s.field;
11053             var st = this.fields.get(f).sortType;
11054             var fn = function(r1, r2){
11055                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11056                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11057             };
11058             this.data.sort(s.direction, fn);
11059             if(this.snapshot && this.snapshot != this.data){
11060                 this.snapshot.sort(s.direction, fn);
11061             }
11062         }
11063     },
11064
11065     /**
11066      * Sets the default sort column and order to be used by the next load operation.
11067      * @param {String} fieldName The name of the field to sort by.
11068      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11069      */
11070     setDefaultSort : function(field, dir){
11071         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11072     },
11073
11074     /**
11075      * Sort the Records.
11076      * If remote sorting is used, the sort is performed on the server, and the cache is
11077      * reloaded. If local sorting is used, the cache is sorted internally.
11078      * @param {String} fieldName The name of the field to sort by.
11079      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11080      */
11081     sort : function(fieldName, dir){
11082         var f = this.fields.get(fieldName);
11083         if(!dir){
11084             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11085             
11086             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11087                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11088             }else{
11089                 dir = f.sortDir;
11090             }
11091         }
11092         this.sortToggle[f.name] = dir;
11093         this.sortInfo = {field: f.name, direction: dir};
11094         if(!this.remoteSort){
11095             this.applySort();
11096             this.fireEvent("datachanged", this);
11097         }else{
11098             this.load(this.lastOptions);
11099         }
11100     },
11101
11102     /**
11103      * Calls the specified function for each of the Records in the cache.
11104      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11105      * Returning <em>false</em> aborts and exits the iteration.
11106      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11107      */
11108     each : function(fn, scope){
11109         this.data.each(fn, scope);
11110     },
11111
11112     /**
11113      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11114      * (e.g., during paging).
11115      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11116      */
11117     getModifiedRecords : function(){
11118         return this.modified;
11119     },
11120
11121     // private
11122     createFilterFn : function(property, value, anyMatch){
11123         if(!value.exec){ // not a regex
11124             value = String(value);
11125             if(value.length == 0){
11126                 return false;
11127             }
11128             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11129         }
11130         return function(r){
11131             return value.test(r.data[property]);
11132         };
11133     },
11134
11135     /**
11136      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11137      * @param {String} property A field on your records
11138      * @param {Number} start The record index to start at (defaults to 0)
11139      * @param {Number} end The last record index to include (defaults to length - 1)
11140      * @return {Number} The sum
11141      */
11142     sum : function(property, start, end){
11143         var rs = this.data.items, v = 0;
11144         start = start || 0;
11145         end = (end || end === 0) ? end : rs.length-1;
11146
11147         for(var i = start; i <= end; i++){
11148             v += (rs[i].data[property] || 0);
11149         }
11150         return v;
11151     },
11152
11153     /**
11154      * Filter the records by a specified property.
11155      * @param {String} field A field on your records
11156      * @param {String/RegExp} value Either a string that the field
11157      * should start with or a RegExp to test against the field
11158      * @param {Boolean} anyMatch True to match any part not just the beginning
11159      */
11160     filter : function(property, value, anyMatch){
11161         var fn = this.createFilterFn(property, value, anyMatch);
11162         return fn ? this.filterBy(fn) : this.clearFilter();
11163     },
11164
11165     /**
11166      * Filter by a function. The specified function will be called with each
11167      * record in this data source. If the function returns true the record is included,
11168      * otherwise it is filtered.
11169      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11170      * @param {Object} scope (optional) The scope of the function (defaults to this)
11171      */
11172     filterBy : function(fn, scope){
11173         this.snapshot = this.snapshot || this.data;
11174         this.data = this.queryBy(fn, scope||this);
11175         this.fireEvent("datachanged", this);
11176     },
11177
11178     /**
11179      * Query the records by a specified property.
11180      * @param {String} field A field on your records
11181      * @param {String/RegExp} value Either a string that the field
11182      * should start with or a RegExp to test against the field
11183      * @param {Boolean} anyMatch True to match any part not just the beginning
11184      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11185      */
11186     query : function(property, value, anyMatch){
11187         var fn = this.createFilterFn(property, value, anyMatch);
11188         return fn ? this.queryBy(fn) : this.data.clone();
11189     },
11190
11191     /**
11192      * Query by a function. The specified function will be called with each
11193      * record in this data source. If the function returns true the record is included
11194      * in the results.
11195      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11196      * @param {Object} scope (optional) The scope of the function (defaults to this)
11197       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11198      **/
11199     queryBy : function(fn, scope){
11200         var data = this.snapshot || this.data;
11201         return data.filterBy(fn, scope||this);
11202     },
11203
11204     /**
11205      * Collects unique values for a particular dataIndex from this store.
11206      * @param {String} dataIndex The property to collect
11207      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11208      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11209      * @return {Array} An array of the unique values
11210      **/
11211     collect : function(dataIndex, allowNull, bypassFilter){
11212         var d = (bypassFilter === true && this.snapshot) ?
11213                 this.snapshot.items : this.data.items;
11214         var v, sv, r = [], l = {};
11215         for(var i = 0, len = d.length; i < len; i++){
11216             v = d[i].data[dataIndex];
11217             sv = String(v);
11218             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11219                 l[sv] = true;
11220                 r[r.length] = v;
11221             }
11222         }
11223         return r;
11224     },
11225
11226     /**
11227      * Revert to a view of the Record cache with no filtering applied.
11228      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11229      */
11230     clearFilter : function(suppressEvent){
11231         if(this.snapshot && this.snapshot != this.data){
11232             this.data = this.snapshot;
11233             delete this.snapshot;
11234             if(suppressEvent !== true){
11235                 this.fireEvent("datachanged", this);
11236             }
11237         }
11238     },
11239
11240     // private
11241     afterEdit : function(record){
11242         if(this.modified.indexOf(record) == -1){
11243             this.modified.push(record);
11244         }
11245         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11246     },
11247     
11248     // private
11249     afterReject : function(record){
11250         this.modified.remove(record);
11251         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11252     },
11253
11254     // private
11255     afterCommit : function(record){
11256         this.modified.remove(record);
11257         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11258     },
11259
11260     /**
11261      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11262      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11263      */
11264     commitChanges : function(){
11265         var m = this.modified.slice(0);
11266         this.modified = [];
11267         for(var i = 0, len = m.length; i < len; i++){
11268             m[i].commit();
11269         }
11270     },
11271
11272     /**
11273      * Cancel outstanding changes on all changed records.
11274      */
11275     rejectChanges : function(){
11276         var m = this.modified.slice(0);
11277         this.modified = [];
11278         for(var i = 0, len = m.length; i < len; i++){
11279             m[i].reject();
11280         }
11281     },
11282
11283     onMetaChange : function(meta, rtype, o){
11284         this.recordType = rtype;
11285         this.fields = rtype.prototype.fields;
11286         delete this.snapshot;
11287         this.sortInfo = meta.sortInfo || this.sortInfo;
11288         this.modified = [];
11289         this.fireEvent('metachange', this, this.reader.meta);
11290     },
11291     
11292     moveIndex : function(data, type)
11293     {
11294         var index = this.indexOf(data);
11295         
11296         var newIndex = index + type;
11297         
11298         this.remove(data);
11299         
11300         this.insert(newIndex, data);
11301         
11302     }
11303 });/*
11304  * Based on:
11305  * Ext JS Library 1.1.1
11306  * Copyright(c) 2006-2007, Ext JS, LLC.
11307  *
11308  * Originally Released Under LGPL - original licence link has changed is not relivant.
11309  *
11310  * Fork - LGPL
11311  * <script type="text/javascript">
11312  */
11313
11314 /**
11315  * @class Roo.data.SimpleStore
11316  * @extends Roo.data.Store
11317  * Small helper class to make creating Stores from Array data easier.
11318  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11319  * @cfg {Array} fields An array of field definition objects, or field name strings.
11320  * @cfg {Array} data The multi-dimensional array of data
11321  * @constructor
11322  * @param {Object} config
11323  */
11324 Roo.data.SimpleStore = function(config){
11325     Roo.data.SimpleStore.superclass.constructor.call(this, {
11326         isLocal : true,
11327         reader: new Roo.data.ArrayReader({
11328                 id: config.id
11329             },
11330             Roo.data.Record.create(config.fields)
11331         ),
11332         proxy : new Roo.data.MemoryProxy(config.data)
11333     });
11334     this.load();
11335 };
11336 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11337  * Based on:
11338  * Ext JS Library 1.1.1
11339  * Copyright(c) 2006-2007, Ext JS, LLC.
11340  *
11341  * Originally Released Under LGPL - original licence link has changed is not relivant.
11342  *
11343  * Fork - LGPL
11344  * <script type="text/javascript">
11345  */
11346
11347 /**
11348 /**
11349  * @extends Roo.data.Store
11350  * @class Roo.data.JsonStore
11351  * Small helper class to make creating Stores for JSON data easier. <br/>
11352 <pre><code>
11353 var store = new Roo.data.JsonStore({
11354     url: 'get-images.php',
11355     root: 'images',
11356     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11357 });
11358 </code></pre>
11359  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11360  * JsonReader and HttpProxy (unless inline data is provided).</b>
11361  * @cfg {Array} fields An array of field definition objects, or field name strings.
11362  * @constructor
11363  * @param {Object} config
11364  */
11365 Roo.data.JsonStore = function(c){
11366     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11367         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11368         reader: new Roo.data.JsonReader(c, c.fields)
11369     }));
11370 };
11371 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11372  * Based on:
11373  * Ext JS Library 1.1.1
11374  * Copyright(c) 2006-2007, Ext JS, LLC.
11375  *
11376  * Originally Released Under LGPL - original licence link has changed is not relivant.
11377  *
11378  * Fork - LGPL
11379  * <script type="text/javascript">
11380  */
11381
11382  
11383 Roo.data.Field = function(config){
11384     if(typeof config == "string"){
11385         config = {name: config};
11386     }
11387     Roo.apply(this, config);
11388     
11389     if(!this.type){
11390         this.type = "auto";
11391     }
11392     
11393     var st = Roo.data.SortTypes;
11394     // named sortTypes are supported, here we look them up
11395     if(typeof this.sortType == "string"){
11396         this.sortType = st[this.sortType];
11397     }
11398     
11399     // set default sortType for strings and dates
11400     if(!this.sortType){
11401         switch(this.type){
11402             case "string":
11403                 this.sortType = st.asUCString;
11404                 break;
11405             case "date":
11406                 this.sortType = st.asDate;
11407                 break;
11408             default:
11409                 this.sortType = st.none;
11410         }
11411     }
11412
11413     // define once
11414     var stripRe = /[\$,%]/g;
11415
11416     // prebuilt conversion function for this field, instead of
11417     // switching every time we're reading a value
11418     if(!this.convert){
11419         var cv, dateFormat = this.dateFormat;
11420         switch(this.type){
11421             case "":
11422             case "auto":
11423             case undefined:
11424                 cv = function(v){ return v; };
11425                 break;
11426             case "string":
11427                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11428                 break;
11429             case "int":
11430                 cv = function(v){
11431                     return v !== undefined && v !== null && v !== '' ?
11432                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11433                     };
11434                 break;
11435             case "float":
11436                 cv = function(v){
11437                     return v !== undefined && v !== null && v !== '' ?
11438                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11439                     };
11440                 break;
11441             case "bool":
11442             case "boolean":
11443                 cv = function(v){ return v === true || v === "true" || v == 1; };
11444                 break;
11445             case "date":
11446                 cv = function(v){
11447                     if(!v){
11448                         return '';
11449                     }
11450                     if(v instanceof Date){
11451                         return v;
11452                     }
11453                     if(dateFormat){
11454                         if(dateFormat == "timestamp"){
11455                             return new Date(v*1000);
11456                         }
11457                         return Date.parseDate(v, dateFormat);
11458                     }
11459                     var parsed = Date.parse(v);
11460                     return parsed ? new Date(parsed) : null;
11461                 };
11462              break;
11463             
11464         }
11465         this.convert = cv;
11466     }
11467 };
11468
11469 Roo.data.Field.prototype = {
11470     dateFormat: null,
11471     defaultValue: "",
11472     mapping: null,
11473     sortType : null,
11474     sortDir : "ASC"
11475 };/*
11476  * Based on:
11477  * Ext JS Library 1.1.1
11478  * Copyright(c) 2006-2007, Ext JS, LLC.
11479  *
11480  * Originally Released Under LGPL - original licence link has changed is not relivant.
11481  *
11482  * Fork - LGPL
11483  * <script type="text/javascript">
11484  */
11485  
11486 // Base class for reading structured data from a data source.  This class is intended to be
11487 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11488
11489 /**
11490  * @class Roo.data.DataReader
11491  * Base class for reading structured data from a data source.  This class is intended to be
11492  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11493  */
11494
11495 Roo.data.DataReader = function(meta, recordType){
11496     
11497     this.meta = meta;
11498     
11499     this.recordType = recordType instanceof Array ? 
11500         Roo.data.Record.create(recordType) : recordType;
11501 };
11502
11503 Roo.data.DataReader.prototype = {
11504      /**
11505      * Create an empty record
11506      * @param {Object} data (optional) - overlay some values
11507      * @return {Roo.data.Record} record created.
11508      */
11509     newRow :  function(d) {
11510         var da =  {};
11511         this.recordType.prototype.fields.each(function(c) {
11512             switch( c.type) {
11513                 case 'int' : da[c.name] = 0; break;
11514                 case 'date' : da[c.name] = new Date(); break;
11515                 case 'float' : da[c.name] = 0.0; break;
11516                 case 'boolean' : da[c.name] = false; break;
11517                 default : da[c.name] = ""; break;
11518             }
11519             
11520         });
11521         return new this.recordType(Roo.apply(da, d));
11522     }
11523     
11524 };/*
11525  * Based on:
11526  * Ext JS Library 1.1.1
11527  * Copyright(c) 2006-2007, Ext JS, LLC.
11528  *
11529  * Originally Released Under LGPL - original licence link has changed is not relivant.
11530  *
11531  * Fork - LGPL
11532  * <script type="text/javascript">
11533  */
11534
11535 /**
11536  * @class Roo.data.DataProxy
11537  * @extends Roo.data.Observable
11538  * This class is an abstract base class for implementations which provide retrieval of
11539  * unformatted data objects.<br>
11540  * <p>
11541  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11542  * (of the appropriate type which knows how to parse the data object) to provide a block of
11543  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11544  * <p>
11545  * Custom implementations must implement the load method as described in
11546  * {@link Roo.data.HttpProxy#load}.
11547  */
11548 Roo.data.DataProxy = function(){
11549     this.addEvents({
11550         /**
11551          * @event beforeload
11552          * Fires before a network request is made to retrieve a data object.
11553          * @param {Object} This DataProxy object.
11554          * @param {Object} params The params parameter to the load function.
11555          */
11556         beforeload : true,
11557         /**
11558          * @event load
11559          * Fires before the load method's callback is called.
11560          * @param {Object} This DataProxy object.
11561          * @param {Object} o The data object.
11562          * @param {Object} arg The callback argument object passed to the load function.
11563          */
11564         load : true,
11565         /**
11566          * @event loadexception
11567          * Fires if an Exception occurs during data retrieval.
11568          * @param {Object} This DataProxy object.
11569          * @param {Object} o The data object.
11570          * @param {Object} arg The callback argument object passed to the load function.
11571          * @param {Object} e The Exception.
11572          */
11573         loadexception : true
11574     });
11575     Roo.data.DataProxy.superclass.constructor.call(this);
11576 };
11577
11578 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11579
11580     /**
11581      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11582      */
11583 /*
11584  * Based on:
11585  * Ext JS Library 1.1.1
11586  * Copyright(c) 2006-2007, Ext JS, LLC.
11587  *
11588  * Originally Released Under LGPL - original licence link has changed is not relivant.
11589  *
11590  * Fork - LGPL
11591  * <script type="text/javascript">
11592  */
11593 /**
11594  * @class Roo.data.MemoryProxy
11595  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11596  * to the Reader when its load method is called.
11597  * @constructor
11598  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11599  */
11600 Roo.data.MemoryProxy = function(data){
11601     if (data.data) {
11602         data = data.data;
11603     }
11604     Roo.data.MemoryProxy.superclass.constructor.call(this);
11605     this.data = data;
11606 };
11607
11608 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11609     
11610     /**
11611      * Load data from the requested source (in this case an in-memory
11612      * data object passed to the constructor), read the data object into
11613      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11614      * process that block using the passed callback.
11615      * @param {Object} params This parameter is not used by the MemoryProxy class.
11616      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11617      * object into a block of Roo.data.Records.
11618      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11619      * The function must be passed <ul>
11620      * <li>The Record block object</li>
11621      * <li>The "arg" argument from the load function</li>
11622      * <li>A boolean success indicator</li>
11623      * </ul>
11624      * @param {Object} scope The scope in which to call the callback
11625      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11626      */
11627     load : function(params, reader, callback, scope, arg){
11628         params = params || {};
11629         var result;
11630         try {
11631             result = reader.readRecords(this.data);
11632         }catch(e){
11633             this.fireEvent("loadexception", this, arg, null, e);
11634             callback.call(scope, null, arg, false);
11635             return;
11636         }
11637         callback.call(scope, result, arg, true);
11638     },
11639     
11640     // private
11641     update : function(params, records){
11642         
11643     }
11644 });/*
11645  * Based on:
11646  * Ext JS Library 1.1.1
11647  * Copyright(c) 2006-2007, Ext JS, LLC.
11648  *
11649  * Originally Released Under LGPL - original licence link has changed is not relivant.
11650  *
11651  * Fork - LGPL
11652  * <script type="text/javascript">
11653  */
11654 /**
11655  * @class Roo.data.HttpProxy
11656  * @extends Roo.data.DataProxy
11657  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11658  * configured to reference a certain URL.<br><br>
11659  * <p>
11660  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11661  * from which the running page was served.<br><br>
11662  * <p>
11663  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11664  * <p>
11665  * Be aware that to enable the browser to parse an XML document, the server must set
11666  * the Content-Type header in the HTTP response to "text/xml".
11667  * @constructor
11668  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11669  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11670  * will be used to make the request.
11671  */
11672 Roo.data.HttpProxy = function(conn){
11673     Roo.data.HttpProxy.superclass.constructor.call(this);
11674     // is conn a conn config or a real conn?
11675     this.conn = conn;
11676     this.useAjax = !conn || !conn.events;
11677   
11678 };
11679
11680 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11681     // thse are take from connection...
11682     
11683     /**
11684      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11685      */
11686     /**
11687      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11688      * extra parameters to each request made by this object. (defaults to undefined)
11689      */
11690     /**
11691      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11692      *  to each request made by this object. (defaults to undefined)
11693      */
11694     /**
11695      * @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)
11696      */
11697     /**
11698      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11699      */
11700      /**
11701      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11702      * @type Boolean
11703      */
11704   
11705
11706     /**
11707      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11708      * @type Boolean
11709      */
11710     /**
11711      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11712      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11713      * a finer-grained basis than the DataProxy events.
11714      */
11715     getConnection : function(){
11716         return this.useAjax ? Roo.Ajax : this.conn;
11717     },
11718
11719     /**
11720      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11721      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11722      * process that block using the passed callback.
11723      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11724      * for the request to the remote server.
11725      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11726      * object into a block of Roo.data.Records.
11727      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11728      * The function must be passed <ul>
11729      * <li>The Record block object</li>
11730      * <li>The "arg" argument from the load function</li>
11731      * <li>A boolean success indicator</li>
11732      * </ul>
11733      * @param {Object} scope The scope in which to call the callback
11734      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11735      */
11736     load : function(params, reader, callback, scope, arg){
11737         if(this.fireEvent("beforeload", this, params) !== false){
11738             var  o = {
11739                 params : params || {},
11740                 request: {
11741                     callback : callback,
11742                     scope : scope,
11743                     arg : arg
11744                 },
11745                 reader: reader,
11746                 callback : this.loadResponse,
11747                 scope: this
11748             };
11749             if(this.useAjax){
11750                 Roo.applyIf(o, this.conn);
11751                 if(this.activeRequest){
11752                     Roo.Ajax.abort(this.activeRequest);
11753                 }
11754                 this.activeRequest = Roo.Ajax.request(o);
11755             }else{
11756                 this.conn.request(o);
11757             }
11758         }else{
11759             callback.call(scope||this, null, arg, false);
11760         }
11761     },
11762
11763     // private
11764     loadResponse : function(o, success, response){
11765         delete this.activeRequest;
11766         if(!success){
11767             this.fireEvent("loadexception", this, o, response);
11768             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11769             return;
11770         }
11771         var result;
11772         try {
11773             result = o.reader.read(response);
11774         }catch(e){
11775             this.fireEvent("loadexception", this, o, response, e);
11776             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11777             return;
11778         }
11779         
11780         this.fireEvent("load", this, o, o.request.arg);
11781         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11782     },
11783
11784     // private
11785     update : function(dataSet){
11786
11787     },
11788
11789     // private
11790     updateResponse : function(dataSet){
11791
11792     }
11793 });/*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803
11804 /**
11805  * @class Roo.data.ScriptTagProxy
11806  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11807  * other than the originating domain of the running page.<br><br>
11808  * <p>
11809  * <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
11810  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11811  * <p>
11812  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11813  * source code that is used as the source inside a &lt;script> tag.<br><br>
11814  * <p>
11815  * In order for the browser to process the returned data, the server must wrap the data object
11816  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11817  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11818  * depending on whether the callback name was passed:
11819  * <p>
11820  * <pre><code>
11821 boolean scriptTag = false;
11822 String cb = request.getParameter("callback");
11823 if (cb != null) {
11824     scriptTag = true;
11825     response.setContentType("text/javascript");
11826 } else {
11827     response.setContentType("application/x-json");
11828 }
11829 Writer out = response.getWriter();
11830 if (scriptTag) {
11831     out.write(cb + "(");
11832 }
11833 out.print(dataBlock.toJsonString());
11834 if (scriptTag) {
11835     out.write(");");
11836 }
11837 </pre></code>
11838  *
11839  * @constructor
11840  * @param {Object} config A configuration object.
11841  */
11842 Roo.data.ScriptTagProxy = function(config){
11843     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11844     Roo.apply(this, config);
11845     this.head = document.getElementsByTagName("head")[0];
11846 };
11847
11848 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11849
11850 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11851     /**
11852      * @cfg {String} url The URL from which to request the data object.
11853      */
11854     /**
11855      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11856      */
11857     timeout : 30000,
11858     /**
11859      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11860      * the server the name of the callback function set up by the load call to process the returned data object.
11861      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11862      * javascript output which calls this named function passing the data object as its only parameter.
11863      */
11864     callbackParam : "callback",
11865     /**
11866      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11867      * name to the request.
11868      */
11869     nocache : true,
11870
11871     /**
11872      * Load data from the configured URL, read the data object into
11873      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11874      * process that block using the passed callback.
11875      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11876      * for the request to the remote server.
11877      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11878      * object into a block of Roo.data.Records.
11879      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11880      * The function must be passed <ul>
11881      * <li>The Record block object</li>
11882      * <li>The "arg" argument from the load function</li>
11883      * <li>A boolean success indicator</li>
11884      * </ul>
11885      * @param {Object} scope The scope in which to call the callback
11886      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11887      */
11888     load : function(params, reader, callback, scope, arg){
11889         if(this.fireEvent("beforeload", this, params) !== false){
11890
11891             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11892
11893             var url = this.url;
11894             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11895             if(this.nocache){
11896                 url += "&_dc=" + (new Date().getTime());
11897             }
11898             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11899             var trans = {
11900                 id : transId,
11901                 cb : "stcCallback"+transId,
11902                 scriptId : "stcScript"+transId,
11903                 params : params,
11904                 arg : arg,
11905                 url : url,
11906                 callback : callback,
11907                 scope : scope,
11908                 reader : reader
11909             };
11910             var conn = this;
11911
11912             window[trans.cb] = function(o){
11913                 conn.handleResponse(o, trans);
11914             };
11915
11916             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11917
11918             if(this.autoAbort !== false){
11919                 this.abort();
11920             }
11921
11922             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11923
11924             var script = document.createElement("script");
11925             script.setAttribute("src", url);
11926             script.setAttribute("type", "text/javascript");
11927             script.setAttribute("id", trans.scriptId);
11928             this.head.appendChild(script);
11929
11930             this.trans = trans;
11931         }else{
11932             callback.call(scope||this, null, arg, false);
11933         }
11934     },
11935
11936     // private
11937     isLoading : function(){
11938         return this.trans ? true : false;
11939     },
11940
11941     /**
11942      * Abort the current server request.
11943      */
11944     abort : function(){
11945         if(this.isLoading()){
11946             this.destroyTrans(this.trans);
11947         }
11948     },
11949
11950     // private
11951     destroyTrans : function(trans, isLoaded){
11952         this.head.removeChild(document.getElementById(trans.scriptId));
11953         clearTimeout(trans.timeoutId);
11954         if(isLoaded){
11955             window[trans.cb] = undefined;
11956             try{
11957                 delete window[trans.cb];
11958             }catch(e){}
11959         }else{
11960             // if hasn't been loaded, wait for load to remove it to prevent script error
11961             window[trans.cb] = function(){
11962                 window[trans.cb] = undefined;
11963                 try{
11964                     delete window[trans.cb];
11965                 }catch(e){}
11966             };
11967         }
11968     },
11969
11970     // private
11971     handleResponse : function(o, trans){
11972         this.trans = false;
11973         this.destroyTrans(trans, true);
11974         var result;
11975         try {
11976             result = trans.reader.readRecords(o);
11977         }catch(e){
11978             this.fireEvent("loadexception", this, o, trans.arg, e);
11979             trans.callback.call(trans.scope||window, null, trans.arg, false);
11980             return;
11981         }
11982         this.fireEvent("load", this, o, trans.arg);
11983         trans.callback.call(trans.scope||window, result, trans.arg, true);
11984     },
11985
11986     // private
11987     handleFailure : function(trans){
11988         this.trans = false;
11989         this.destroyTrans(trans, false);
11990         this.fireEvent("loadexception", this, null, trans.arg);
11991         trans.callback.call(trans.scope||window, null, trans.arg, false);
11992     }
11993 });/*
11994  * Based on:
11995  * Ext JS Library 1.1.1
11996  * Copyright(c) 2006-2007, Ext JS, LLC.
11997  *
11998  * Originally Released Under LGPL - original licence link has changed is not relivant.
11999  *
12000  * Fork - LGPL
12001  * <script type="text/javascript">
12002  */
12003
12004 /**
12005  * @class Roo.data.JsonReader
12006  * @extends Roo.data.DataReader
12007  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12008  * based on mappings in a provided Roo.data.Record constructor.
12009  * 
12010  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12011  * in the reply previously. 
12012  * 
12013  * <p>
12014  * Example code:
12015  * <pre><code>
12016 var RecordDef = Roo.data.Record.create([
12017     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12018     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12019 ]);
12020 var myReader = new Roo.data.JsonReader({
12021     totalProperty: "results",    // The property which contains the total dataset size (optional)
12022     root: "rows",                // The property which contains an Array of row objects
12023     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12024 }, RecordDef);
12025 </code></pre>
12026  * <p>
12027  * This would consume a JSON file like this:
12028  * <pre><code>
12029 { 'results': 2, 'rows': [
12030     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12031     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12032 }
12033 </code></pre>
12034  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12035  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12036  * paged from the remote server.
12037  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12038  * @cfg {String} root name of the property which contains the Array of row objects.
12039  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12040  * @cfg {Array} fields Array of field definition objects
12041  * @constructor
12042  * Create a new JsonReader
12043  * @param {Object} meta Metadata configuration options
12044  * @param {Object} recordType Either an Array of field definition objects,
12045  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12046  */
12047 Roo.data.JsonReader = function(meta, recordType){
12048     
12049     meta = meta || {};
12050     // set some defaults:
12051     Roo.applyIf(meta, {
12052         totalProperty: 'total',
12053         successProperty : 'success',
12054         root : 'data',
12055         id : 'id'
12056     });
12057     
12058     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12059 };
12060 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12061     
12062     /**
12063      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12064      * Used by Store query builder to append _requestMeta to params.
12065      * 
12066      */
12067     metaFromRemote : false,
12068     /**
12069      * This method is only used by a DataProxy which has retrieved data from a remote server.
12070      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12071      * @return {Object} data A data block which is used by an Roo.data.Store object as
12072      * a cache of Roo.data.Records.
12073      */
12074     read : function(response){
12075         var json = response.responseText;
12076        
12077         var o = /* eval:var:o */ eval("("+json+")");
12078         if(!o) {
12079             throw {message: "JsonReader.read: Json object not found"};
12080         }
12081         
12082         if(o.metaData){
12083             
12084             delete this.ef;
12085             this.metaFromRemote = true;
12086             this.meta = o.metaData;
12087             this.recordType = Roo.data.Record.create(o.metaData.fields);
12088             this.onMetaChange(this.meta, this.recordType, o);
12089         }
12090         return this.readRecords(o);
12091     },
12092
12093     // private function a store will implement
12094     onMetaChange : function(meta, recordType, o){
12095
12096     },
12097
12098     /**
12099          * @ignore
12100          */
12101     simpleAccess: function(obj, subsc) {
12102         return obj[subsc];
12103     },
12104
12105         /**
12106          * @ignore
12107          */
12108     getJsonAccessor: function(){
12109         var re = /[\[\.]/;
12110         return function(expr) {
12111             try {
12112                 return(re.test(expr))
12113                     ? new Function("obj", "return obj." + expr)
12114                     : function(obj){
12115                         return obj[expr];
12116                     };
12117             } catch(e){}
12118             return Roo.emptyFn;
12119         };
12120     }(),
12121
12122     /**
12123      * Create a data block containing Roo.data.Records from an XML document.
12124      * @param {Object} o An object which contains an Array of row objects in the property specified
12125      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12126      * which contains the total size of the dataset.
12127      * @return {Object} data A data block which is used by an Roo.data.Store object as
12128      * a cache of Roo.data.Records.
12129      */
12130     readRecords : function(o){
12131         /**
12132          * After any data loads, the raw JSON data is available for further custom processing.
12133          * @type Object
12134          */
12135         this.o = o;
12136         var s = this.meta, Record = this.recordType,
12137             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12138
12139 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12140         if (!this.ef) {
12141             if(s.totalProperty) {
12142                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12143                 }
12144                 if(s.successProperty) {
12145                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12146                 }
12147                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12148                 if (s.id) {
12149                         var g = this.getJsonAccessor(s.id);
12150                         this.getId = function(rec) {
12151                                 var r = g(rec);  
12152                                 return (r === undefined || r === "") ? null : r;
12153                         };
12154                 } else {
12155                         this.getId = function(){return null;};
12156                 }
12157             this.ef = [];
12158             for(var jj = 0; jj < fl; jj++){
12159                 f = fi[jj];
12160                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12161                 this.ef[jj] = this.getJsonAccessor(map);
12162             }
12163         }
12164
12165         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12166         if(s.totalProperty){
12167             var vt = parseInt(this.getTotal(o), 10);
12168             if(!isNaN(vt)){
12169                 totalRecords = vt;
12170             }
12171         }
12172         if(s.successProperty){
12173             var vs = this.getSuccess(o);
12174             if(vs === false || vs === 'false'){
12175                 success = false;
12176             }
12177         }
12178         var records = [];
12179         for(var i = 0; i < c; i++){
12180                 var n = root[i];
12181             var values = {};
12182             var id = this.getId(n);
12183             for(var j = 0; j < fl; j++){
12184                 f = fi[j];
12185             var v = this.ef[j](n);
12186             if (!f.convert) {
12187                 Roo.log('missing convert for ' + f.name);
12188                 Roo.log(f);
12189                 continue;
12190             }
12191             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12192             }
12193             var record = new Record(values, id);
12194             record.json = n;
12195             records[i] = record;
12196         }
12197         return {
12198             raw : o,
12199             success : success,
12200             records : records,
12201             totalRecords : totalRecords
12202         };
12203     }
12204 });/*
12205  * Based on:
12206  * Ext JS Library 1.1.1
12207  * Copyright(c) 2006-2007, Ext JS, LLC.
12208  *
12209  * Originally Released Under LGPL - original licence link has changed is not relivant.
12210  *
12211  * Fork - LGPL
12212  * <script type="text/javascript">
12213  */
12214
12215 /**
12216  * @class Roo.data.ArrayReader
12217  * @extends Roo.data.DataReader
12218  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12219  * Each element of that Array represents a row of data fields. The
12220  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12221  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12222  * <p>
12223  * Example code:.
12224  * <pre><code>
12225 var RecordDef = Roo.data.Record.create([
12226     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12227     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12228 ]);
12229 var myReader = new Roo.data.ArrayReader({
12230     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12231 }, RecordDef);
12232 </code></pre>
12233  * <p>
12234  * This would consume an Array like this:
12235  * <pre><code>
12236 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12237   </code></pre>
12238  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12239  * @constructor
12240  * Create a new JsonReader
12241  * @param {Object} meta Metadata configuration options.
12242  * @param {Object} recordType Either an Array of field definition objects
12243  * as specified to {@link Roo.data.Record#create},
12244  * or an {@link Roo.data.Record} object
12245  * created using {@link Roo.data.Record#create}.
12246  */
12247 Roo.data.ArrayReader = function(meta, recordType){
12248     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12249 };
12250
12251 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12252     /**
12253      * Create a data block containing Roo.data.Records from an XML document.
12254      * @param {Object} o An Array of row objects which represents the dataset.
12255      * @return {Object} data A data block which is used by an Roo.data.Store object as
12256      * a cache of Roo.data.Records.
12257      */
12258     readRecords : function(o){
12259         var sid = this.meta ? this.meta.id : null;
12260         var recordType = this.recordType, fields = recordType.prototype.fields;
12261         var records = [];
12262         var root = o;
12263             for(var i = 0; i < root.length; i++){
12264                     var n = root[i];
12265                 var values = {};
12266                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12267                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12268                 var f = fields.items[j];
12269                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12270                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12271                 v = f.convert(v);
12272                 values[f.name] = v;
12273             }
12274                 var record = new recordType(values, id);
12275                 record.json = n;
12276                 records[records.length] = record;
12277             }
12278             return {
12279                 records : records,
12280                 totalRecords : records.length
12281             };
12282     }
12283 });/*
12284  * - LGPL
12285  * * 
12286  */
12287
12288 /**
12289  * @class Roo.bootstrap.ComboBox
12290  * @extends Roo.bootstrap.TriggerField
12291  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12292  * @cfg {Boolean} append (true|false) default false
12293  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12294  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12295  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12296  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12297  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12298  * @cfg {Boolean} animate default true
12299  * @cfg {Boolean} emptyResultText only for touch device
12300  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12301  * @constructor
12302  * Create a new ComboBox.
12303  * @param {Object} config Configuration options
12304  */
12305 Roo.bootstrap.ComboBox = function(config){
12306     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12307     this.addEvents({
12308         /**
12309          * @event expand
12310          * Fires when the dropdown list is expanded
12311              * @param {Roo.bootstrap.ComboBox} combo This combo box
12312              */
12313         'expand' : true,
12314         /**
12315          * @event collapse
12316          * Fires when the dropdown list is collapsed
12317              * @param {Roo.bootstrap.ComboBox} combo This combo box
12318              */
12319         'collapse' : true,
12320         /**
12321          * @event beforeselect
12322          * Fires before a list item is selected. Return false to cancel the selection.
12323              * @param {Roo.bootstrap.ComboBox} combo This combo box
12324              * @param {Roo.data.Record} record The data record returned from the underlying store
12325              * @param {Number} index The index of the selected item in the dropdown list
12326              */
12327         'beforeselect' : true,
12328         /**
12329          * @event select
12330          * Fires when a list item is selected
12331              * @param {Roo.bootstrap.ComboBox} combo This combo box
12332              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12333              * @param {Number} index The index of the selected item in the dropdown list
12334              */
12335         'select' : true,
12336         /**
12337          * @event beforequery
12338          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12339          * The event object passed has these properties:
12340              * @param {Roo.bootstrap.ComboBox} combo This combo box
12341              * @param {String} query The query
12342              * @param {Boolean} forceAll true to force "all" query
12343              * @param {Boolean} cancel true to cancel the query
12344              * @param {Object} e The query event object
12345              */
12346         'beforequery': true,
12347          /**
12348          * @event add
12349          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12350              * @param {Roo.bootstrap.ComboBox} combo This combo box
12351              */
12352         'add' : true,
12353         /**
12354          * @event edit
12355          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12356              * @param {Roo.bootstrap.ComboBox} combo This combo box
12357              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12358              */
12359         'edit' : true,
12360         /**
12361          * @event remove
12362          * Fires when the remove value from the combobox array
12363              * @param {Roo.bootstrap.ComboBox} combo This combo box
12364              */
12365         'remove' : true,
12366         /**
12367          * @event afterremove
12368          * Fires when the remove value from the combobox array
12369              * @param {Roo.bootstrap.ComboBox} combo This combo box
12370              */
12371         'afterremove' : true,
12372         /**
12373          * @event specialfilter
12374          * Fires when specialfilter
12375             * @param {Roo.bootstrap.ComboBox} combo This combo box
12376             */
12377         'specialfilter' : true,
12378         /**
12379          * @event tick
12380          * Fires when tick the element
12381             * @param {Roo.bootstrap.ComboBox} combo This combo box
12382             */
12383         'tick' : true,
12384         /**
12385          * @event touchviewdisplay
12386          * Fires when touch view require special display (default is using displayField)
12387             * @param {Roo.bootstrap.ComboBox} combo This combo box
12388             * @param {Object} cfg set html .
12389             */
12390         'touchviewdisplay' : true
12391         
12392     });
12393     
12394     this.item = [];
12395     this.tickItems = [];
12396     
12397     this.selectedIndex = -1;
12398     if(this.mode == 'local'){
12399         if(config.queryDelay === undefined){
12400             this.queryDelay = 10;
12401         }
12402         if(config.minChars === undefined){
12403             this.minChars = 0;
12404         }
12405     }
12406 };
12407
12408 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12409      
12410     /**
12411      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12412      * rendering into an Roo.Editor, defaults to false)
12413      */
12414     /**
12415      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12416      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12417      */
12418     /**
12419      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12420      */
12421     /**
12422      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12423      * the dropdown list (defaults to undefined, with no header element)
12424      */
12425
12426      /**
12427      * @cfg {String/Roo.Template} tpl The template to use to render the output
12428      */
12429      
12430      /**
12431      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12432      */
12433     listWidth: undefined,
12434     /**
12435      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12436      * mode = 'remote' or 'text' if mode = 'local')
12437      */
12438     displayField: undefined,
12439     
12440     /**
12441      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12442      * mode = 'remote' or 'value' if mode = 'local'). 
12443      * Note: use of a valueField requires the user make a selection
12444      * in order for a value to be mapped.
12445      */
12446     valueField: undefined,
12447     /**
12448      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12449      */
12450     modalTitle : '',
12451     
12452     /**
12453      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12454      * field's data value (defaults to the underlying DOM element's name)
12455      */
12456     hiddenName: undefined,
12457     /**
12458      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12459      */
12460     listClass: '',
12461     /**
12462      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12463      */
12464     selectedClass: 'active',
12465     
12466     /**
12467      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12468      */
12469     shadow:'sides',
12470     /**
12471      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12472      * anchor positions (defaults to 'tl-bl')
12473      */
12474     listAlign: 'tl-bl?',
12475     /**
12476      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12477      */
12478     maxHeight: 300,
12479     /**
12480      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12481      * query specified by the allQuery config option (defaults to 'query')
12482      */
12483     triggerAction: 'query',
12484     /**
12485      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12486      * (defaults to 4, does not apply if editable = false)
12487      */
12488     minChars : 4,
12489     /**
12490      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12491      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12492      */
12493     typeAhead: false,
12494     /**
12495      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12496      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12497      */
12498     queryDelay: 500,
12499     /**
12500      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12501      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12502      */
12503     pageSize: 0,
12504     /**
12505      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12506      * when editable = true (defaults to false)
12507      */
12508     selectOnFocus:false,
12509     /**
12510      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12511      */
12512     queryParam: 'query',
12513     /**
12514      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12515      * when mode = 'remote' (defaults to 'Loading...')
12516      */
12517     loadingText: 'Loading...',
12518     /**
12519      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12520      */
12521     resizable: false,
12522     /**
12523      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12524      */
12525     handleHeight : 8,
12526     /**
12527      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12528      * traditional select (defaults to true)
12529      */
12530     editable: true,
12531     /**
12532      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12533      */
12534     allQuery: '',
12535     /**
12536      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12537      */
12538     mode: 'remote',
12539     /**
12540      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12541      * listWidth has a higher value)
12542      */
12543     minListWidth : 70,
12544     /**
12545      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12546      * allow the user to set arbitrary text into the field (defaults to false)
12547      */
12548     forceSelection:false,
12549     /**
12550      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12551      * if typeAhead = true (defaults to 250)
12552      */
12553     typeAheadDelay : 250,
12554     /**
12555      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12556      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12557      */
12558     valueNotFoundText : undefined,
12559     /**
12560      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12561      */
12562     blockFocus : false,
12563     
12564     /**
12565      * @cfg {Boolean} disableClear Disable showing of clear button.
12566      */
12567     disableClear : false,
12568     /**
12569      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12570      */
12571     alwaysQuery : false,
12572     
12573     /**
12574      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12575      */
12576     multiple : false,
12577     
12578     /**
12579      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12580      */
12581     invalidClass : "has-warning",
12582     
12583     /**
12584      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12585      */
12586     validClass : "has-success",
12587     
12588     /**
12589      * @cfg {Boolean} specialFilter (true|false) special filter default false
12590      */
12591     specialFilter : false,
12592     
12593     /**
12594      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12595      */
12596     mobileTouchView : true,
12597     
12598     /**
12599      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12600      */
12601     useNativeIOS : false,
12602     
12603     ios_options : false,
12604     
12605     //private
12606     addicon : false,
12607     editicon: false,
12608     
12609     page: 0,
12610     hasQuery: false,
12611     append: false,
12612     loadNext: false,
12613     autoFocus : true,
12614     tickable : false,
12615     btnPosition : 'right',
12616     triggerList : true,
12617     showToggleBtn : true,
12618     animate : true,
12619     emptyResultText: 'Empty',
12620     triggerText : 'Select',
12621     
12622     // element that contains real text value.. (when hidden is used..)
12623     
12624     getAutoCreate : function()
12625     {   
12626         var cfg = false;
12627         //render
12628         /*
12629          * Render classic select for iso
12630          */
12631         
12632         if(Roo.isIOS && this.useNativeIOS){
12633             cfg = this.getAutoCreateNativeIOS();
12634             return cfg;
12635         }
12636         
12637         /*
12638          * Touch Devices
12639          */
12640         
12641         if(Roo.isTouch && this.mobileTouchView){
12642             cfg = this.getAutoCreateTouchView();
12643             return cfg;;
12644         }
12645         
12646         /*
12647          *  Normal ComboBox
12648          */
12649         if(!this.tickable){
12650             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12651             if(this.name == 'info_year_invest_id_display_name'){
12652                 Roo.log('cfg.................................................');
12653                 Roo.log(cfg);
12654             }
12655             return cfg;
12656         }
12657         
12658         /*
12659          *  ComboBox with tickable selections
12660          */
12661              
12662         var align = this.labelAlign || this.parentLabelAlign();
12663         
12664         cfg = {
12665             cls : 'form-group roo-combobox-tickable' //input-group
12666         };
12667         
12668         var btn_text_select = '';
12669         var btn_text_done = '';
12670         var btn_text_cancel = '';
12671         
12672         if (this.btn_text_show) {
12673             btn_text_select = 'Select';
12674             btn_text_done = 'Done';
12675             btn_text_cancel = 'Cancel'; 
12676         }
12677         
12678         var buttons = {
12679             tag : 'div',
12680             cls : 'tickable-buttons',
12681             cn : [
12682                 {
12683                     tag : 'button',
12684                     type : 'button',
12685                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12686                     //html : this.triggerText
12687                     html: btn_text_select
12688                 },
12689                 {
12690                     tag : 'button',
12691                     type : 'button',
12692                     name : 'ok',
12693                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12694                     //html : 'Done'
12695                     html: btn_text_done
12696                 },
12697                 {
12698                     tag : 'button',
12699                     type : 'button',
12700                     name : 'cancel',
12701                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12702                     //html : 'Cancel'
12703                     html: btn_text_cancel
12704                 }
12705             ]
12706         };
12707         
12708         if(this.editable){
12709             buttons.cn.unshift({
12710                 tag: 'input',
12711                 cls: 'roo-select2-search-field-input'
12712             });
12713         }
12714         
12715         var _this = this;
12716         
12717         Roo.each(buttons.cn, function(c){
12718             if (_this.size) {
12719                 c.cls += ' btn-' + _this.size;
12720             }
12721
12722             if (_this.disabled) {
12723                 c.disabled = true;
12724             }
12725         });
12726         
12727         var box = {
12728             tag: 'div',
12729             cn: [
12730                 {
12731                     tag: 'input',
12732                     type : 'hidden',
12733                     cls: 'form-hidden-field'
12734                 },
12735                 {
12736                     tag: 'ul',
12737                     cls: 'roo-select2-choices',
12738                     cn:[
12739                         {
12740                             tag: 'li',
12741                             cls: 'roo-select2-search-field',
12742                             cn: [
12743                                 buttons
12744                             ]
12745                         }
12746                     ]
12747                 }
12748             ]
12749         };
12750         
12751         var combobox = {
12752             cls: 'roo-select2-container input-group roo-select2-container-multi',
12753             cn: [
12754                 box
12755 //                {
12756 //                    tag: 'ul',
12757 //                    cls: 'typeahead typeahead-long dropdown-menu',
12758 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12759 //                }
12760             ]
12761         };
12762         
12763         if(this.hasFeedback && !this.allowBlank){
12764             
12765             var feedback = {
12766                 tag: 'span',
12767                 cls: 'glyphicon form-control-feedback'
12768             };
12769
12770             combobox.cn.push(feedback);
12771         }
12772         
12773         
12774         if (align ==='left' && this.fieldLabel.length) {
12775             
12776             cfg.cls += ' roo-form-group-label-left';
12777             
12778             cfg.cn = [
12779                 {
12780                     tag : 'i',
12781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12782                     tooltip : 'This field is required'
12783                 },
12784                 {
12785                     tag: 'label',
12786                     'for' :  id,
12787                     cls : 'control-label',
12788                     html : this.fieldLabel
12789
12790                 },
12791                 {
12792                     cls : "", 
12793                     cn: [
12794                         combobox
12795                     ]
12796                 }
12797
12798             ];
12799             
12800             var labelCfg = cfg.cn[1];
12801             var contentCfg = cfg.cn[2];
12802             
12803
12804             if(this.indicatorpos == 'right'){
12805                 
12806                 cfg.cn = [
12807                     {
12808                         tag: 'label',
12809                         'for' :  id,
12810                         cls : 'control-label',
12811                         cn : [
12812                             {
12813                                 tag : 'span',
12814                                 html : this.fieldLabel
12815                             },
12816                             {
12817                                 tag : 'i',
12818                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12819                                 tooltip : 'This field is required'
12820                             }
12821                         ]
12822                     },
12823                     {
12824                         cls : "",
12825                         cn: [
12826                             combobox
12827                         ]
12828                     }
12829
12830                 ];
12831                 
12832                 
12833                 
12834                 labelCfg = cfg.cn[0];
12835                 contentCfg = cfg.cn[1];
12836             
12837             }
12838             
12839             if(this.labelWidth > 12){
12840                 labelCfg.style = "width: " + this.labelWidth + 'px';
12841             }
12842             
12843             if(this.labelWidth < 13 && this.labelmd == 0){
12844                 this.labelmd = this.labelWidth;
12845             }
12846             
12847             if(this.labellg > 0){
12848                 labelCfg.cls += ' col-lg-' + this.labellg;
12849                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12850             }
12851             
12852             if(this.labelmd > 0){
12853                 labelCfg.cls += ' col-md-' + this.labelmd;
12854                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12855             }
12856             
12857             if(this.labelsm > 0){
12858                 labelCfg.cls += ' col-sm-' + this.labelsm;
12859                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12860             }
12861             
12862             if(this.labelxs > 0){
12863                 labelCfg.cls += ' col-xs-' + this.labelxs;
12864                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12865             }
12866                 
12867                 
12868         } else if ( this.fieldLabel.length) {
12869 //                Roo.log(" label");
12870                  cfg.cn = [
12871                     {
12872                         tag : 'i',
12873                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12874                         tooltip : 'This field is required'
12875                     },
12876                     {
12877                         tag: 'label',
12878                         //cls : 'input-group-addon',
12879                         html : this.fieldLabel
12880                         
12881                     },
12882                     
12883                     combobox
12884                     
12885                 ];
12886                 
12887                 if(this.indicatorpos == 'right'){
12888                     
12889                     cfg.cn = [
12890                         {
12891                             tag: 'label',
12892                             //cls : 'input-group-addon',
12893                             cn : [
12894                                 {
12895                                     tag : 'span',
12896                                     html : this.fieldLabel
12897                                 },
12898                                 {
12899                                     tag : 'i',
12900                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12901                                     tooltip : 'This field is required'
12902                                 }
12903                             ]
12904                         },
12905                         
12906                         
12907                         
12908                         combobox
12909
12910                     ];
12911                 
12912                 }
12913
12914         } else {
12915             
12916 //                Roo.log(" no label && no align");
12917                 cfg = combobox
12918                      
12919                 
12920         }
12921          
12922         var settings=this;
12923         ['xs','sm','md','lg'].map(function(size){
12924             if (settings[size]) {
12925                 cfg.cls += ' col-' + size + '-' + settings[size];
12926             }
12927         });
12928         
12929         return cfg;
12930         
12931     },
12932     
12933     _initEventsCalled : false,
12934     
12935     // private
12936     initEvents: function()
12937     {   
12938         if (this._initEventsCalled) { // as we call render... prevent looping...
12939             return;
12940         }
12941         this._initEventsCalled = true;
12942         
12943         if (!this.store) {
12944             throw "can not find store for combo";
12945         }
12946         
12947         this.store = Roo.factory(this.store, Roo.data);
12948         
12949         // if we are building from html. then this element is so complex, that we can not really
12950         // use the rendered HTML.
12951         // so we have to trash and replace the previous code.
12952         if (Roo.XComponent.build_from_html) {
12953             
12954             // remove this element....
12955             var e = this.el.dom, k=0;
12956             while (e ) { e = e.previousSibling;  ++k;}
12957
12958             this.el.remove();
12959             
12960             this.el=false;
12961             this.rendered = false;
12962             
12963             this.render(this.parent().getChildContainer(true), k);
12964             
12965             
12966             
12967         }
12968         
12969         if(Roo.isIOS && this.useNativeIOS){
12970             this.initIOSView();
12971             return;
12972         }
12973         
12974         /*
12975          * Touch Devices
12976          */
12977         
12978         if(Roo.isTouch && this.mobileTouchView){
12979             this.initTouchView();
12980             return;
12981         }
12982         
12983         if(this.tickable){
12984             this.initTickableEvents();
12985             return;
12986         }
12987         
12988         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12989         
12990         if(this.hiddenName){
12991             
12992             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12993             
12994             this.hiddenField.dom.value =
12995                 this.hiddenValue !== undefined ? this.hiddenValue :
12996                 this.value !== undefined ? this.value : '';
12997
12998             // prevent input submission
12999             this.el.dom.removeAttribute('name');
13000             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13001              
13002              
13003         }
13004         //if(Roo.isGecko){
13005         //    this.el.dom.setAttribute('autocomplete', 'off');
13006         //}
13007         
13008         var cls = 'x-combo-list';
13009         
13010         //this.list = new Roo.Layer({
13011         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13012         //});
13013         
13014         var _this = this;
13015         
13016         (function(){
13017             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13018             _this.list.setWidth(lw);
13019         }).defer(100);
13020         
13021         this.list.on('mouseover', this.onViewOver, this);
13022         this.list.on('mousemove', this.onViewMove, this);
13023         
13024         this.list.on('scroll', this.onViewScroll, this);
13025         
13026         /*
13027         this.list.swallowEvent('mousewheel');
13028         this.assetHeight = 0;
13029
13030         if(this.title){
13031             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13032             this.assetHeight += this.header.getHeight();
13033         }
13034
13035         this.innerList = this.list.createChild({cls:cls+'-inner'});
13036         this.innerList.on('mouseover', this.onViewOver, this);
13037         this.innerList.on('mousemove', this.onViewMove, this);
13038         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13039         
13040         if(this.allowBlank && !this.pageSize && !this.disableClear){
13041             this.footer = this.list.createChild({cls:cls+'-ft'});
13042             this.pageTb = new Roo.Toolbar(this.footer);
13043            
13044         }
13045         if(this.pageSize){
13046             this.footer = this.list.createChild({cls:cls+'-ft'});
13047             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13048                     {pageSize: this.pageSize});
13049             
13050         }
13051         
13052         if (this.pageTb && this.allowBlank && !this.disableClear) {
13053             var _this = this;
13054             this.pageTb.add(new Roo.Toolbar.Fill(), {
13055                 cls: 'x-btn-icon x-btn-clear',
13056                 text: '&#160;',
13057                 handler: function()
13058                 {
13059                     _this.collapse();
13060                     _this.clearValue();
13061                     _this.onSelect(false, -1);
13062                 }
13063             });
13064         }
13065         if (this.footer) {
13066             this.assetHeight += this.footer.getHeight();
13067         }
13068         */
13069             
13070         if(!this.tpl){
13071             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13072         }
13073
13074         this.view = new Roo.View(this.list, this.tpl, {
13075             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13076         });
13077         //this.view.wrapEl.setDisplayed(false);
13078         this.view.on('click', this.onViewClick, this);
13079         
13080         
13081         
13082         this.store.on('beforeload', this.onBeforeLoad, this);
13083         this.store.on('load', this.onLoad, this);
13084         this.store.on('loadexception', this.onLoadException, this);
13085         /*
13086         if(this.resizable){
13087             this.resizer = new Roo.Resizable(this.list,  {
13088                pinned:true, handles:'se'
13089             });
13090             this.resizer.on('resize', function(r, w, h){
13091                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13092                 this.listWidth = w;
13093                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13094                 this.restrictHeight();
13095             }, this);
13096             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13097         }
13098         */
13099         if(!this.editable){
13100             this.editable = true;
13101             this.setEditable(false);
13102         }
13103         
13104         /*
13105         
13106         if (typeof(this.events.add.listeners) != 'undefined') {
13107             
13108             this.addicon = this.wrap.createChild(
13109                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13110        
13111             this.addicon.on('click', function(e) {
13112                 this.fireEvent('add', this);
13113             }, this);
13114         }
13115         if (typeof(this.events.edit.listeners) != 'undefined') {
13116             
13117             this.editicon = this.wrap.createChild(
13118                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13119             if (this.addicon) {
13120                 this.editicon.setStyle('margin-left', '40px');
13121             }
13122             this.editicon.on('click', function(e) {
13123                 
13124                 // we fire even  if inothing is selected..
13125                 this.fireEvent('edit', this, this.lastData );
13126                 
13127             }, this);
13128         }
13129         */
13130         
13131         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13132             "up" : function(e){
13133                 this.inKeyMode = true;
13134                 this.selectPrev();
13135             },
13136
13137             "down" : function(e){
13138                 if(!this.isExpanded()){
13139                     this.onTriggerClick();
13140                 }else{
13141                     this.inKeyMode = true;
13142                     this.selectNext();
13143                 }
13144             },
13145
13146             "enter" : function(e){
13147 //                this.onViewClick();
13148                 //return true;
13149                 this.collapse();
13150                 
13151                 if(this.fireEvent("specialkey", this, e)){
13152                     this.onViewClick(false);
13153                 }
13154                 
13155                 return true;
13156             },
13157
13158             "esc" : function(e){
13159                 this.collapse();
13160             },
13161
13162             "tab" : function(e){
13163                 this.collapse();
13164                 
13165                 if(this.fireEvent("specialkey", this, e)){
13166                     this.onViewClick(false);
13167                 }
13168                 
13169                 return true;
13170             },
13171
13172             scope : this,
13173
13174             doRelay : function(foo, bar, hname){
13175                 if(hname == 'down' || this.scope.isExpanded()){
13176                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13177                 }
13178                 return true;
13179             },
13180
13181             forceKeyDown: true
13182         });
13183         
13184         
13185         this.queryDelay = Math.max(this.queryDelay || 10,
13186                 this.mode == 'local' ? 10 : 250);
13187         
13188         
13189         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13190         
13191         if(this.typeAhead){
13192             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13193         }
13194         if(this.editable !== false){
13195             this.inputEl().on("keyup", this.onKeyUp, this);
13196         }
13197         if(this.forceSelection){
13198             this.inputEl().on('blur', this.doForce, this);
13199         }
13200         
13201         if(this.multiple){
13202             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13203             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13204         }
13205     },
13206     
13207     initTickableEvents: function()
13208     {   
13209         this.createList();
13210         
13211         if(this.hiddenName){
13212             
13213             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13214             
13215             this.hiddenField.dom.value =
13216                 this.hiddenValue !== undefined ? this.hiddenValue :
13217                 this.value !== undefined ? this.value : '';
13218
13219             // prevent input submission
13220             this.el.dom.removeAttribute('name');
13221             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13222              
13223              
13224         }
13225         
13226 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13227         
13228         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13229         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13230         if(this.triggerList){
13231             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13232         }
13233          
13234         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13235         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13236         
13237         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13238         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13239         
13240         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13241         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13242         
13243         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13244         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13245         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13246         
13247         this.okBtn.hide();
13248         this.cancelBtn.hide();
13249         
13250         var _this = this;
13251         
13252         (function(){
13253             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13254             _this.list.setWidth(lw);
13255         }).defer(100);
13256         
13257         this.list.on('mouseover', this.onViewOver, this);
13258         this.list.on('mousemove', this.onViewMove, this);
13259         
13260         this.list.on('scroll', this.onViewScroll, this);
13261         
13262         if(!this.tpl){
13263             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>';
13264         }
13265
13266         this.view = new Roo.View(this.list, this.tpl, {
13267             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13268         });
13269         
13270         //this.view.wrapEl.setDisplayed(false);
13271         this.view.on('click', this.onViewClick, this);
13272         
13273         
13274         
13275         this.store.on('beforeload', this.onBeforeLoad, this);
13276         this.store.on('load', this.onLoad, this);
13277         this.store.on('loadexception', this.onLoadException, this);
13278         
13279         if(this.editable){
13280             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13281                 "up" : function(e){
13282                     this.inKeyMode = true;
13283                     this.selectPrev();
13284                 },
13285
13286                 "down" : function(e){
13287                     this.inKeyMode = true;
13288                     this.selectNext();
13289                 },
13290
13291                 "enter" : function(e){
13292                     if(this.fireEvent("specialkey", this, e)){
13293                         this.onViewClick(false);
13294                     }
13295                     
13296                     return true;
13297                 },
13298
13299                 "esc" : function(e){
13300                     this.onTickableFooterButtonClick(e, false, false);
13301                 },
13302
13303                 "tab" : function(e){
13304                     this.fireEvent("specialkey", this, e);
13305                     
13306                     this.onTickableFooterButtonClick(e, false, false);
13307                     
13308                     return true;
13309                 },
13310
13311                 scope : this,
13312
13313                 doRelay : function(e, fn, key){
13314                     if(this.scope.isExpanded()){
13315                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13316                     }
13317                     return true;
13318                 },
13319
13320                 forceKeyDown: true
13321             });
13322         }
13323         
13324         this.queryDelay = Math.max(this.queryDelay || 10,
13325                 this.mode == 'local' ? 10 : 250);
13326         
13327         
13328         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13329         
13330         if(this.typeAhead){
13331             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13332         }
13333         
13334         if(this.editable !== false){
13335             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13336         }
13337         
13338         this.indicator = this.indicatorEl();
13339         
13340         if(this.indicator){
13341             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13342             this.indicator.hide();
13343         }
13344         
13345     },
13346
13347     onDestroy : function(){
13348         if(this.view){
13349             this.view.setStore(null);
13350             this.view.el.removeAllListeners();
13351             this.view.el.remove();
13352             this.view.purgeListeners();
13353         }
13354         if(this.list){
13355             this.list.dom.innerHTML  = '';
13356         }
13357         
13358         if(this.store){
13359             this.store.un('beforeload', this.onBeforeLoad, this);
13360             this.store.un('load', this.onLoad, this);
13361             this.store.un('loadexception', this.onLoadException, this);
13362         }
13363         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13364     },
13365
13366     // private
13367     fireKey : function(e){
13368         if(e.isNavKeyPress() && !this.list.isVisible()){
13369             this.fireEvent("specialkey", this, e);
13370         }
13371     },
13372
13373     // private
13374     onResize: function(w, h){
13375 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13376 //        
13377 //        if(typeof w != 'number'){
13378 //            // we do not handle it!?!?
13379 //            return;
13380 //        }
13381 //        var tw = this.trigger.getWidth();
13382 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13383 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13384 //        var x = w - tw;
13385 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13386 //            
13387 //        //this.trigger.setStyle('left', x+'px');
13388 //        
13389 //        if(this.list && this.listWidth === undefined){
13390 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13391 //            this.list.setWidth(lw);
13392 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13393 //        }
13394         
13395     
13396         
13397     },
13398
13399     /**
13400      * Allow or prevent the user from directly editing the field text.  If false is passed,
13401      * the user will only be able to select from the items defined in the dropdown list.  This method
13402      * is the runtime equivalent of setting the 'editable' config option at config time.
13403      * @param {Boolean} value True to allow the user to directly edit the field text
13404      */
13405     setEditable : function(value){
13406         if(value == this.editable){
13407             return;
13408         }
13409         this.editable = value;
13410         if(!value){
13411             this.inputEl().dom.setAttribute('readOnly', true);
13412             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13413             this.inputEl().addClass('x-combo-noedit');
13414         }else{
13415             this.inputEl().dom.setAttribute('readOnly', false);
13416             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13417             this.inputEl().removeClass('x-combo-noedit');
13418         }
13419     },
13420
13421     // private
13422     
13423     onBeforeLoad : function(combo,opts){
13424         if(!this.hasFocus){
13425             return;
13426         }
13427          if (!opts.add) {
13428             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13429          }
13430         this.restrictHeight();
13431         this.selectedIndex = -1;
13432     },
13433
13434     // private
13435     onLoad : function(){
13436         
13437         this.hasQuery = false;
13438         
13439         if(!this.hasFocus){
13440             return;
13441         }
13442         
13443         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13444             this.loading.hide();
13445         }
13446              
13447         if(this.store.getCount() > 0){
13448             this.expand();
13449             this.restrictHeight();
13450             if(this.lastQuery == this.allQuery){
13451                 if(this.editable && !this.tickable){
13452                     this.inputEl().dom.select();
13453                 }
13454                 
13455                 if(
13456                     !this.selectByValue(this.value, true) &&
13457                     this.autoFocus && 
13458                     (
13459                         !this.store.lastOptions ||
13460                         typeof(this.store.lastOptions.add) == 'undefined' || 
13461                         this.store.lastOptions.add != true
13462                     )
13463                 ){
13464                     this.select(0, true);
13465                 }
13466             }else{
13467                 if(this.autoFocus){
13468                     this.selectNext();
13469                 }
13470                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13471                     this.taTask.delay(this.typeAheadDelay);
13472                 }
13473             }
13474         }else{
13475             this.onEmptyResults();
13476         }
13477         
13478         //this.el.focus();
13479     },
13480     // private
13481     onLoadException : function()
13482     {
13483         this.hasQuery = false;
13484         
13485         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13486             this.loading.hide();
13487         }
13488         
13489         if(this.tickable && this.editable){
13490             return;
13491         }
13492         
13493         this.collapse();
13494         // only causes errors at present
13495         //Roo.log(this.store.reader.jsonData);
13496         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13497             // fixme
13498             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13499         //}
13500         
13501         
13502     },
13503     // private
13504     onTypeAhead : function(){
13505         if(this.store.getCount() > 0){
13506             var r = this.store.getAt(0);
13507             var newValue = r.data[this.displayField];
13508             var len = newValue.length;
13509             var selStart = this.getRawValue().length;
13510             
13511             if(selStart != len){
13512                 this.setRawValue(newValue);
13513                 this.selectText(selStart, newValue.length);
13514             }
13515         }
13516     },
13517
13518     // private
13519     onSelect : function(record, index){
13520         
13521         if(this.fireEvent('beforeselect', this, record, index) !== false){
13522         
13523             this.setFromData(index > -1 ? record.data : false);
13524             
13525             this.collapse();
13526             this.fireEvent('select', this, record, index);
13527         }
13528     },
13529
13530     /**
13531      * Returns the currently selected field value or empty string if no value is set.
13532      * @return {String} value The selected value
13533      */
13534     getValue : function()
13535     {
13536         if(Roo.isIOS && this.useNativeIOS){
13537             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13538         }
13539         
13540         if(this.multiple){
13541             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13542         }
13543         
13544         if(this.valueField){
13545             return typeof this.value != 'undefined' ? this.value : '';
13546         }else{
13547             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13548         }
13549     },
13550     
13551     getRawValue : function()
13552     {
13553         if(Roo.isIOS && this.useNativeIOS){
13554             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13555         }
13556         
13557         var v = this.inputEl().getValue();
13558         
13559         return v;
13560     },
13561
13562     /**
13563      * Clears any text/value currently set in the field
13564      */
13565     clearValue : function(){
13566         
13567         if(this.hiddenField){
13568             this.hiddenField.dom.value = '';
13569         }
13570         this.value = '';
13571         this.setRawValue('');
13572         this.lastSelectionText = '';
13573         this.lastData = false;
13574         
13575         var close = this.closeTriggerEl();
13576         
13577         if(close){
13578             close.hide();
13579         }
13580         
13581         this.validate();
13582         
13583     },
13584
13585     /**
13586      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13587      * will be displayed in the field.  If the value does not match the data value of an existing item,
13588      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13589      * Otherwise the field will be blank (although the value will still be set).
13590      * @param {String} value The value to match
13591      */
13592     setValue : function(v)
13593     {
13594         if(Roo.isIOS && this.useNativeIOS){
13595             this.setIOSValue(v);
13596             return;
13597         }
13598         
13599         if(this.multiple){
13600             this.syncValue();
13601             return;
13602         }
13603         
13604         var text = v;
13605         if(this.valueField){
13606             var r = this.findRecord(this.valueField, v);
13607             if(r){
13608                 text = r.data[this.displayField];
13609             }else if(this.valueNotFoundText !== undefined){
13610                 text = this.valueNotFoundText;
13611             }
13612         }
13613         this.lastSelectionText = text;
13614         if(this.hiddenField){
13615             this.hiddenField.dom.value = v;
13616         }
13617         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13618         this.value = v;
13619         
13620         var close = this.closeTriggerEl();
13621         
13622         if(close){
13623             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13624         }
13625         
13626         this.validate();
13627     },
13628     /**
13629      * @property {Object} the last set data for the element
13630      */
13631     
13632     lastData : false,
13633     /**
13634      * Sets the value of the field based on a object which is related to the record format for the store.
13635      * @param {Object} value the value to set as. or false on reset?
13636      */
13637     setFromData : function(o){
13638         
13639         if(this.multiple){
13640             this.addItem(o);
13641             return;
13642         }
13643             
13644         var dv = ''; // display value
13645         var vv = ''; // value value..
13646         this.lastData = o;
13647         if (this.displayField) {
13648             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13649         } else {
13650             // this is an error condition!!!
13651             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13652         }
13653         
13654         if(this.valueField){
13655             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13656         }
13657         
13658         var close = this.closeTriggerEl();
13659         
13660         if(close){
13661             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13662         }
13663         
13664         if(this.hiddenField){
13665             this.hiddenField.dom.value = vv;
13666             
13667             this.lastSelectionText = dv;
13668             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13669             this.value = vv;
13670             return;
13671         }
13672         // no hidden field.. - we store the value in 'value', but still display
13673         // display field!!!!
13674         this.lastSelectionText = dv;
13675         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13676         this.value = vv;
13677         
13678         
13679         
13680     },
13681     // private
13682     reset : function(){
13683         // overridden so that last data is reset..
13684         
13685         if(this.multiple){
13686             this.clearItem();
13687             return;
13688         }
13689         
13690         this.setValue(this.originalValue);
13691         //this.clearInvalid();
13692         this.lastData = false;
13693         if (this.view) {
13694             this.view.clearSelections();
13695         }
13696         
13697         this.validate();
13698     },
13699     // private
13700     findRecord : function(prop, value){
13701         var record;
13702         if(this.store.getCount() > 0){
13703             this.store.each(function(r){
13704                 if(r.data[prop] == value){
13705                     record = r;
13706                     return false;
13707                 }
13708                 return true;
13709             });
13710         }
13711         return record;
13712     },
13713     
13714     getName: function()
13715     {
13716         // returns hidden if it's set..
13717         if (!this.rendered) {return ''};
13718         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13719         
13720     },
13721     // private
13722     onViewMove : function(e, t){
13723         this.inKeyMode = false;
13724     },
13725
13726     // private
13727     onViewOver : function(e, t){
13728         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13729             return;
13730         }
13731         var item = this.view.findItemFromChild(t);
13732         
13733         if(item){
13734             var index = this.view.indexOf(item);
13735             this.select(index, false);
13736         }
13737     },
13738
13739     // private
13740     onViewClick : function(view, doFocus, el, e)
13741     {
13742         var index = this.view.getSelectedIndexes()[0];
13743         
13744         var r = this.store.getAt(index);
13745         
13746         if(this.tickable){
13747             
13748             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13749                 return;
13750             }
13751             
13752             var rm = false;
13753             var _this = this;
13754             
13755             Roo.each(this.tickItems, function(v,k){
13756                 
13757                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13758                     Roo.log(v);
13759                     _this.tickItems.splice(k, 1);
13760                     
13761                     if(typeof(e) == 'undefined' && view == false){
13762                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13763                     }
13764                     
13765                     rm = true;
13766                     return;
13767                 }
13768             });
13769             
13770             if(rm){
13771                 return;
13772             }
13773             
13774             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13775                 this.tickItems.push(r.data);
13776             }
13777             
13778             if(typeof(e) == 'undefined' && view == false){
13779                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13780             }
13781                     
13782             return;
13783         }
13784         
13785         if(r){
13786             this.onSelect(r, index);
13787         }
13788         if(doFocus !== false && !this.blockFocus){
13789             this.inputEl().focus();
13790         }
13791     },
13792
13793     // private
13794     restrictHeight : function(){
13795         //this.innerList.dom.style.height = '';
13796         //var inner = this.innerList.dom;
13797         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13798         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13799         //this.list.beginUpdate();
13800         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13801         this.list.alignTo(this.inputEl(), this.listAlign);
13802         this.list.alignTo(this.inputEl(), this.listAlign);
13803         //this.list.endUpdate();
13804     },
13805
13806     // private
13807     onEmptyResults : function(){
13808         
13809         if(this.tickable && this.editable){
13810             this.restrictHeight();
13811             return;
13812         }
13813         
13814         this.collapse();
13815     },
13816
13817     /**
13818      * Returns true if the dropdown list is expanded, else false.
13819      */
13820     isExpanded : function(){
13821         return this.list.isVisible();
13822     },
13823
13824     /**
13825      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13826      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13827      * @param {String} value The data value of the item to select
13828      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13829      * selected item if it is not currently in view (defaults to true)
13830      * @return {Boolean} True if the value matched an item in the list, else false
13831      */
13832     selectByValue : function(v, scrollIntoView){
13833         if(v !== undefined && v !== null){
13834             var r = this.findRecord(this.valueField || this.displayField, v);
13835             if(r){
13836                 this.select(this.store.indexOf(r), scrollIntoView);
13837                 return true;
13838             }
13839         }
13840         return false;
13841     },
13842
13843     /**
13844      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13845      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13846      * @param {Number} index The zero-based index of the list item to select
13847      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13848      * selected item if it is not currently in view (defaults to true)
13849      */
13850     select : function(index, scrollIntoView){
13851         this.selectedIndex = index;
13852         this.view.select(index);
13853         if(scrollIntoView !== false){
13854             var el = this.view.getNode(index);
13855             /*
13856              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13857              */
13858             if(el){
13859                 this.list.scrollChildIntoView(el, false);
13860             }
13861         }
13862     },
13863
13864     // private
13865     selectNext : function(){
13866         var ct = this.store.getCount();
13867         if(ct > 0){
13868             if(this.selectedIndex == -1){
13869                 this.select(0);
13870             }else if(this.selectedIndex < ct-1){
13871                 this.select(this.selectedIndex+1);
13872             }
13873         }
13874     },
13875
13876     // private
13877     selectPrev : function(){
13878         var ct = this.store.getCount();
13879         if(ct > 0){
13880             if(this.selectedIndex == -1){
13881                 this.select(0);
13882             }else if(this.selectedIndex != 0){
13883                 this.select(this.selectedIndex-1);
13884             }
13885         }
13886     },
13887
13888     // private
13889     onKeyUp : function(e){
13890         if(this.editable !== false && !e.isSpecialKey()){
13891             this.lastKey = e.getKey();
13892             this.dqTask.delay(this.queryDelay);
13893         }
13894     },
13895
13896     // private
13897     validateBlur : function(){
13898         return !this.list || !this.list.isVisible();   
13899     },
13900
13901     // private
13902     initQuery : function(){
13903         
13904         var v = this.getRawValue();
13905         
13906         if(this.tickable && this.editable){
13907             v = this.tickableInputEl().getValue();
13908         }
13909         
13910         this.doQuery(v);
13911     },
13912
13913     // private
13914     doForce : function(){
13915         if(this.inputEl().dom.value.length > 0){
13916             this.inputEl().dom.value =
13917                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13918              
13919         }
13920     },
13921
13922     /**
13923      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13924      * query allowing the query action to be canceled if needed.
13925      * @param {String} query The SQL query to execute
13926      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13927      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13928      * saved in the current store (defaults to false)
13929      */
13930     doQuery : function(q, forceAll){
13931         
13932         if(q === undefined || q === null){
13933             q = '';
13934         }
13935         var qe = {
13936             query: q,
13937             forceAll: forceAll,
13938             combo: this,
13939             cancel:false
13940         };
13941         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13942             return false;
13943         }
13944         q = qe.query;
13945         
13946         forceAll = qe.forceAll;
13947         if(forceAll === true || (q.length >= this.minChars)){
13948             
13949             this.hasQuery = true;
13950             
13951             if(this.lastQuery != q || this.alwaysQuery){
13952                 this.lastQuery = q;
13953                 if(this.mode == 'local'){
13954                     this.selectedIndex = -1;
13955                     if(forceAll){
13956                         this.store.clearFilter();
13957                     }else{
13958                         
13959                         if(this.specialFilter){
13960                             this.fireEvent('specialfilter', this);
13961                             this.onLoad();
13962                             return;
13963                         }
13964                         
13965                         this.store.filter(this.displayField, q);
13966                     }
13967                     
13968                     this.store.fireEvent("datachanged", this.store);
13969                     
13970                     this.onLoad();
13971                     
13972                     
13973                 }else{
13974                     
13975                     this.store.baseParams[this.queryParam] = q;
13976                     
13977                     var options = {params : this.getParams(q)};
13978                     
13979                     if(this.loadNext){
13980                         options.add = true;
13981                         options.params.start = this.page * this.pageSize;
13982                     }
13983                     
13984                     this.store.load(options);
13985                     
13986                     /*
13987                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13988                      *  we should expand the list on onLoad
13989                      *  so command out it
13990                      */
13991 //                    this.expand();
13992                 }
13993             }else{
13994                 this.selectedIndex = -1;
13995                 this.onLoad();   
13996             }
13997         }
13998         
13999         this.loadNext = false;
14000     },
14001     
14002     // private
14003     getParams : function(q){
14004         var p = {};
14005         //p[this.queryParam] = q;
14006         
14007         if(this.pageSize){
14008             p.start = 0;
14009             p.limit = this.pageSize;
14010         }
14011         return p;
14012     },
14013
14014     /**
14015      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14016      */
14017     collapse : function(){
14018         if(!this.isExpanded()){
14019             return;
14020         }
14021         
14022         this.list.hide();
14023         
14024         this.hasFocus = false;
14025         
14026         if(this.tickable){
14027             this.okBtn.hide();
14028             this.cancelBtn.hide();
14029             this.trigger.show();
14030             
14031             if(this.editable){
14032                 this.tickableInputEl().dom.value = '';
14033                 this.tickableInputEl().blur();
14034             }
14035             
14036         }
14037         
14038         Roo.get(document).un('mousedown', this.collapseIf, this);
14039         Roo.get(document).un('mousewheel', this.collapseIf, this);
14040         if (!this.editable) {
14041             Roo.get(document).un('keydown', this.listKeyPress, this);
14042         }
14043         this.fireEvent('collapse', this);
14044         
14045         this.validate();
14046     },
14047
14048     // private
14049     collapseIf : function(e){
14050         var in_combo  = e.within(this.el);
14051         var in_list =  e.within(this.list);
14052         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14053         
14054         if (in_combo || in_list || is_list) {
14055             //e.stopPropagation();
14056             return;
14057         }
14058         
14059         if(this.tickable){
14060             this.onTickableFooterButtonClick(e, false, false);
14061         }
14062
14063         this.collapse();
14064         
14065     },
14066
14067     /**
14068      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14069      */
14070     expand : function(){
14071        
14072         if(this.isExpanded() || !this.hasFocus){
14073             return;
14074         }
14075         
14076         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14077         this.list.setWidth(lw);
14078         
14079         Roo.log('expand');
14080         
14081         this.list.show();
14082         
14083         this.restrictHeight();
14084         
14085         if(this.tickable){
14086             
14087             this.tickItems = Roo.apply([], this.item);
14088             
14089             this.okBtn.show();
14090             this.cancelBtn.show();
14091             this.trigger.hide();
14092             
14093             if(this.editable){
14094                 this.tickableInputEl().focus();
14095             }
14096             
14097         }
14098         
14099         Roo.get(document).on('mousedown', this.collapseIf, this);
14100         Roo.get(document).on('mousewheel', this.collapseIf, this);
14101         if (!this.editable) {
14102             Roo.get(document).on('keydown', this.listKeyPress, this);
14103         }
14104         
14105         this.fireEvent('expand', this);
14106     },
14107
14108     // private
14109     // Implements the default empty TriggerField.onTriggerClick function
14110     onTriggerClick : function(e)
14111     {
14112         Roo.log('trigger click');
14113         
14114         if(this.disabled || !this.triggerList){
14115             return;
14116         }
14117         
14118         this.page = 0;
14119         this.loadNext = false;
14120         
14121         if(this.isExpanded()){
14122             this.collapse();
14123             if (!this.blockFocus) {
14124                 this.inputEl().focus();
14125             }
14126             
14127         }else {
14128             this.hasFocus = true;
14129             if(this.triggerAction == 'all') {
14130                 this.doQuery(this.allQuery, true);
14131             } else {
14132                 this.doQuery(this.getRawValue());
14133             }
14134             if (!this.blockFocus) {
14135                 this.inputEl().focus();
14136             }
14137         }
14138     },
14139     
14140     onTickableTriggerClick : function(e)
14141     {
14142         if(this.disabled){
14143             return;
14144         }
14145         
14146         this.page = 0;
14147         this.loadNext = false;
14148         this.hasFocus = true;
14149         
14150         if(this.triggerAction == 'all') {
14151             this.doQuery(this.allQuery, true);
14152         } else {
14153             this.doQuery(this.getRawValue());
14154         }
14155     },
14156     
14157     onSearchFieldClick : function(e)
14158     {
14159         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14160             this.onTickableFooterButtonClick(e, false, false);
14161             return;
14162         }
14163         
14164         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14165             return;
14166         }
14167         
14168         this.page = 0;
14169         this.loadNext = false;
14170         this.hasFocus = true;
14171         
14172         if(this.triggerAction == 'all') {
14173             this.doQuery(this.allQuery, true);
14174         } else {
14175             this.doQuery(this.getRawValue());
14176         }
14177     },
14178     
14179     listKeyPress : function(e)
14180     {
14181         //Roo.log('listkeypress');
14182         // scroll to first matching element based on key pres..
14183         if (e.isSpecialKey()) {
14184             return false;
14185         }
14186         var k = String.fromCharCode(e.getKey()).toUpperCase();
14187         //Roo.log(k);
14188         var match  = false;
14189         var csel = this.view.getSelectedNodes();
14190         var cselitem = false;
14191         if (csel.length) {
14192             var ix = this.view.indexOf(csel[0]);
14193             cselitem  = this.store.getAt(ix);
14194             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14195                 cselitem = false;
14196             }
14197             
14198         }
14199         
14200         this.store.each(function(v) { 
14201             if (cselitem) {
14202                 // start at existing selection.
14203                 if (cselitem.id == v.id) {
14204                     cselitem = false;
14205                 }
14206                 return true;
14207             }
14208                 
14209             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14210                 match = this.store.indexOf(v);
14211                 return false;
14212             }
14213             return true;
14214         }, this);
14215         
14216         if (match === false) {
14217             return true; // no more action?
14218         }
14219         // scroll to?
14220         this.view.select(match);
14221         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14222         sn.scrollIntoView(sn.dom.parentNode, false);
14223     },
14224     
14225     onViewScroll : function(e, t){
14226         
14227         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){
14228             return;
14229         }
14230         
14231         this.hasQuery = true;
14232         
14233         this.loading = this.list.select('.loading', true).first();
14234         
14235         if(this.loading === null){
14236             this.list.createChild({
14237                 tag: 'div',
14238                 cls: 'loading roo-select2-more-results roo-select2-active',
14239                 html: 'Loading more results...'
14240             });
14241             
14242             this.loading = this.list.select('.loading', true).first();
14243             
14244             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14245             
14246             this.loading.hide();
14247         }
14248         
14249         this.loading.show();
14250         
14251         var _combo = this;
14252         
14253         this.page++;
14254         this.loadNext = true;
14255         
14256         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14257         
14258         return;
14259     },
14260     
14261     addItem : function(o)
14262     {   
14263         var dv = ''; // display value
14264         
14265         if (this.displayField) {
14266             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14267         } else {
14268             // this is an error condition!!!
14269             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14270         }
14271         
14272         if(!dv.length){
14273             return;
14274         }
14275         
14276         var choice = this.choices.createChild({
14277             tag: 'li',
14278             cls: 'roo-select2-search-choice',
14279             cn: [
14280                 {
14281                     tag: 'div',
14282                     html: dv
14283                 },
14284                 {
14285                     tag: 'a',
14286                     href: '#',
14287                     cls: 'roo-select2-search-choice-close',
14288                     tabindex: '-1'
14289                 }
14290             ]
14291             
14292         }, this.searchField);
14293         
14294         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14295         
14296         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14297         
14298         this.item.push(o);
14299         
14300         this.lastData = o;
14301         
14302         this.syncValue();
14303         
14304         this.inputEl().dom.value = '';
14305         
14306         this.validate();
14307     },
14308     
14309     onRemoveItem : function(e, _self, o)
14310     {
14311         e.preventDefault();
14312         
14313         this.lastItem = Roo.apply([], this.item);
14314         
14315         var index = this.item.indexOf(o.data) * 1;
14316         
14317         if( index < 0){
14318             Roo.log('not this item?!');
14319             return;
14320         }
14321         
14322         this.item.splice(index, 1);
14323         o.item.remove();
14324         
14325         this.syncValue();
14326         
14327         this.fireEvent('remove', this, e);
14328         
14329         this.validate();
14330         
14331     },
14332     
14333     syncValue : function()
14334     {
14335         if(!this.item.length){
14336             this.clearValue();
14337             return;
14338         }
14339             
14340         var value = [];
14341         var _this = this;
14342         Roo.each(this.item, function(i){
14343             if(_this.valueField){
14344                 value.push(i[_this.valueField]);
14345                 return;
14346             }
14347
14348             value.push(i);
14349         });
14350
14351         this.value = value.join(',');
14352
14353         if(this.hiddenField){
14354             this.hiddenField.dom.value = this.value;
14355         }
14356         
14357         this.store.fireEvent("datachanged", this.store);
14358         
14359         this.validate();
14360     },
14361     
14362     clearItem : function()
14363     {
14364         if(!this.multiple){
14365             return;
14366         }
14367         
14368         this.item = [];
14369         
14370         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14371            c.remove();
14372         });
14373         
14374         this.syncValue();
14375         
14376         this.validate();
14377         
14378         if(this.tickable && !Roo.isTouch){
14379             this.view.refresh();
14380         }
14381     },
14382     
14383     inputEl: function ()
14384     {
14385         if(Roo.isIOS && this.useNativeIOS){
14386             return this.el.select('select.roo-ios-select', true).first();
14387         }
14388         
14389         if(Roo.isTouch && this.mobileTouchView){
14390             return this.el.select('input.form-control',true).first();
14391         }
14392         
14393         if(this.tickable){
14394             return this.searchField;
14395         }
14396         
14397         return this.el.select('input.form-control',true).first();
14398     },
14399     
14400     onTickableFooterButtonClick : function(e, btn, el)
14401     {
14402         e.preventDefault();
14403         
14404         this.lastItem = Roo.apply([], this.item);
14405         
14406         if(btn && btn.name == 'cancel'){
14407             this.tickItems = Roo.apply([], this.item);
14408             this.collapse();
14409             return;
14410         }
14411         
14412         this.clearItem();
14413         
14414         var _this = this;
14415         
14416         Roo.each(this.tickItems, function(o){
14417             _this.addItem(o);
14418         });
14419         
14420         this.collapse();
14421         
14422     },
14423     
14424     validate : function()
14425     {
14426         var v = this.getRawValue();
14427         
14428         if(this.multiple){
14429             v = this.getValue();
14430         }
14431         
14432         if(this.disabled || this.allowBlank || v.length){
14433             this.markValid();
14434             return true;
14435         }
14436         
14437         this.markInvalid();
14438         return false;
14439     },
14440     
14441     tickableInputEl : function()
14442     {
14443         if(!this.tickable || !this.editable){
14444             return this.inputEl();
14445         }
14446         
14447         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14448     },
14449     
14450     
14451     getAutoCreateTouchView : function()
14452     {
14453         var id = Roo.id();
14454         
14455         var cfg = {
14456             cls: 'form-group' //input-group
14457         };
14458         
14459         var input =  {
14460             tag: 'input',
14461             id : id,
14462             type : this.inputType,
14463             cls : 'form-control x-combo-noedit',
14464             autocomplete: 'new-password',
14465             placeholder : this.placeholder || '',
14466             readonly : true
14467         };
14468         
14469         if (this.name) {
14470             input.name = this.name;
14471         }
14472         
14473         if (this.size) {
14474             input.cls += ' input-' + this.size;
14475         }
14476         
14477         if (this.disabled) {
14478             input.disabled = true;
14479         }
14480         
14481         var inputblock = {
14482             cls : '',
14483             cn : [
14484                 input
14485             ]
14486         };
14487         
14488         if(this.before){
14489             inputblock.cls += ' input-group';
14490             
14491             inputblock.cn.unshift({
14492                 tag :'span',
14493                 cls : 'input-group-addon',
14494                 html : this.before
14495             });
14496         }
14497         
14498         if(this.removable && !this.multiple){
14499             inputblock.cls += ' roo-removable';
14500             
14501             inputblock.cn.push({
14502                 tag: 'button',
14503                 html : 'x',
14504                 cls : 'roo-combo-removable-btn close'
14505             });
14506         }
14507
14508         if(this.hasFeedback && !this.allowBlank){
14509             
14510             inputblock.cls += ' has-feedback';
14511             
14512             inputblock.cn.push({
14513                 tag: 'span',
14514                 cls: 'glyphicon form-control-feedback'
14515             });
14516             
14517         }
14518         
14519         if (this.after) {
14520             
14521             inputblock.cls += (this.before) ? '' : ' input-group';
14522             
14523             inputblock.cn.push({
14524                 tag :'span',
14525                 cls : 'input-group-addon',
14526                 html : this.after
14527             });
14528         }
14529
14530         var box = {
14531             tag: 'div',
14532             cn: [
14533                 {
14534                     tag: 'input',
14535                     type : 'hidden',
14536                     cls: 'form-hidden-field'
14537                 },
14538                 inputblock
14539             ]
14540             
14541         };
14542         
14543         if(this.multiple){
14544             box = {
14545                 tag: 'div',
14546                 cn: [
14547                     {
14548                         tag: 'input',
14549                         type : 'hidden',
14550                         cls: 'form-hidden-field'
14551                     },
14552                     {
14553                         tag: 'ul',
14554                         cls: 'roo-select2-choices',
14555                         cn:[
14556                             {
14557                                 tag: 'li',
14558                                 cls: 'roo-select2-search-field',
14559                                 cn: [
14560
14561                                     inputblock
14562                                 ]
14563                             }
14564                         ]
14565                     }
14566                 ]
14567             }
14568         };
14569         
14570         var combobox = {
14571             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14572             cn: [
14573                 box
14574             ]
14575         };
14576         
14577         if(!this.multiple && this.showToggleBtn){
14578             
14579             var caret = {
14580                         tag: 'span',
14581                         cls: 'caret'
14582             };
14583             
14584             if (this.caret != false) {
14585                 caret = {
14586                      tag: 'i',
14587                      cls: 'fa fa-' + this.caret
14588                 };
14589                 
14590             }
14591             
14592             combobox.cn.push({
14593                 tag :'span',
14594                 cls : 'input-group-addon btn dropdown-toggle',
14595                 cn : [
14596                     caret,
14597                     {
14598                         tag: 'span',
14599                         cls: 'combobox-clear',
14600                         cn  : [
14601                             {
14602                                 tag : 'i',
14603                                 cls: 'icon-remove'
14604                             }
14605                         ]
14606                     }
14607                 ]
14608
14609             })
14610         }
14611         
14612         if(this.multiple){
14613             combobox.cls += ' roo-select2-container-multi';
14614         }
14615         
14616         var align = this.labelAlign || this.parentLabelAlign();
14617         
14618         if (align ==='left' && this.fieldLabel.length) {
14619
14620             cfg.cn = [
14621                 {
14622                    tag : 'i',
14623                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14624                    tooltip : 'This field is required'
14625                 },
14626                 {
14627                     tag: 'label',
14628                     cls : 'control-label',
14629                     html : this.fieldLabel
14630
14631                 },
14632                 {
14633                     cls : '', 
14634                     cn: [
14635                         combobox
14636                     ]
14637                 }
14638             ];
14639             
14640             var labelCfg = cfg.cn[1];
14641             var contentCfg = cfg.cn[2];
14642             
14643
14644             if(this.indicatorpos == 'right'){
14645                 cfg.cn = [
14646                     {
14647                         tag: 'label',
14648                         cls : 'control-label',
14649                         html : this.fieldLabel,
14650                         cn : [
14651                             {
14652                                tag : 'i',
14653                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14654                                tooltip : 'This field is required'
14655                             }
14656                         ]
14657                     },
14658                     {
14659                         cls : '', 
14660                         cn: [
14661                             combobox
14662                         ]
14663                     }
14664                 ];
14665             }
14666             
14667             labelCfg = cfg.cn[0];
14668             contentCfg = cfg.cn[2];
14669             
14670             if(this.labelWidth > 12){
14671                 labelCfg.style = "width: " + this.labelWidth + 'px';
14672             }
14673             
14674             if(this.labelWidth < 13 && this.labelmd == 0){
14675                 this.labelmd = this.labelWidth;
14676             }
14677             
14678             if(this.labellg > 0){
14679                 labelCfg.cls += ' col-lg-' + this.labellg;
14680                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14681             }
14682             
14683             if(this.labelmd > 0){
14684                 labelCfg.cls += ' col-md-' + this.labelmd;
14685                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14686             }
14687             
14688             if(this.labelsm > 0){
14689                 labelCfg.cls += ' col-sm-' + this.labelsm;
14690                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14691             }
14692             
14693             if(this.labelxs > 0){
14694                 labelCfg.cls += ' col-xs-' + this.labelxs;
14695                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14696             }
14697                 
14698                 
14699         } else if ( this.fieldLabel.length) {
14700             cfg.cn = [
14701                 {
14702                    tag : 'i',
14703                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14704                    tooltip : 'This field is required'
14705                 },
14706                 {
14707                     tag: 'label',
14708                     cls : 'control-label',
14709                     html : this.fieldLabel
14710
14711                 },
14712                 {
14713                     cls : '', 
14714                     cn: [
14715                         combobox
14716                     ]
14717                 }
14718             ];
14719             
14720             if(this.indicatorpos == 'right'){
14721                 cfg.cn = [
14722                     {
14723                         tag: 'label',
14724                         cls : 'control-label',
14725                         html : this.fieldLabel,
14726                         cn : [
14727                             {
14728                                tag : 'i',
14729                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14730                                tooltip : 'This field is required'
14731                             }
14732                         ]
14733                     },
14734                     {
14735                         cls : '', 
14736                         cn: [
14737                             combobox
14738                         ]
14739                     }
14740                 ];
14741             }
14742         } else {
14743             cfg.cn = combobox;    
14744         }
14745         
14746         
14747         var settings = this;
14748         
14749         ['xs','sm','md','lg'].map(function(size){
14750             if (settings[size]) {
14751                 cfg.cls += ' col-' + size + '-' + settings[size];
14752             }
14753         });
14754         
14755         return cfg;
14756     },
14757     
14758     initTouchView : function()
14759     {
14760         this.renderTouchView();
14761         
14762         this.touchViewEl.on('scroll', function(){
14763             this.el.dom.scrollTop = 0;
14764         }, this);
14765         
14766         this.originalValue = this.getValue();
14767         
14768         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14769         
14770         this.inputEl().on("click", this.showTouchView, this);
14771         if (this.triggerEl) {
14772             this.triggerEl.on("click", this.showTouchView, this);
14773         }
14774         
14775         
14776         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14777         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14778         
14779         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14780         
14781         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14782         this.store.on('load', this.onTouchViewLoad, this);
14783         this.store.on('loadexception', this.onTouchViewLoadException, this);
14784         
14785         if(this.hiddenName){
14786             
14787             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14788             
14789             this.hiddenField.dom.value =
14790                 this.hiddenValue !== undefined ? this.hiddenValue :
14791                 this.value !== undefined ? this.value : '';
14792         
14793             this.el.dom.removeAttribute('name');
14794             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14795         }
14796         
14797         if(this.multiple){
14798             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14799             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14800         }
14801         
14802         if(this.removable && !this.multiple){
14803             var close = this.closeTriggerEl();
14804             if(close){
14805                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14806                 close.on('click', this.removeBtnClick, this, close);
14807             }
14808         }
14809         /*
14810          * fix the bug in Safari iOS8
14811          */
14812         this.inputEl().on("focus", function(e){
14813             document.activeElement.blur();
14814         }, this);
14815         
14816         return;
14817         
14818         
14819     },
14820     
14821     renderTouchView : function()
14822     {
14823         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14824         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14825         
14826         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14827         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14828         
14829         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14830         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14831         this.touchViewBodyEl.setStyle('overflow', 'auto');
14832         
14833         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14834         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14835         
14836         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14837         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14838         
14839     },
14840     
14841     showTouchView : function()
14842     {
14843         if(this.disabled){
14844             return;
14845         }
14846         
14847         this.touchViewHeaderEl.hide();
14848
14849         if(this.modalTitle.length){
14850             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14851             this.touchViewHeaderEl.show();
14852         }
14853
14854         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14855         this.touchViewEl.show();
14856
14857         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14858         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14859                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14860
14861         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14862
14863         if(this.modalTitle.length){
14864             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14865         }
14866         
14867         this.touchViewBodyEl.setHeight(bodyHeight);
14868
14869         if(this.animate){
14870             var _this = this;
14871             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14872         }else{
14873             this.touchViewEl.addClass('in');
14874         }
14875
14876         this.doTouchViewQuery();
14877         
14878     },
14879     
14880     hideTouchView : function()
14881     {
14882         this.touchViewEl.removeClass('in');
14883
14884         if(this.animate){
14885             var _this = this;
14886             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14887         }else{
14888             this.touchViewEl.setStyle('display', 'none');
14889         }
14890         
14891     },
14892     
14893     setTouchViewValue : function()
14894     {
14895         if(this.multiple){
14896             this.clearItem();
14897         
14898             var _this = this;
14899
14900             Roo.each(this.tickItems, function(o){
14901                 this.addItem(o);
14902             }, this);
14903         }
14904         
14905         this.hideTouchView();
14906     },
14907     
14908     doTouchViewQuery : function()
14909     {
14910         var qe = {
14911             query: '',
14912             forceAll: true,
14913             combo: this,
14914             cancel:false
14915         };
14916         
14917         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14918             return false;
14919         }
14920         
14921         if(!this.alwaysQuery || this.mode == 'local'){
14922             this.onTouchViewLoad();
14923             return;
14924         }
14925         
14926         this.store.load();
14927     },
14928     
14929     onTouchViewBeforeLoad : function(combo,opts)
14930     {
14931         return;
14932     },
14933
14934     // private
14935     onTouchViewLoad : function()
14936     {
14937         if(this.store.getCount() < 1){
14938             this.onTouchViewEmptyResults();
14939             return;
14940         }
14941         
14942         this.clearTouchView();
14943         
14944         var rawValue = this.getRawValue();
14945         
14946         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14947         
14948         this.tickItems = [];
14949         
14950         this.store.data.each(function(d, rowIndex){
14951             var row = this.touchViewListGroup.createChild(template);
14952             
14953             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14954                 row.addClass(d.data.cls);
14955             }
14956             
14957             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14958                 var cfg = {
14959                     data : d.data,
14960                     html : d.data[this.displayField]
14961                 };
14962                 
14963                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14964                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14965                 }
14966             }
14967             row.removeClass('selected');
14968             if(!this.multiple && this.valueField &&
14969                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14970             {
14971                 // radio buttons..
14972                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14973                 row.addClass('selected');
14974             }
14975             
14976             if(this.multiple && this.valueField &&
14977                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14978             {
14979                 
14980                 // checkboxes...
14981                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14982                 this.tickItems.push(d.data);
14983             }
14984             
14985             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14986             
14987         }, this);
14988         
14989         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14990         
14991         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14992
14993         if(this.modalTitle.length){
14994             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14995         }
14996
14997         var listHeight = this.touchViewListGroup.getHeight();
14998         
14999         var _this = this;
15000         
15001         if(firstChecked && listHeight > bodyHeight){
15002             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15003         }
15004         
15005     },
15006     
15007     onTouchViewLoadException : function()
15008     {
15009         this.hideTouchView();
15010     },
15011     
15012     onTouchViewEmptyResults : function()
15013     {
15014         this.clearTouchView();
15015         
15016         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15017         
15018         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15019         
15020     },
15021     
15022     clearTouchView : function()
15023     {
15024         this.touchViewListGroup.dom.innerHTML = '';
15025     },
15026     
15027     onTouchViewClick : function(e, el, o)
15028     {
15029         e.preventDefault();
15030         
15031         var row = o.row;
15032         var rowIndex = o.rowIndex;
15033         
15034         var r = this.store.getAt(rowIndex);
15035         
15036         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15037             
15038             if(!this.multiple){
15039                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15040                     c.dom.removeAttribute('checked');
15041                 }, this);
15042
15043                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15044
15045                 this.setFromData(r.data);
15046
15047                 var close = this.closeTriggerEl();
15048
15049                 if(close){
15050                     close.show();
15051                 }
15052
15053                 this.hideTouchView();
15054
15055                 this.fireEvent('select', this, r, rowIndex);
15056
15057                 return;
15058             }
15059
15060             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15061                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15062                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15063                 return;
15064             }
15065
15066             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15067             this.addItem(r.data);
15068             this.tickItems.push(r.data);
15069         }
15070     },
15071     
15072     getAutoCreateNativeIOS : function()
15073     {
15074         var cfg = {
15075             cls: 'form-group' //input-group,
15076         };
15077         
15078         var combobox =  {
15079             tag: 'select',
15080             cls : 'roo-ios-select'
15081         };
15082         
15083         if (this.name) {
15084             combobox.name = this.name;
15085         }
15086         
15087         if (this.disabled) {
15088             combobox.disabled = true;
15089         }
15090         
15091         var settings = this;
15092         
15093         ['xs','sm','md','lg'].map(function(size){
15094             if (settings[size]) {
15095                 cfg.cls += ' col-' + size + '-' + settings[size];
15096             }
15097         });
15098         
15099         cfg.cn = combobox;
15100         
15101         return cfg;
15102         
15103     },
15104     
15105     initIOSView : function()
15106     {
15107         this.store.on('load', this.onIOSViewLoad, this);
15108         
15109         return;
15110     },
15111     
15112     onIOSViewLoad : function()
15113     {
15114         if(this.store.getCount() < 1){
15115             return;
15116         }
15117         
15118         this.clearIOSView();
15119         
15120         if(this.allowBlank) {
15121             
15122             var default_text = '-- SELECT --';
15123             
15124             var opt = this.inputEl().createChild({
15125                 tag: 'option',
15126                 value : 0,
15127                 html : default_text
15128             });
15129             
15130             var o = {};
15131             o[this.valueField] = 0;
15132             o[this.displayField] = default_text;
15133             
15134             this.ios_options.push({
15135                 data : o,
15136                 el : opt
15137             });
15138             
15139         }
15140         
15141         this.store.data.each(function(d, rowIndex){
15142             
15143             var html = '';
15144             
15145             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15146                 html = d.data[this.displayField];
15147             }
15148             
15149             var value = '';
15150             
15151             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15152                 value = d.data[this.valueField];
15153             }
15154             
15155             var option = {
15156                 tag: 'option',
15157                 value : value,
15158                 html : html
15159             };
15160             
15161             if(this.value == d.data[this.valueField]){
15162                 option['selected'] = true;
15163             }
15164             
15165             var opt = this.inputEl().createChild(option);
15166             
15167             this.ios_options.push({
15168                 data : d.data,
15169                 el : opt
15170             });
15171             
15172         }, this);
15173         
15174         this.inputEl().on('change', function(){
15175            this.fireEvent('select', this);
15176         }, this);
15177         
15178     },
15179     
15180     clearIOSView: function()
15181     {
15182         this.inputEl().dom.innerHTML = '';
15183         
15184         this.ios_options = [];
15185     },
15186     
15187     setIOSValue: function(v)
15188     {
15189         this.value = v;
15190         
15191         if(!this.ios_options){
15192             return;
15193         }
15194         
15195         Roo.each(this.ios_options, function(opts){
15196            
15197            opts.el.dom.removeAttribute('selected');
15198            
15199            if(opts.data[this.valueField] != v){
15200                return;
15201            }
15202            
15203            opts.el.dom.setAttribute('selected', true);
15204            
15205         }, this);
15206     }
15207
15208     /** 
15209     * @cfg {Boolean} grow 
15210     * @hide 
15211     */
15212     /** 
15213     * @cfg {Number} growMin 
15214     * @hide 
15215     */
15216     /** 
15217     * @cfg {Number} growMax 
15218     * @hide 
15219     */
15220     /**
15221      * @hide
15222      * @method autoSize
15223      */
15224 });
15225
15226 Roo.apply(Roo.bootstrap.ComboBox,  {
15227     
15228     header : {
15229         tag: 'div',
15230         cls: 'modal-header',
15231         cn: [
15232             {
15233                 tag: 'h4',
15234                 cls: 'modal-title'
15235             }
15236         ]
15237     },
15238     
15239     body : {
15240         tag: 'div',
15241         cls: 'modal-body',
15242         cn: [
15243             {
15244                 tag: 'ul',
15245                 cls: 'list-group'
15246             }
15247         ]
15248     },
15249     
15250     listItemRadio : {
15251         tag: 'li',
15252         cls: 'list-group-item',
15253         cn: [
15254             {
15255                 tag: 'span',
15256                 cls: 'roo-combobox-list-group-item-value'
15257             },
15258             {
15259                 tag: 'div',
15260                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15261                 cn: [
15262                     {
15263                         tag: 'input',
15264                         type: 'radio'
15265                     },
15266                     {
15267                         tag: 'label'
15268                     }
15269                 ]
15270             }
15271         ]
15272     },
15273     
15274     listItemCheckbox : {
15275         tag: 'li',
15276         cls: 'list-group-item',
15277         cn: [
15278             {
15279                 tag: 'span',
15280                 cls: 'roo-combobox-list-group-item-value'
15281             },
15282             {
15283                 tag: 'div',
15284                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15285                 cn: [
15286                     {
15287                         tag: 'input',
15288                         type: 'checkbox'
15289                     },
15290                     {
15291                         tag: 'label'
15292                     }
15293                 ]
15294             }
15295         ]
15296     },
15297     
15298     emptyResult : {
15299         tag: 'div',
15300         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15301     },
15302     
15303     footer : {
15304         tag: 'div',
15305         cls: 'modal-footer',
15306         cn: [
15307             {
15308                 tag: 'div',
15309                 cls: 'row',
15310                 cn: [
15311                     {
15312                         tag: 'div',
15313                         cls: 'col-xs-6 text-left',
15314                         cn: {
15315                             tag: 'button',
15316                             cls: 'btn btn-danger roo-touch-view-cancel',
15317                             html: 'Cancel'
15318                         }
15319                     },
15320                     {
15321                         tag: 'div',
15322                         cls: 'col-xs-6 text-right',
15323                         cn: {
15324                             tag: 'button',
15325                             cls: 'btn btn-success roo-touch-view-ok',
15326                             html: 'OK'
15327                         }
15328                     }
15329                 ]
15330             }
15331         ]
15332         
15333     }
15334 });
15335
15336 Roo.apply(Roo.bootstrap.ComboBox,  {
15337     
15338     touchViewTemplate : {
15339         tag: 'div',
15340         cls: 'modal fade roo-combobox-touch-view',
15341         cn: [
15342             {
15343                 tag: 'div',
15344                 cls: 'modal-dialog',
15345                 style : 'position:fixed', // we have to fix position....
15346                 cn: [
15347                     {
15348                         tag: 'div',
15349                         cls: 'modal-content',
15350                         cn: [
15351                             Roo.bootstrap.ComboBox.header,
15352                             Roo.bootstrap.ComboBox.body,
15353                             Roo.bootstrap.ComboBox.footer
15354                         ]
15355                     }
15356                 ]
15357             }
15358         ]
15359     }
15360 });/*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370
15371 /**
15372  * @class Roo.View
15373  * @extends Roo.util.Observable
15374  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15375  * This class also supports single and multi selection modes. <br>
15376  * Create a data model bound view:
15377  <pre><code>
15378  var store = new Roo.data.Store(...);
15379
15380  var view = new Roo.View({
15381     el : "my-element",
15382     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15383  
15384     singleSelect: true,
15385     selectedClass: "ydataview-selected",
15386     store: store
15387  });
15388
15389  // listen for node click?
15390  view.on("click", function(vw, index, node, e){
15391  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15392  });
15393
15394  // load XML data
15395  dataModel.load("foobar.xml");
15396  </code></pre>
15397  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15398  * <br><br>
15399  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15400  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15401  * 
15402  * Note: old style constructor is still suported (container, template, config)
15403  * 
15404  * @constructor
15405  * Create a new View
15406  * @param {Object} config The config object
15407  * 
15408  */
15409 Roo.View = function(config, depreciated_tpl, depreciated_config){
15410     
15411     this.parent = false;
15412     
15413     if (typeof(depreciated_tpl) == 'undefined') {
15414         // new way.. - universal constructor.
15415         Roo.apply(this, config);
15416         this.el  = Roo.get(this.el);
15417     } else {
15418         // old format..
15419         this.el  = Roo.get(config);
15420         this.tpl = depreciated_tpl;
15421         Roo.apply(this, depreciated_config);
15422     }
15423     this.wrapEl  = this.el.wrap().wrap();
15424     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15425     
15426     
15427     if(typeof(this.tpl) == "string"){
15428         this.tpl = new Roo.Template(this.tpl);
15429     } else {
15430         // support xtype ctors..
15431         this.tpl = new Roo.factory(this.tpl, Roo);
15432     }
15433     
15434     
15435     this.tpl.compile();
15436     
15437     /** @private */
15438     this.addEvents({
15439         /**
15440          * @event beforeclick
15441          * Fires before a click is processed. Returns false to cancel the default action.
15442          * @param {Roo.View} this
15443          * @param {Number} index The index of the target node
15444          * @param {HTMLElement} node The target node
15445          * @param {Roo.EventObject} e The raw event object
15446          */
15447             "beforeclick" : true,
15448         /**
15449          * @event click
15450          * Fires when a template node is clicked.
15451          * @param {Roo.View} this
15452          * @param {Number} index The index of the target node
15453          * @param {HTMLElement} node The target node
15454          * @param {Roo.EventObject} e The raw event object
15455          */
15456             "click" : true,
15457         /**
15458          * @event dblclick
15459          * Fires when a template node is double clicked.
15460          * @param {Roo.View} this
15461          * @param {Number} index The index of the target node
15462          * @param {HTMLElement} node The target node
15463          * @param {Roo.EventObject} e The raw event object
15464          */
15465             "dblclick" : true,
15466         /**
15467          * @event contextmenu
15468          * Fires when a template node is right clicked.
15469          * @param {Roo.View} this
15470          * @param {Number} index The index of the target node
15471          * @param {HTMLElement} node The target node
15472          * @param {Roo.EventObject} e The raw event object
15473          */
15474             "contextmenu" : true,
15475         /**
15476          * @event selectionchange
15477          * Fires when the selected nodes change.
15478          * @param {Roo.View} this
15479          * @param {Array} selections Array of the selected nodes
15480          */
15481             "selectionchange" : true,
15482     
15483         /**
15484          * @event beforeselect
15485          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15486          * @param {Roo.View} this
15487          * @param {HTMLElement} node The node to be selected
15488          * @param {Array} selections Array of currently selected nodes
15489          */
15490             "beforeselect" : true,
15491         /**
15492          * @event preparedata
15493          * Fires on every row to render, to allow you to change the data.
15494          * @param {Roo.View} this
15495          * @param {Object} data to be rendered (change this)
15496          */
15497           "preparedata" : true
15498           
15499           
15500         });
15501
15502
15503
15504     this.el.on({
15505         "click": this.onClick,
15506         "dblclick": this.onDblClick,
15507         "contextmenu": this.onContextMenu,
15508         scope:this
15509     });
15510
15511     this.selections = [];
15512     this.nodes = [];
15513     this.cmp = new Roo.CompositeElementLite([]);
15514     if(this.store){
15515         this.store = Roo.factory(this.store, Roo.data);
15516         this.setStore(this.store, true);
15517     }
15518     
15519     if ( this.footer && this.footer.xtype) {
15520            
15521          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15522         
15523         this.footer.dataSource = this.store;
15524         this.footer.container = fctr;
15525         this.footer = Roo.factory(this.footer, Roo);
15526         fctr.insertFirst(this.el);
15527         
15528         // this is a bit insane - as the paging toolbar seems to detach the el..
15529 //        dom.parentNode.parentNode.parentNode
15530          // they get detached?
15531     }
15532     
15533     
15534     Roo.View.superclass.constructor.call(this);
15535     
15536     
15537 };
15538
15539 Roo.extend(Roo.View, Roo.util.Observable, {
15540     
15541      /**
15542      * @cfg {Roo.data.Store} store Data store to load data from.
15543      */
15544     store : false,
15545     
15546     /**
15547      * @cfg {String|Roo.Element} el The container element.
15548      */
15549     el : '',
15550     
15551     /**
15552      * @cfg {String|Roo.Template} tpl The template used by this View 
15553      */
15554     tpl : false,
15555     /**
15556      * @cfg {String} dataName the named area of the template to use as the data area
15557      *                          Works with domtemplates roo-name="name"
15558      */
15559     dataName: false,
15560     /**
15561      * @cfg {String} selectedClass The css class to add to selected nodes
15562      */
15563     selectedClass : "x-view-selected",
15564      /**
15565      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15566      */
15567     emptyText : "",
15568     
15569     /**
15570      * @cfg {String} text to display on mask (default Loading)
15571      */
15572     mask : false,
15573     /**
15574      * @cfg {Boolean} multiSelect Allow multiple selection
15575      */
15576     multiSelect : false,
15577     /**
15578      * @cfg {Boolean} singleSelect Allow single selection
15579      */
15580     singleSelect:  false,
15581     
15582     /**
15583      * @cfg {Boolean} toggleSelect - selecting 
15584      */
15585     toggleSelect : false,
15586     
15587     /**
15588      * @cfg {Boolean} tickable - selecting 
15589      */
15590     tickable : false,
15591     
15592     /**
15593      * Returns the element this view is bound to.
15594      * @return {Roo.Element}
15595      */
15596     getEl : function(){
15597         return this.wrapEl;
15598     },
15599     
15600     
15601
15602     /**
15603      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15604      */
15605     refresh : function(){
15606         //Roo.log('refresh');
15607         var t = this.tpl;
15608         
15609         // if we are using something like 'domtemplate', then
15610         // the what gets used is:
15611         // t.applySubtemplate(NAME, data, wrapping data..)
15612         // the outer template then get' applied with
15613         //     the store 'extra data'
15614         // and the body get's added to the
15615         //      roo-name="data" node?
15616         //      <span class='roo-tpl-{name}'></span> ?????
15617         
15618         
15619         
15620         this.clearSelections();
15621         this.el.update("");
15622         var html = [];
15623         var records = this.store.getRange();
15624         if(records.length < 1) {
15625             
15626             // is this valid??  = should it render a template??
15627             
15628             this.el.update(this.emptyText);
15629             return;
15630         }
15631         var el = this.el;
15632         if (this.dataName) {
15633             this.el.update(t.apply(this.store.meta)); //????
15634             el = this.el.child('.roo-tpl-' + this.dataName);
15635         }
15636         
15637         for(var i = 0, len = records.length; i < len; i++){
15638             var data = this.prepareData(records[i].data, i, records[i]);
15639             this.fireEvent("preparedata", this, data, i, records[i]);
15640             
15641             var d = Roo.apply({}, data);
15642             
15643             if(this.tickable){
15644                 Roo.apply(d, {'roo-id' : Roo.id()});
15645                 
15646                 var _this = this;
15647             
15648                 Roo.each(this.parent.item, function(item){
15649                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15650                         return;
15651                     }
15652                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15653                 });
15654             }
15655             
15656             html[html.length] = Roo.util.Format.trim(
15657                 this.dataName ?
15658                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15659                     t.apply(d)
15660             );
15661         }
15662         
15663         
15664         
15665         el.update(html.join(""));
15666         this.nodes = el.dom.childNodes;
15667         this.updateIndexes(0);
15668     },
15669     
15670
15671     /**
15672      * Function to override to reformat the data that is sent to
15673      * the template for each node.
15674      * DEPRICATED - use the preparedata event handler.
15675      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15676      * a JSON object for an UpdateManager bound view).
15677      */
15678     prepareData : function(data, index, record)
15679     {
15680         this.fireEvent("preparedata", this, data, index, record);
15681         return data;
15682     },
15683
15684     onUpdate : function(ds, record){
15685         // Roo.log('on update');   
15686         this.clearSelections();
15687         var index = this.store.indexOf(record);
15688         var n = this.nodes[index];
15689         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15690         n.parentNode.removeChild(n);
15691         this.updateIndexes(index, index);
15692     },
15693
15694     
15695     
15696 // --------- FIXME     
15697     onAdd : function(ds, records, index)
15698     {
15699         //Roo.log(['on Add', ds, records, index] );        
15700         this.clearSelections();
15701         if(this.nodes.length == 0){
15702             this.refresh();
15703             return;
15704         }
15705         var n = this.nodes[index];
15706         for(var i = 0, len = records.length; i < len; i++){
15707             var d = this.prepareData(records[i].data, i, records[i]);
15708             if(n){
15709                 this.tpl.insertBefore(n, d);
15710             }else{
15711                 
15712                 this.tpl.append(this.el, d);
15713             }
15714         }
15715         this.updateIndexes(index);
15716     },
15717
15718     onRemove : function(ds, record, index){
15719        // Roo.log('onRemove');
15720         this.clearSelections();
15721         var el = this.dataName  ?
15722             this.el.child('.roo-tpl-' + this.dataName) :
15723             this.el; 
15724         
15725         el.dom.removeChild(this.nodes[index]);
15726         this.updateIndexes(index);
15727     },
15728
15729     /**
15730      * Refresh an individual node.
15731      * @param {Number} index
15732      */
15733     refreshNode : function(index){
15734         this.onUpdate(this.store, this.store.getAt(index));
15735     },
15736
15737     updateIndexes : function(startIndex, endIndex){
15738         var ns = this.nodes;
15739         startIndex = startIndex || 0;
15740         endIndex = endIndex || ns.length - 1;
15741         for(var i = startIndex; i <= endIndex; i++){
15742             ns[i].nodeIndex = i;
15743         }
15744     },
15745
15746     /**
15747      * Changes the data store this view uses and refresh the view.
15748      * @param {Store} store
15749      */
15750     setStore : function(store, initial){
15751         if(!initial && this.store){
15752             this.store.un("datachanged", this.refresh);
15753             this.store.un("add", this.onAdd);
15754             this.store.un("remove", this.onRemove);
15755             this.store.un("update", this.onUpdate);
15756             this.store.un("clear", this.refresh);
15757             this.store.un("beforeload", this.onBeforeLoad);
15758             this.store.un("load", this.onLoad);
15759             this.store.un("loadexception", this.onLoad);
15760         }
15761         if(store){
15762           
15763             store.on("datachanged", this.refresh, this);
15764             store.on("add", this.onAdd, this);
15765             store.on("remove", this.onRemove, this);
15766             store.on("update", this.onUpdate, this);
15767             store.on("clear", this.refresh, this);
15768             store.on("beforeload", this.onBeforeLoad, this);
15769             store.on("load", this.onLoad, this);
15770             store.on("loadexception", this.onLoad, this);
15771         }
15772         
15773         if(store){
15774             this.refresh();
15775         }
15776     },
15777     /**
15778      * onbeforeLoad - masks the loading area.
15779      *
15780      */
15781     onBeforeLoad : function(store,opts)
15782     {
15783          //Roo.log('onBeforeLoad');   
15784         if (!opts.add) {
15785             this.el.update("");
15786         }
15787         this.el.mask(this.mask ? this.mask : "Loading" ); 
15788     },
15789     onLoad : function ()
15790     {
15791         this.el.unmask();
15792     },
15793     
15794
15795     /**
15796      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15797      * @param {HTMLElement} node
15798      * @return {HTMLElement} The template node
15799      */
15800     findItemFromChild : function(node){
15801         var el = this.dataName  ?
15802             this.el.child('.roo-tpl-' + this.dataName,true) :
15803             this.el.dom; 
15804         
15805         if(!node || node.parentNode == el){
15806                     return node;
15807             }
15808             var p = node.parentNode;
15809             while(p && p != el){
15810             if(p.parentNode == el){
15811                 return p;
15812             }
15813             p = p.parentNode;
15814         }
15815             return null;
15816     },
15817
15818     /** @ignore */
15819     onClick : function(e){
15820         var item = this.findItemFromChild(e.getTarget());
15821         if(item){
15822             var index = this.indexOf(item);
15823             if(this.onItemClick(item, index, e) !== false){
15824                 this.fireEvent("click", this, index, item, e);
15825             }
15826         }else{
15827             this.clearSelections();
15828         }
15829     },
15830
15831     /** @ignore */
15832     onContextMenu : function(e){
15833         var item = this.findItemFromChild(e.getTarget());
15834         if(item){
15835             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15836         }
15837     },
15838
15839     /** @ignore */
15840     onDblClick : function(e){
15841         var item = this.findItemFromChild(e.getTarget());
15842         if(item){
15843             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15844         }
15845     },
15846
15847     onItemClick : function(item, index, e)
15848     {
15849         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15850             return false;
15851         }
15852         if (this.toggleSelect) {
15853             var m = this.isSelected(item) ? 'unselect' : 'select';
15854             //Roo.log(m);
15855             var _t = this;
15856             _t[m](item, true, false);
15857             return true;
15858         }
15859         if(this.multiSelect || this.singleSelect){
15860             if(this.multiSelect && e.shiftKey && this.lastSelection){
15861                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15862             }else{
15863                 this.select(item, this.multiSelect && e.ctrlKey);
15864                 this.lastSelection = item;
15865             }
15866             
15867             if(!this.tickable){
15868                 e.preventDefault();
15869             }
15870             
15871         }
15872         return true;
15873     },
15874
15875     /**
15876      * Get the number of selected nodes.
15877      * @return {Number}
15878      */
15879     getSelectionCount : function(){
15880         return this.selections.length;
15881     },
15882
15883     /**
15884      * Get the currently selected nodes.
15885      * @return {Array} An array of HTMLElements
15886      */
15887     getSelectedNodes : function(){
15888         return this.selections;
15889     },
15890
15891     /**
15892      * Get the indexes of the selected nodes.
15893      * @return {Array}
15894      */
15895     getSelectedIndexes : function(){
15896         var indexes = [], s = this.selections;
15897         for(var i = 0, len = s.length; i < len; i++){
15898             indexes.push(s[i].nodeIndex);
15899         }
15900         return indexes;
15901     },
15902
15903     /**
15904      * Clear all selections
15905      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15906      */
15907     clearSelections : function(suppressEvent){
15908         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15909             this.cmp.elements = this.selections;
15910             this.cmp.removeClass(this.selectedClass);
15911             this.selections = [];
15912             if(!suppressEvent){
15913                 this.fireEvent("selectionchange", this, this.selections);
15914             }
15915         }
15916     },
15917
15918     /**
15919      * Returns true if the passed node is selected
15920      * @param {HTMLElement/Number} node The node or node index
15921      * @return {Boolean}
15922      */
15923     isSelected : function(node){
15924         var s = this.selections;
15925         if(s.length < 1){
15926             return false;
15927         }
15928         node = this.getNode(node);
15929         return s.indexOf(node) !== -1;
15930     },
15931
15932     /**
15933      * Selects nodes.
15934      * @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
15935      * @param {Boolean} keepExisting (optional) true to keep existing selections
15936      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15937      */
15938     select : function(nodeInfo, keepExisting, suppressEvent){
15939         if(nodeInfo instanceof Array){
15940             if(!keepExisting){
15941                 this.clearSelections(true);
15942             }
15943             for(var i = 0, len = nodeInfo.length; i < len; i++){
15944                 this.select(nodeInfo[i], true, true);
15945             }
15946             return;
15947         } 
15948         var node = this.getNode(nodeInfo);
15949         if(!node || this.isSelected(node)){
15950             return; // already selected.
15951         }
15952         if(!keepExisting){
15953             this.clearSelections(true);
15954         }
15955         
15956         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15957             Roo.fly(node).addClass(this.selectedClass);
15958             this.selections.push(node);
15959             if(!suppressEvent){
15960                 this.fireEvent("selectionchange", this, this.selections);
15961             }
15962         }
15963         
15964         
15965     },
15966       /**
15967      * Unselects nodes.
15968      * @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
15969      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15970      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15971      */
15972     unselect : function(nodeInfo, keepExisting, suppressEvent)
15973     {
15974         if(nodeInfo instanceof Array){
15975             Roo.each(this.selections, function(s) {
15976                 this.unselect(s, nodeInfo);
15977             }, this);
15978             return;
15979         }
15980         var node = this.getNode(nodeInfo);
15981         if(!node || !this.isSelected(node)){
15982             //Roo.log("not selected");
15983             return; // not selected.
15984         }
15985         // fireevent???
15986         var ns = [];
15987         Roo.each(this.selections, function(s) {
15988             if (s == node ) {
15989                 Roo.fly(node).removeClass(this.selectedClass);
15990
15991                 return;
15992             }
15993             ns.push(s);
15994         },this);
15995         
15996         this.selections= ns;
15997         this.fireEvent("selectionchange", this, this.selections);
15998     },
15999
16000     /**
16001      * Gets a template node.
16002      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16003      * @return {HTMLElement} The node or null if it wasn't found
16004      */
16005     getNode : function(nodeInfo){
16006         if(typeof nodeInfo == "string"){
16007             return document.getElementById(nodeInfo);
16008         }else if(typeof nodeInfo == "number"){
16009             return this.nodes[nodeInfo];
16010         }
16011         return nodeInfo;
16012     },
16013
16014     /**
16015      * Gets a range template nodes.
16016      * @param {Number} startIndex
16017      * @param {Number} endIndex
16018      * @return {Array} An array of nodes
16019      */
16020     getNodes : function(start, end){
16021         var ns = this.nodes;
16022         start = start || 0;
16023         end = typeof end == "undefined" ? ns.length - 1 : end;
16024         var nodes = [];
16025         if(start <= end){
16026             for(var i = start; i <= end; i++){
16027                 nodes.push(ns[i]);
16028             }
16029         } else{
16030             for(var i = start; i >= end; i--){
16031                 nodes.push(ns[i]);
16032             }
16033         }
16034         return nodes;
16035     },
16036
16037     /**
16038      * Finds the index of the passed node
16039      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16040      * @return {Number} The index of the node or -1
16041      */
16042     indexOf : function(node){
16043         node = this.getNode(node);
16044         if(typeof node.nodeIndex == "number"){
16045             return node.nodeIndex;
16046         }
16047         var ns = this.nodes;
16048         for(var i = 0, len = ns.length; i < len; i++){
16049             if(ns[i] == node){
16050                 return i;
16051             }
16052         }
16053         return -1;
16054     }
16055 });
16056 /*
16057  * - LGPL
16058  *
16059  * based on jquery fullcalendar
16060  * 
16061  */
16062
16063 Roo.bootstrap = Roo.bootstrap || {};
16064 /**
16065  * @class Roo.bootstrap.Calendar
16066  * @extends Roo.bootstrap.Component
16067  * Bootstrap Calendar class
16068  * @cfg {Boolean} loadMask (true|false) default false
16069  * @cfg {Object} header generate the user specific header of the calendar, default false
16070
16071  * @constructor
16072  * Create a new Container
16073  * @param {Object} config The config object
16074  */
16075
16076
16077
16078 Roo.bootstrap.Calendar = function(config){
16079     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16080      this.addEvents({
16081         /**
16082              * @event select
16083              * Fires when a date is selected
16084              * @param {DatePicker} this
16085              * @param {Date} date The selected date
16086              */
16087         'select': true,
16088         /**
16089              * @event monthchange
16090              * Fires when the displayed month changes 
16091              * @param {DatePicker} this
16092              * @param {Date} date The selected month
16093              */
16094         'monthchange': true,
16095         /**
16096              * @event evententer
16097              * Fires when mouse over an event
16098              * @param {Calendar} this
16099              * @param {event} Event
16100              */
16101         'evententer': true,
16102         /**
16103              * @event eventleave
16104              * Fires when the mouse leaves an
16105              * @param {Calendar} this
16106              * @param {event}
16107              */
16108         'eventleave': true,
16109         /**
16110              * @event eventclick
16111              * Fires when the mouse click an
16112              * @param {Calendar} this
16113              * @param {event}
16114              */
16115         'eventclick': true
16116         
16117     });
16118
16119 };
16120
16121 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16122     
16123      /**
16124      * @cfg {Number} startDay
16125      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16126      */
16127     startDay : 0,
16128     
16129     loadMask : false,
16130     
16131     header : false,
16132       
16133     getAutoCreate : function(){
16134         
16135         
16136         var fc_button = function(name, corner, style, content ) {
16137             return Roo.apply({},{
16138                 tag : 'span',
16139                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16140                          (corner.length ?
16141                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16142                             ''
16143                         ),
16144                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16145                 unselectable: 'on'
16146             });
16147         };
16148         
16149         var header = {};
16150         
16151         if(!this.header){
16152             header = {
16153                 tag : 'table',
16154                 cls : 'fc-header',
16155                 style : 'width:100%',
16156                 cn : [
16157                     {
16158                         tag: 'tr',
16159                         cn : [
16160                             {
16161                                 tag : 'td',
16162                                 cls : 'fc-header-left',
16163                                 cn : [
16164                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16165                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16166                                     { tag: 'span', cls: 'fc-header-space' },
16167                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16168
16169
16170                                 ]
16171                             },
16172
16173                             {
16174                                 tag : 'td',
16175                                 cls : 'fc-header-center',
16176                                 cn : [
16177                                     {
16178                                         tag: 'span',
16179                                         cls: 'fc-header-title',
16180                                         cn : {
16181                                             tag: 'H2',
16182                                             html : 'month / year'
16183                                         }
16184                                     }
16185
16186                                 ]
16187                             },
16188                             {
16189                                 tag : 'td',
16190                                 cls : 'fc-header-right',
16191                                 cn : [
16192                               /*      fc_button('month', 'left', '', 'month' ),
16193                                     fc_button('week', '', '', 'week' ),
16194                                     fc_button('day', 'right', '', 'day' )
16195                                 */    
16196
16197                                 ]
16198                             }
16199
16200                         ]
16201                     }
16202                 ]
16203             };
16204         }
16205         
16206         header = this.header;
16207         
16208        
16209         var cal_heads = function() {
16210             var ret = [];
16211             // fixme - handle this.
16212             
16213             for (var i =0; i < Date.dayNames.length; i++) {
16214                 var d = Date.dayNames[i];
16215                 ret.push({
16216                     tag: 'th',
16217                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16218                     html : d.substring(0,3)
16219                 });
16220                 
16221             }
16222             ret[0].cls += ' fc-first';
16223             ret[6].cls += ' fc-last';
16224             return ret;
16225         };
16226         var cal_cell = function(n) {
16227             return  {
16228                 tag: 'td',
16229                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16230                 cn : [
16231                     {
16232                         cn : [
16233                             {
16234                                 cls: 'fc-day-number',
16235                                 html: 'D'
16236                             },
16237                             {
16238                                 cls: 'fc-day-content',
16239                              
16240                                 cn : [
16241                                      {
16242                                         style: 'position: relative;' // height: 17px;
16243                                     }
16244                                 ]
16245                             }
16246                             
16247                             
16248                         ]
16249                     }
16250                 ]
16251                 
16252             }
16253         };
16254         var cal_rows = function() {
16255             
16256             var ret = [];
16257             for (var r = 0; r < 6; r++) {
16258                 var row= {
16259                     tag : 'tr',
16260                     cls : 'fc-week',
16261                     cn : []
16262                 };
16263                 
16264                 for (var i =0; i < Date.dayNames.length; i++) {
16265                     var d = Date.dayNames[i];
16266                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16267
16268                 }
16269                 row.cn[0].cls+=' fc-first';
16270                 row.cn[0].cn[0].style = 'min-height:90px';
16271                 row.cn[6].cls+=' fc-last';
16272                 ret.push(row);
16273                 
16274             }
16275             ret[0].cls += ' fc-first';
16276             ret[4].cls += ' fc-prev-last';
16277             ret[5].cls += ' fc-last';
16278             return ret;
16279             
16280         };
16281         
16282         var cal_table = {
16283             tag: 'table',
16284             cls: 'fc-border-separate',
16285             style : 'width:100%',
16286             cellspacing  : 0,
16287             cn : [
16288                 { 
16289                     tag: 'thead',
16290                     cn : [
16291                         { 
16292                             tag: 'tr',
16293                             cls : 'fc-first fc-last',
16294                             cn : cal_heads()
16295                         }
16296                     ]
16297                 },
16298                 { 
16299                     tag: 'tbody',
16300                     cn : cal_rows()
16301                 }
16302                   
16303             ]
16304         };
16305          
16306          var cfg = {
16307             cls : 'fc fc-ltr',
16308             cn : [
16309                 header,
16310                 {
16311                     cls : 'fc-content',
16312                     style : "position: relative;",
16313                     cn : [
16314                         {
16315                             cls : 'fc-view fc-view-month fc-grid',
16316                             style : 'position: relative',
16317                             unselectable : 'on',
16318                             cn : [
16319                                 {
16320                                     cls : 'fc-event-container',
16321                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16322                                 },
16323                                 cal_table
16324                             ]
16325                         }
16326                     ]
16327     
16328                 }
16329            ] 
16330             
16331         };
16332         
16333          
16334         
16335         return cfg;
16336     },
16337     
16338     
16339     initEvents : function()
16340     {
16341         if(!this.store){
16342             throw "can not find store for calendar";
16343         }
16344         
16345         var mark = {
16346             tag: "div",
16347             cls:"x-dlg-mask",
16348             style: "text-align:center",
16349             cn: [
16350                 {
16351                     tag: "div",
16352                     style: "background-color:white;width:50%;margin:250 auto",
16353                     cn: [
16354                         {
16355                             tag: "img",
16356                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16357                         },
16358                         {
16359                             tag: "span",
16360                             html: "Loading"
16361                         }
16362                         
16363                     ]
16364                 }
16365             ]
16366         };
16367         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16368         
16369         var size = this.el.select('.fc-content', true).first().getSize();
16370         this.maskEl.setSize(size.width, size.height);
16371         this.maskEl.enableDisplayMode("block");
16372         if(!this.loadMask){
16373             this.maskEl.hide();
16374         }
16375         
16376         this.store = Roo.factory(this.store, Roo.data);
16377         this.store.on('load', this.onLoad, this);
16378         this.store.on('beforeload', this.onBeforeLoad, this);
16379         
16380         this.resize();
16381         
16382         this.cells = this.el.select('.fc-day',true);
16383         //Roo.log(this.cells);
16384         this.textNodes = this.el.query('.fc-day-number');
16385         this.cells.addClassOnOver('fc-state-hover');
16386         
16387         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16388         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16389         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16390         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16391         
16392         this.on('monthchange', this.onMonthChange, this);
16393         
16394         this.update(new Date().clearTime());
16395     },
16396     
16397     resize : function() {
16398         var sz  = this.el.getSize();
16399         
16400         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16401         this.el.select('.fc-day-content div',true).setHeight(34);
16402     },
16403     
16404     
16405     // private
16406     showPrevMonth : function(e){
16407         this.update(this.activeDate.add("mo", -1));
16408     },
16409     showToday : function(e){
16410         this.update(new Date().clearTime());
16411     },
16412     // private
16413     showNextMonth : function(e){
16414         this.update(this.activeDate.add("mo", 1));
16415     },
16416
16417     // private
16418     showPrevYear : function(){
16419         this.update(this.activeDate.add("y", -1));
16420     },
16421
16422     // private
16423     showNextYear : function(){
16424         this.update(this.activeDate.add("y", 1));
16425     },
16426
16427     
16428    // private
16429     update : function(date)
16430     {
16431         var vd = this.activeDate;
16432         this.activeDate = date;
16433 //        if(vd && this.el){
16434 //            var t = date.getTime();
16435 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16436 //                Roo.log('using add remove');
16437 //                
16438 //                this.fireEvent('monthchange', this, date);
16439 //                
16440 //                this.cells.removeClass("fc-state-highlight");
16441 //                this.cells.each(function(c){
16442 //                   if(c.dateValue == t){
16443 //                       c.addClass("fc-state-highlight");
16444 //                       setTimeout(function(){
16445 //                            try{c.dom.firstChild.focus();}catch(e){}
16446 //                       }, 50);
16447 //                       return false;
16448 //                   }
16449 //                   return true;
16450 //                });
16451 //                return;
16452 //            }
16453 //        }
16454         
16455         var days = date.getDaysInMonth();
16456         
16457         var firstOfMonth = date.getFirstDateOfMonth();
16458         var startingPos = firstOfMonth.getDay()-this.startDay;
16459         
16460         if(startingPos < this.startDay){
16461             startingPos += 7;
16462         }
16463         
16464         var pm = date.add(Date.MONTH, -1);
16465         var prevStart = pm.getDaysInMonth()-startingPos;
16466 //        
16467         this.cells = this.el.select('.fc-day',true);
16468         this.textNodes = this.el.query('.fc-day-number');
16469         this.cells.addClassOnOver('fc-state-hover');
16470         
16471         var cells = this.cells.elements;
16472         var textEls = this.textNodes;
16473         
16474         Roo.each(cells, function(cell){
16475             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16476         });
16477         
16478         days += startingPos;
16479
16480         // convert everything to numbers so it's fast
16481         var day = 86400000;
16482         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16483         //Roo.log(d);
16484         //Roo.log(pm);
16485         //Roo.log(prevStart);
16486         
16487         var today = new Date().clearTime().getTime();
16488         var sel = date.clearTime().getTime();
16489         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16490         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16491         var ddMatch = this.disabledDatesRE;
16492         var ddText = this.disabledDatesText;
16493         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16494         var ddaysText = this.disabledDaysText;
16495         var format = this.format;
16496         
16497         var setCellClass = function(cal, cell){
16498             cell.row = 0;
16499             cell.events = [];
16500             cell.more = [];
16501             //Roo.log('set Cell Class');
16502             cell.title = "";
16503             var t = d.getTime();
16504             
16505             //Roo.log(d);
16506             
16507             cell.dateValue = t;
16508             if(t == today){
16509                 cell.className += " fc-today";
16510                 cell.className += " fc-state-highlight";
16511                 cell.title = cal.todayText;
16512             }
16513             if(t == sel){
16514                 // disable highlight in other month..
16515                 //cell.className += " fc-state-highlight";
16516                 
16517             }
16518             // disabling
16519             if(t < min) {
16520                 cell.className = " fc-state-disabled";
16521                 cell.title = cal.minText;
16522                 return;
16523             }
16524             if(t > max) {
16525                 cell.className = " fc-state-disabled";
16526                 cell.title = cal.maxText;
16527                 return;
16528             }
16529             if(ddays){
16530                 if(ddays.indexOf(d.getDay()) != -1){
16531                     cell.title = ddaysText;
16532                     cell.className = " fc-state-disabled";
16533                 }
16534             }
16535             if(ddMatch && format){
16536                 var fvalue = d.dateFormat(format);
16537                 if(ddMatch.test(fvalue)){
16538                     cell.title = ddText.replace("%0", fvalue);
16539                     cell.className = " fc-state-disabled";
16540                 }
16541             }
16542             
16543             if (!cell.initialClassName) {
16544                 cell.initialClassName = cell.dom.className;
16545             }
16546             
16547             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16548         };
16549
16550         var i = 0;
16551         
16552         for(; i < startingPos; i++) {
16553             textEls[i].innerHTML = (++prevStart);
16554             d.setDate(d.getDate()+1);
16555             
16556             cells[i].className = "fc-past fc-other-month";
16557             setCellClass(this, cells[i]);
16558         }
16559         
16560         var intDay = 0;
16561         
16562         for(; i < days; i++){
16563             intDay = i - startingPos + 1;
16564             textEls[i].innerHTML = (intDay);
16565             d.setDate(d.getDate()+1);
16566             
16567             cells[i].className = ''; // "x-date-active";
16568             setCellClass(this, cells[i]);
16569         }
16570         var extraDays = 0;
16571         
16572         for(; i < 42; i++) {
16573             textEls[i].innerHTML = (++extraDays);
16574             d.setDate(d.getDate()+1);
16575             
16576             cells[i].className = "fc-future fc-other-month";
16577             setCellClass(this, cells[i]);
16578         }
16579         
16580         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16581         
16582         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16583         
16584         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16585         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16586         
16587         if(totalRows != 6){
16588             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16589             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16590         }
16591         
16592         this.fireEvent('monthchange', this, date);
16593         
16594         
16595         /*
16596         if(!this.internalRender){
16597             var main = this.el.dom.firstChild;
16598             var w = main.offsetWidth;
16599             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16600             Roo.fly(main).setWidth(w);
16601             this.internalRender = true;
16602             // opera does not respect the auto grow header center column
16603             // then, after it gets a width opera refuses to recalculate
16604             // without a second pass
16605             if(Roo.isOpera && !this.secondPass){
16606                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16607                 this.secondPass = true;
16608                 this.update.defer(10, this, [date]);
16609             }
16610         }
16611         */
16612         
16613     },
16614     
16615     findCell : function(dt) {
16616         dt = dt.clearTime().getTime();
16617         var ret = false;
16618         this.cells.each(function(c){
16619             //Roo.log("check " +c.dateValue + '?=' + dt);
16620             if(c.dateValue == dt){
16621                 ret = c;
16622                 return false;
16623             }
16624             return true;
16625         });
16626         
16627         return ret;
16628     },
16629     
16630     findCells : function(ev) {
16631         var s = ev.start.clone().clearTime().getTime();
16632        // Roo.log(s);
16633         var e= ev.end.clone().clearTime().getTime();
16634        // Roo.log(e);
16635         var ret = [];
16636         this.cells.each(function(c){
16637              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16638             
16639             if(c.dateValue > e){
16640                 return ;
16641             }
16642             if(c.dateValue < s){
16643                 return ;
16644             }
16645             ret.push(c);
16646         });
16647         
16648         return ret;    
16649     },
16650     
16651 //    findBestRow: function(cells)
16652 //    {
16653 //        var ret = 0;
16654 //        
16655 //        for (var i =0 ; i < cells.length;i++) {
16656 //            ret  = Math.max(cells[i].rows || 0,ret);
16657 //        }
16658 //        return ret;
16659 //        
16660 //    },
16661     
16662     
16663     addItem : function(ev)
16664     {
16665         // look for vertical location slot in
16666         var cells = this.findCells(ev);
16667         
16668 //        ev.row = this.findBestRow(cells);
16669         
16670         // work out the location.
16671         
16672         var crow = false;
16673         var rows = [];
16674         for(var i =0; i < cells.length; i++) {
16675             
16676             cells[i].row = cells[0].row;
16677             
16678             if(i == 0){
16679                 cells[i].row = cells[i].row + 1;
16680             }
16681             
16682             if (!crow) {
16683                 crow = {
16684                     start : cells[i],
16685                     end :  cells[i]
16686                 };
16687                 continue;
16688             }
16689             if (crow.start.getY() == cells[i].getY()) {
16690                 // on same row.
16691                 crow.end = cells[i];
16692                 continue;
16693             }
16694             // different row.
16695             rows.push(crow);
16696             crow = {
16697                 start: cells[i],
16698                 end : cells[i]
16699             };
16700             
16701         }
16702         
16703         rows.push(crow);
16704         ev.els = [];
16705         ev.rows = rows;
16706         ev.cells = cells;
16707         
16708         cells[0].events.push(ev);
16709         
16710         this.calevents.push(ev);
16711     },
16712     
16713     clearEvents: function() {
16714         
16715         if(!this.calevents){
16716             return;
16717         }
16718         
16719         Roo.each(this.cells.elements, function(c){
16720             c.row = 0;
16721             c.events = [];
16722             c.more = [];
16723         });
16724         
16725         Roo.each(this.calevents, function(e) {
16726             Roo.each(e.els, function(el) {
16727                 el.un('mouseenter' ,this.onEventEnter, this);
16728                 el.un('mouseleave' ,this.onEventLeave, this);
16729                 el.remove();
16730             },this);
16731         },this);
16732         
16733         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16734             e.remove();
16735         });
16736         
16737     },
16738     
16739     renderEvents: function()
16740     {   
16741         var _this = this;
16742         
16743         this.cells.each(function(c) {
16744             
16745             if(c.row < 5){
16746                 return;
16747             }
16748             
16749             var ev = c.events;
16750             
16751             var r = 4;
16752             if(c.row != c.events.length){
16753                 r = 4 - (4 - (c.row - c.events.length));
16754             }
16755             
16756             c.events = ev.slice(0, r);
16757             c.more = ev.slice(r);
16758             
16759             if(c.more.length && c.more.length == 1){
16760                 c.events.push(c.more.pop());
16761             }
16762             
16763             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16764             
16765         });
16766             
16767         this.cells.each(function(c) {
16768             
16769             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16770             
16771             
16772             for (var e = 0; e < c.events.length; e++){
16773                 var ev = c.events[e];
16774                 var rows = ev.rows;
16775                 
16776                 for(var i = 0; i < rows.length; i++) {
16777                 
16778                     // how many rows should it span..
16779
16780                     var  cfg = {
16781                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16782                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16783
16784                         unselectable : "on",
16785                         cn : [
16786                             {
16787                                 cls: 'fc-event-inner',
16788                                 cn : [
16789     //                                {
16790     //                                  tag:'span',
16791     //                                  cls: 'fc-event-time',
16792     //                                  html : cells.length > 1 ? '' : ev.time
16793     //                                },
16794                                     {
16795                                       tag:'span',
16796                                       cls: 'fc-event-title',
16797                                       html : String.format('{0}', ev.title)
16798                                     }
16799
16800
16801                                 ]
16802                             },
16803                             {
16804                                 cls: 'ui-resizable-handle ui-resizable-e',
16805                                 html : '&nbsp;&nbsp;&nbsp'
16806                             }
16807
16808                         ]
16809                     };
16810
16811                     if (i == 0) {
16812                         cfg.cls += ' fc-event-start';
16813                     }
16814                     if ((i+1) == rows.length) {
16815                         cfg.cls += ' fc-event-end';
16816                     }
16817
16818                     var ctr = _this.el.select('.fc-event-container',true).first();
16819                     var cg = ctr.createChild(cfg);
16820
16821                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16822                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16823
16824                     var r = (c.more.length) ? 1 : 0;
16825                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16826                     cg.setWidth(ebox.right - sbox.x -2);
16827
16828                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16829                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16830                     cg.on('click', _this.onEventClick, _this, ev);
16831
16832                     ev.els.push(cg);
16833                     
16834                 }
16835                 
16836             }
16837             
16838             
16839             if(c.more.length){
16840                 var  cfg = {
16841                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16842                     style : 'position: absolute',
16843                     unselectable : "on",
16844                     cn : [
16845                         {
16846                             cls: 'fc-event-inner',
16847                             cn : [
16848                                 {
16849                                   tag:'span',
16850                                   cls: 'fc-event-title',
16851                                   html : 'More'
16852                                 }
16853
16854
16855                             ]
16856                         },
16857                         {
16858                             cls: 'ui-resizable-handle ui-resizable-e',
16859                             html : '&nbsp;&nbsp;&nbsp'
16860                         }
16861
16862                     ]
16863                 };
16864
16865                 var ctr = _this.el.select('.fc-event-container',true).first();
16866                 var cg = ctr.createChild(cfg);
16867
16868                 var sbox = c.select('.fc-day-content',true).first().getBox();
16869                 var ebox = c.select('.fc-day-content',true).first().getBox();
16870                 //Roo.log(cg);
16871                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16872                 cg.setWidth(ebox.right - sbox.x -2);
16873
16874                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16875                 
16876             }
16877             
16878         });
16879         
16880         
16881         
16882     },
16883     
16884     onEventEnter: function (e, el,event,d) {
16885         this.fireEvent('evententer', this, el, event);
16886     },
16887     
16888     onEventLeave: function (e, el,event,d) {
16889         this.fireEvent('eventleave', this, el, event);
16890     },
16891     
16892     onEventClick: function (e, el,event,d) {
16893         this.fireEvent('eventclick', this, el, event);
16894     },
16895     
16896     onMonthChange: function () {
16897         this.store.load();
16898     },
16899     
16900     onMoreEventClick: function(e, el, more)
16901     {
16902         var _this = this;
16903         
16904         this.calpopover.placement = 'right';
16905         this.calpopover.setTitle('More');
16906         
16907         this.calpopover.setContent('');
16908         
16909         var ctr = this.calpopover.el.select('.popover-content', true).first();
16910         
16911         Roo.each(more, function(m){
16912             var cfg = {
16913                 cls : 'fc-event-hori fc-event-draggable',
16914                 html : m.title
16915             };
16916             var cg = ctr.createChild(cfg);
16917             
16918             cg.on('click', _this.onEventClick, _this, m);
16919         });
16920         
16921         this.calpopover.show(el);
16922         
16923         
16924     },
16925     
16926     onLoad: function () 
16927     {   
16928         this.calevents = [];
16929         var cal = this;
16930         
16931         if(this.store.getCount() > 0){
16932             this.store.data.each(function(d){
16933                cal.addItem({
16934                     id : d.data.id,
16935                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16936                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16937                     time : d.data.start_time,
16938                     title : d.data.title,
16939                     description : d.data.description,
16940                     venue : d.data.venue
16941                 });
16942             });
16943         }
16944         
16945         this.renderEvents();
16946         
16947         if(this.calevents.length && this.loadMask){
16948             this.maskEl.hide();
16949         }
16950     },
16951     
16952     onBeforeLoad: function()
16953     {
16954         this.clearEvents();
16955         if(this.loadMask){
16956             this.maskEl.show();
16957         }
16958     }
16959 });
16960
16961  
16962  /*
16963  * - LGPL
16964  *
16965  * element
16966  * 
16967  */
16968
16969 /**
16970  * @class Roo.bootstrap.Popover
16971  * @extends Roo.bootstrap.Component
16972  * Bootstrap Popover class
16973  * @cfg {String} html contents of the popover   (or false to use children..)
16974  * @cfg {String} title of popover (or false to hide)
16975  * @cfg {String} placement how it is placed
16976  * @cfg {String} trigger click || hover (or false to trigger manually)
16977  * @cfg {String} over what (parent or false to trigger manually.)
16978  * @cfg {Number} delay - delay before showing
16979  
16980  * @constructor
16981  * Create a new Popover
16982  * @param {Object} config The config object
16983  */
16984
16985 Roo.bootstrap.Popover = function(config){
16986     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16987     
16988     this.addEvents({
16989         // raw events
16990          /**
16991          * @event show
16992          * After the popover show
16993          * 
16994          * @param {Roo.bootstrap.Popover} this
16995          */
16996         "show" : true,
16997         /**
16998          * @event hide
16999          * After the popover hide
17000          * 
17001          * @param {Roo.bootstrap.Popover} this
17002          */
17003         "hide" : true
17004     });
17005 };
17006
17007 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17008     
17009     title: 'Fill in a title',
17010     html: false,
17011     
17012     placement : 'right',
17013     trigger : 'hover', // hover
17014     
17015     delay : 0,
17016     
17017     over: 'parent',
17018     
17019     can_build_overlaid : false,
17020     
17021     getChildContainer : function()
17022     {
17023         return this.el.select('.popover-content',true).first();
17024     },
17025     
17026     getAutoCreate : function(){
17027          
17028         var cfg = {
17029            cls : 'popover roo-dynamic',
17030            style: 'display:block',
17031            cn : [
17032                 {
17033                     cls : 'arrow'
17034                 },
17035                 {
17036                     cls : 'popover-inner',
17037                     cn : [
17038                         {
17039                             tag: 'h3',
17040                             cls: 'popover-title',
17041                             html : this.title
17042                         },
17043                         {
17044                             cls : 'popover-content',
17045                             html : this.html
17046                         }
17047                     ]
17048                     
17049                 }
17050            ]
17051         };
17052         
17053         return cfg;
17054     },
17055     setTitle: function(str)
17056     {
17057         this.title = str;
17058         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17059     },
17060     setContent: function(str)
17061     {
17062         this.html = str;
17063         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17064     },
17065     // as it get's added to the bottom of the page.
17066     onRender : function(ct, position)
17067     {
17068         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17069         if(!this.el){
17070             var cfg = Roo.apply({},  this.getAutoCreate());
17071             cfg.id = Roo.id();
17072             
17073             if (this.cls) {
17074                 cfg.cls += ' ' + this.cls;
17075             }
17076             if (this.style) {
17077                 cfg.style = this.style;
17078             }
17079             //Roo.log("adding to ");
17080             this.el = Roo.get(document.body).createChild(cfg, position);
17081 //            Roo.log(this.el);
17082         }
17083         this.initEvents();
17084     },
17085     
17086     initEvents : function()
17087     {
17088         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17089         this.el.enableDisplayMode('block');
17090         this.el.hide();
17091         if (this.over === false) {
17092             return; 
17093         }
17094         if (this.triggers === false) {
17095             return;
17096         }
17097         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17098         var triggers = this.trigger ? this.trigger.split(' ') : [];
17099         Roo.each(triggers, function(trigger) {
17100         
17101             if (trigger == 'click') {
17102                 on_el.on('click', this.toggle, this);
17103             } else if (trigger != 'manual') {
17104                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17105                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17106       
17107                 on_el.on(eventIn  ,this.enter, this);
17108                 on_el.on(eventOut, this.leave, this);
17109             }
17110         }, this);
17111         
17112     },
17113     
17114     
17115     // private
17116     timeout : null,
17117     hoverState : null,
17118     
17119     toggle : function () {
17120         this.hoverState == 'in' ? this.leave() : this.enter();
17121     },
17122     
17123     enter : function () {
17124         
17125         clearTimeout(this.timeout);
17126     
17127         this.hoverState = 'in';
17128     
17129         if (!this.delay || !this.delay.show) {
17130             this.show();
17131             return;
17132         }
17133         var _t = this;
17134         this.timeout = setTimeout(function () {
17135             if (_t.hoverState == 'in') {
17136                 _t.show();
17137             }
17138         }, this.delay.show)
17139     },
17140     
17141     leave : function() {
17142         clearTimeout(this.timeout);
17143     
17144         this.hoverState = 'out';
17145     
17146         if (!this.delay || !this.delay.hide) {
17147             this.hide();
17148             return;
17149         }
17150         var _t = this;
17151         this.timeout = setTimeout(function () {
17152             if (_t.hoverState == 'out') {
17153                 _t.hide();
17154             }
17155         }, this.delay.hide)
17156     },
17157     
17158     show : function (on_el)
17159     {
17160         if (!on_el) {
17161             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17162         }
17163         
17164         // set content.
17165         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17166         if (this.html !== false) {
17167             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17168         }
17169         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17170         if (!this.title.length) {
17171             this.el.select('.popover-title',true).hide();
17172         }
17173         
17174         var placement = typeof this.placement == 'function' ?
17175             this.placement.call(this, this.el, on_el) :
17176             this.placement;
17177             
17178         var autoToken = /\s?auto?\s?/i;
17179         var autoPlace = autoToken.test(placement);
17180         if (autoPlace) {
17181             placement = placement.replace(autoToken, '') || 'top';
17182         }
17183         
17184         //this.el.detach()
17185         //this.el.setXY([0,0]);
17186         this.el.show();
17187         this.el.dom.style.display='block';
17188         this.el.addClass(placement);
17189         
17190         //this.el.appendTo(on_el);
17191         
17192         var p = this.getPosition();
17193         var box = this.el.getBox();
17194         
17195         if (autoPlace) {
17196             // fixme..
17197         }
17198         var align = Roo.bootstrap.Popover.alignment[placement];
17199         this.el.alignTo(on_el, align[0],align[1]);
17200         //var arrow = this.el.select('.arrow',true).first();
17201         //arrow.set(align[2], 
17202         
17203         this.el.addClass('in');
17204         
17205         
17206         if (this.el.hasClass('fade')) {
17207             // fade it?
17208         }
17209         
17210         this.hoverState = 'in';
17211         
17212         this.fireEvent('show', this);
17213         
17214     },
17215     hide : function()
17216     {
17217         this.el.setXY([0,0]);
17218         this.el.removeClass('in');
17219         this.el.hide();
17220         this.hoverState = null;
17221         
17222         this.fireEvent('hide', this);
17223     }
17224     
17225 });
17226
17227 Roo.bootstrap.Popover.alignment = {
17228     'left' : ['r-l', [-10,0], 'right'],
17229     'right' : ['l-r', [10,0], 'left'],
17230     'bottom' : ['t-b', [0,10], 'top'],
17231     'top' : [ 'b-t', [0,-10], 'bottom']
17232 };
17233
17234  /*
17235  * - LGPL
17236  *
17237  * Progress
17238  * 
17239  */
17240
17241 /**
17242  * @class Roo.bootstrap.Progress
17243  * @extends Roo.bootstrap.Component
17244  * Bootstrap Progress class
17245  * @cfg {Boolean} striped striped of the progress bar
17246  * @cfg {Boolean} active animated of the progress bar
17247  * 
17248  * 
17249  * @constructor
17250  * Create a new Progress
17251  * @param {Object} config The config object
17252  */
17253
17254 Roo.bootstrap.Progress = function(config){
17255     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17256 };
17257
17258 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17259     
17260     striped : false,
17261     active: false,
17262     
17263     getAutoCreate : function(){
17264         var cfg = {
17265             tag: 'div',
17266             cls: 'progress'
17267         };
17268         
17269         
17270         if(this.striped){
17271             cfg.cls += ' progress-striped';
17272         }
17273       
17274         if(this.active){
17275             cfg.cls += ' active';
17276         }
17277         
17278         
17279         return cfg;
17280     }
17281    
17282 });
17283
17284  
17285
17286  /*
17287  * - LGPL
17288  *
17289  * ProgressBar
17290  * 
17291  */
17292
17293 /**
17294  * @class Roo.bootstrap.ProgressBar
17295  * @extends Roo.bootstrap.Component
17296  * Bootstrap ProgressBar class
17297  * @cfg {Number} aria_valuenow aria-value now
17298  * @cfg {Number} aria_valuemin aria-value min
17299  * @cfg {Number} aria_valuemax aria-value max
17300  * @cfg {String} label label for the progress bar
17301  * @cfg {String} panel (success | info | warning | danger )
17302  * @cfg {String} role role of the progress bar
17303  * @cfg {String} sr_only text
17304  * 
17305  * 
17306  * @constructor
17307  * Create a new ProgressBar
17308  * @param {Object} config The config object
17309  */
17310
17311 Roo.bootstrap.ProgressBar = function(config){
17312     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17313 };
17314
17315 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17316     
17317     aria_valuenow : 0,
17318     aria_valuemin : 0,
17319     aria_valuemax : 100,
17320     label : false,
17321     panel : false,
17322     role : false,
17323     sr_only: false,
17324     
17325     getAutoCreate : function()
17326     {
17327         
17328         var cfg = {
17329             tag: 'div',
17330             cls: 'progress-bar',
17331             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17332         };
17333         
17334         if(this.sr_only){
17335             cfg.cn = {
17336                 tag: 'span',
17337                 cls: 'sr-only',
17338                 html: this.sr_only
17339             }
17340         }
17341         
17342         if(this.role){
17343             cfg.role = this.role;
17344         }
17345         
17346         if(this.aria_valuenow){
17347             cfg['aria-valuenow'] = this.aria_valuenow;
17348         }
17349         
17350         if(this.aria_valuemin){
17351             cfg['aria-valuemin'] = this.aria_valuemin;
17352         }
17353         
17354         if(this.aria_valuemax){
17355             cfg['aria-valuemax'] = this.aria_valuemax;
17356         }
17357         
17358         if(this.label && !this.sr_only){
17359             cfg.html = this.label;
17360         }
17361         
17362         if(this.panel){
17363             cfg.cls += ' progress-bar-' + this.panel;
17364         }
17365         
17366         return cfg;
17367     },
17368     
17369     update : function(aria_valuenow)
17370     {
17371         this.aria_valuenow = aria_valuenow;
17372         
17373         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17374     }
17375    
17376 });
17377
17378  
17379
17380  /*
17381  * - LGPL
17382  *
17383  * column
17384  * 
17385  */
17386
17387 /**
17388  * @class Roo.bootstrap.TabGroup
17389  * @extends Roo.bootstrap.Column
17390  * Bootstrap Column class
17391  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17392  * @cfg {Boolean} carousel true to make the group behave like a carousel
17393  * @cfg {Boolean} bullets show bullets for the panels
17394  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17395  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17396  * @cfg {Boolean} showarrow (true|false) show arrow default true
17397  * 
17398  * @constructor
17399  * Create a new TabGroup
17400  * @param {Object} config The config object
17401  */
17402
17403 Roo.bootstrap.TabGroup = function(config){
17404     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17405     if (!this.navId) {
17406         this.navId = Roo.id();
17407     }
17408     this.tabs = [];
17409     Roo.bootstrap.TabGroup.register(this);
17410     
17411 };
17412
17413 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17414     
17415     carousel : false,
17416     transition : false,
17417     bullets : 0,
17418     timer : 0,
17419     autoslide : false,
17420     slideFn : false,
17421     slideOnTouch : false,
17422     showarrow : true,
17423     
17424     getAutoCreate : function()
17425     {
17426         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17427         
17428         cfg.cls += ' tab-content';
17429         
17430         if (this.carousel) {
17431             cfg.cls += ' carousel slide';
17432             
17433             cfg.cn = [{
17434                cls : 'carousel-inner',
17435                cn : []
17436             }];
17437         
17438             if(this.bullets  && !Roo.isTouch){
17439                 
17440                 var bullets = {
17441                     cls : 'carousel-bullets',
17442                     cn : []
17443                 };
17444                
17445                 if(this.bullets_cls){
17446                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17447                 }
17448                 
17449                 bullets.cn.push({
17450                     cls : 'clear'
17451                 });
17452                 
17453                 cfg.cn[0].cn.push(bullets);
17454             }
17455             
17456             if(this.showarrow){
17457                 cfg.cn[0].cn.push({
17458                     tag : 'div',
17459                     class : 'carousel-arrow',
17460                     cn : [
17461                         {
17462                             tag : 'div',
17463                             class : 'carousel-prev',
17464                             cn : [
17465                                 {
17466                                     tag : 'i',
17467                                     class : 'fa fa-chevron-left'
17468                                 }
17469                             ]
17470                         },
17471                         {
17472                             tag : 'div',
17473                             class : 'carousel-next',
17474                             cn : [
17475                                 {
17476                                     tag : 'i',
17477                                     class : 'fa fa-chevron-right'
17478                                 }
17479                             ]
17480                         }
17481                     ]
17482                 });
17483             }
17484             
17485         }
17486         
17487         return cfg;
17488     },
17489     
17490     initEvents:  function()
17491     {
17492 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17493 //            this.el.on("touchstart", this.onTouchStart, this);
17494 //        }
17495         
17496         if(this.autoslide){
17497             var _this = this;
17498             
17499             this.slideFn = window.setInterval(function() {
17500                 _this.showPanelNext();
17501             }, this.timer);
17502         }
17503         
17504         if(this.showarrow){
17505             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17506             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17507         }
17508         
17509         
17510     },
17511     
17512 //    onTouchStart : function(e, el, o)
17513 //    {
17514 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17515 //            return;
17516 //        }
17517 //        
17518 //        this.showPanelNext();
17519 //    },
17520     
17521     
17522     getChildContainer : function()
17523     {
17524         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17525     },
17526     
17527     /**
17528     * register a Navigation item
17529     * @param {Roo.bootstrap.NavItem} the navitem to add
17530     */
17531     register : function(item)
17532     {
17533         this.tabs.push( item);
17534         item.navId = this.navId; // not really needed..
17535         this.addBullet();
17536     
17537     },
17538     
17539     getActivePanel : function()
17540     {
17541         var r = false;
17542         Roo.each(this.tabs, function(t) {
17543             if (t.active) {
17544                 r = t;
17545                 return false;
17546             }
17547             return null;
17548         });
17549         return r;
17550         
17551     },
17552     getPanelByName : function(n)
17553     {
17554         var r = false;
17555         Roo.each(this.tabs, function(t) {
17556             if (t.tabId == n) {
17557                 r = t;
17558                 return false;
17559             }
17560             return null;
17561         });
17562         return r;
17563     },
17564     indexOfPanel : function(p)
17565     {
17566         var r = false;
17567         Roo.each(this.tabs, function(t,i) {
17568             if (t.tabId == p.tabId) {
17569                 r = i;
17570                 return false;
17571             }
17572             return null;
17573         });
17574         return r;
17575     },
17576     /**
17577      * show a specific panel
17578      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17579      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17580      */
17581     showPanel : function (pan)
17582     {
17583         if(this.transition || typeof(pan) == 'undefined'){
17584             Roo.log("waiting for the transitionend");
17585             return;
17586         }
17587         
17588         if (typeof(pan) == 'number') {
17589             pan = this.tabs[pan];
17590         }
17591         
17592         if (typeof(pan) == 'string') {
17593             pan = this.getPanelByName(pan);
17594         }
17595         
17596         var cur = this.getActivePanel();
17597         
17598         if(!pan || !cur){
17599             Roo.log('pan or acitve pan is undefined');
17600             return false;
17601         }
17602         
17603         if (pan.tabId == this.getActivePanel().tabId) {
17604             return true;
17605         }
17606         
17607         if (false === cur.fireEvent('beforedeactivate')) {
17608             return false;
17609         }
17610         
17611         if(this.bullets > 0 && !Roo.isTouch){
17612             this.setActiveBullet(this.indexOfPanel(pan));
17613         }
17614         
17615         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17616             
17617             this.transition = true;
17618             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17619             var lr = dir == 'next' ? 'left' : 'right';
17620             pan.el.addClass(dir); // or prev
17621             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17622             cur.el.addClass(lr); // or right
17623             pan.el.addClass(lr);
17624             
17625             var _this = this;
17626             cur.el.on('transitionend', function() {
17627                 Roo.log("trans end?");
17628                 
17629                 pan.el.removeClass([lr,dir]);
17630                 pan.setActive(true);
17631                 
17632                 cur.el.removeClass([lr]);
17633                 cur.setActive(false);
17634                 
17635                 _this.transition = false;
17636                 
17637             }, this, { single:  true } );
17638             
17639             return true;
17640         }
17641         
17642         cur.setActive(false);
17643         pan.setActive(true);
17644         
17645         return true;
17646         
17647     },
17648     showPanelNext : function()
17649     {
17650         var i = this.indexOfPanel(this.getActivePanel());
17651         
17652         if (i >= this.tabs.length - 1 && !this.autoslide) {
17653             return;
17654         }
17655         
17656         if (i >= this.tabs.length - 1 && this.autoslide) {
17657             i = -1;
17658         }
17659         
17660         this.showPanel(this.tabs[i+1]);
17661     },
17662     
17663     showPanelPrev : function()
17664     {
17665         var i = this.indexOfPanel(this.getActivePanel());
17666         
17667         if (i  < 1 && !this.autoslide) {
17668             return;
17669         }
17670         
17671         if (i < 1 && this.autoslide) {
17672             i = this.tabs.length;
17673         }
17674         
17675         this.showPanel(this.tabs[i-1]);
17676     },
17677     
17678     
17679     addBullet: function()
17680     {
17681         if(!this.bullets || Roo.isTouch){
17682             return;
17683         }
17684         var ctr = this.el.select('.carousel-bullets',true).first();
17685         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17686         var bullet = ctr.createChild({
17687             cls : 'bullet bullet-' + i
17688         },ctr.dom.lastChild);
17689         
17690         
17691         var _this = this;
17692         
17693         bullet.on('click', (function(e, el, o, ii, t){
17694
17695             e.preventDefault();
17696
17697             this.showPanel(ii);
17698
17699             if(this.autoslide && this.slideFn){
17700                 clearInterval(this.slideFn);
17701                 this.slideFn = window.setInterval(function() {
17702                     _this.showPanelNext();
17703                 }, this.timer);
17704             }
17705
17706         }).createDelegate(this, [i, bullet], true));
17707                 
17708         
17709     },
17710      
17711     setActiveBullet : function(i)
17712     {
17713         if(Roo.isTouch){
17714             return;
17715         }
17716         
17717         Roo.each(this.el.select('.bullet', true).elements, function(el){
17718             el.removeClass('selected');
17719         });
17720
17721         var bullet = this.el.select('.bullet-' + i, true).first();
17722         
17723         if(!bullet){
17724             return;
17725         }
17726         
17727         bullet.addClass('selected');
17728     }
17729     
17730     
17731   
17732 });
17733
17734  
17735
17736  
17737  
17738 Roo.apply(Roo.bootstrap.TabGroup, {
17739     
17740     groups: {},
17741      /**
17742     * register a Navigation Group
17743     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17744     */
17745     register : function(navgrp)
17746     {
17747         this.groups[navgrp.navId] = navgrp;
17748         
17749     },
17750     /**
17751     * fetch a Navigation Group based on the navigation ID
17752     * if one does not exist , it will get created.
17753     * @param {string} the navgroup to add
17754     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17755     */
17756     get: function(navId) {
17757         if (typeof(this.groups[navId]) == 'undefined') {
17758             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17759         }
17760         return this.groups[navId] ;
17761     }
17762     
17763     
17764     
17765 });
17766
17767  /*
17768  * - LGPL
17769  *
17770  * TabPanel
17771  * 
17772  */
17773
17774 /**
17775  * @class Roo.bootstrap.TabPanel
17776  * @extends Roo.bootstrap.Component
17777  * Bootstrap TabPanel class
17778  * @cfg {Boolean} active panel active
17779  * @cfg {String} html panel content
17780  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17781  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17782  * @cfg {String} href click to link..
17783  * 
17784  * 
17785  * @constructor
17786  * Create a new TabPanel
17787  * @param {Object} config The config object
17788  */
17789
17790 Roo.bootstrap.TabPanel = function(config){
17791     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17792     this.addEvents({
17793         /**
17794              * @event changed
17795              * Fires when the active status changes
17796              * @param {Roo.bootstrap.TabPanel} this
17797              * @param {Boolean} state the new state
17798             
17799          */
17800         'changed': true,
17801         /**
17802              * @event beforedeactivate
17803              * Fires before a tab is de-activated - can be used to do validation on a form.
17804              * @param {Roo.bootstrap.TabPanel} this
17805              * @return {Boolean} false if there is an error
17806             
17807          */
17808         'beforedeactivate': true
17809      });
17810     
17811     this.tabId = this.tabId || Roo.id();
17812   
17813 };
17814
17815 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17816     
17817     active: false,
17818     html: false,
17819     tabId: false,
17820     navId : false,
17821     href : '',
17822     
17823     getAutoCreate : function(){
17824         var cfg = {
17825             tag: 'div',
17826             // item is needed for carousel - not sure if it has any effect otherwise
17827             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17828             html: this.html || ''
17829         };
17830         
17831         if(this.active){
17832             cfg.cls += ' active';
17833         }
17834         
17835         if(this.tabId){
17836             cfg.tabId = this.tabId;
17837         }
17838         
17839         
17840         return cfg;
17841     },
17842     
17843     initEvents:  function()
17844     {
17845         var p = this.parent();
17846         
17847         this.navId = this.navId || p.navId;
17848         
17849         if (typeof(this.navId) != 'undefined') {
17850             // not really needed.. but just in case.. parent should be a NavGroup.
17851             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17852             
17853             tg.register(this);
17854             
17855             var i = tg.tabs.length - 1;
17856             
17857             if(this.active && tg.bullets > 0 && i < tg.bullets){
17858                 tg.setActiveBullet(i);
17859             }
17860         }
17861         
17862         this.el.on('click', this.onClick, this);
17863         
17864         if(Roo.isTouch){
17865             this.el.on("touchstart", this.onTouchStart, this);
17866             this.el.on("touchmove", this.onTouchMove, this);
17867             this.el.on("touchend", this.onTouchEnd, this);
17868         }
17869         
17870     },
17871     
17872     onRender : function(ct, position)
17873     {
17874         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17875     },
17876     
17877     setActive : function(state)
17878     {
17879         Roo.log("panel - set active " + this.tabId + "=" + state);
17880         
17881         this.active = state;
17882         if (!state) {
17883             this.el.removeClass('active');
17884             
17885         } else  if (!this.el.hasClass('active')) {
17886             this.el.addClass('active');
17887         }
17888         
17889         this.fireEvent('changed', this, state);
17890     },
17891     
17892     onClick : function(e)
17893     {
17894         e.preventDefault();
17895         
17896         if(!this.href.length){
17897             return;
17898         }
17899         
17900         window.location.href = this.href;
17901     },
17902     
17903     startX : 0,
17904     startY : 0,
17905     endX : 0,
17906     endY : 0,
17907     swiping : false,
17908     
17909     onTouchStart : function(e)
17910     {
17911         this.swiping = false;
17912         
17913         this.startX = e.browserEvent.touches[0].clientX;
17914         this.startY = e.browserEvent.touches[0].clientY;
17915     },
17916     
17917     onTouchMove : function(e)
17918     {
17919         this.swiping = true;
17920         
17921         this.endX = e.browserEvent.touches[0].clientX;
17922         this.endY = e.browserEvent.touches[0].clientY;
17923     },
17924     
17925     onTouchEnd : function(e)
17926     {
17927         if(!this.swiping){
17928             this.onClick(e);
17929             return;
17930         }
17931         
17932         var tabGroup = this.parent();
17933         
17934         if(this.endX > this.startX){ // swiping right
17935             tabGroup.showPanelPrev();
17936             return;
17937         }
17938         
17939         if(this.startX > this.endX){ // swiping left
17940             tabGroup.showPanelNext();
17941             return;
17942         }
17943     }
17944     
17945     
17946 });
17947  
17948
17949  
17950
17951  /*
17952  * - LGPL
17953  *
17954  * DateField
17955  * 
17956  */
17957
17958 /**
17959  * @class Roo.bootstrap.DateField
17960  * @extends Roo.bootstrap.Input
17961  * Bootstrap DateField class
17962  * @cfg {Number} weekStart default 0
17963  * @cfg {String} viewMode default empty, (months|years)
17964  * @cfg {String} minViewMode default empty, (months|years)
17965  * @cfg {Number} startDate default -Infinity
17966  * @cfg {Number} endDate default Infinity
17967  * @cfg {Boolean} todayHighlight default false
17968  * @cfg {Boolean} todayBtn default false
17969  * @cfg {Boolean} calendarWeeks default false
17970  * @cfg {Object} daysOfWeekDisabled default empty
17971  * @cfg {Boolean} singleMode default false (true | false)
17972  * 
17973  * @cfg {Boolean} keyboardNavigation default true
17974  * @cfg {String} language default en
17975  * 
17976  * @constructor
17977  * Create a new DateField
17978  * @param {Object} config The config object
17979  */
17980
17981 Roo.bootstrap.DateField = function(config){
17982     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17983      this.addEvents({
17984             /**
17985              * @event show
17986              * Fires when this field show.
17987              * @param {Roo.bootstrap.DateField} this
17988              * @param {Mixed} date The date value
17989              */
17990             show : true,
17991             /**
17992              * @event show
17993              * Fires when this field hide.
17994              * @param {Roo.bootstrap.DateField} this
17995              * @param {Mixed} date The date value
17996              */
17997             hide : true,
17998             /**
17999              * @event select
18000              * Fires when select a date.
18001              * @param {Roo.bootstrap.DateField} this
18002              * @param {Mixed} date The date value
18003              */
18004             select : true,
18005             /**
18006              * @event beforeselect
18007              * Fires when before select a date.
18008              * @param {Roo.bootstrap.DateField} this
18009              * @param {Mixed} date The date value
18010              */
18011             beforeselect : true
18012         });
18013 };
18014
18015 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18016     
18017     /**
18018      * @cfg {String} format
18019      * The default date format string which can be overriden for localization support.  The format must be
18020      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18021      */
18022     format : "m/d/y",
18023     /**
18024      * @cfg {String} altFormats
18025      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18026      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18027      */
18028     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18029     
18030     weekStart : 0,
18031     
18032     viewMode : '',
18033     
18034     minViewMode : '',
18035     
18036     todayHighlight : false,
18037     
18038     todayBtn: false,
18039     
18040     language: 'en',
18041     
18042     keyboardNavigation: true,
18043     
18044     calendarWeeks: false,
18045     
18046     startDate: -Infinity,
18047     
18048     endDate: Infinity,
18049     
18050     daysOfWeekDisabled: [],
18051     
18052     _events: [],
18053     
18054     singleMode : false,
18055     
18056     UTCDate: function()
18057     {
18058         return new Date(Date.UTC.apply(Date, arguments));
18059     },
18060     
18061     UTCToday: function()
18062     {
18063         var today = new Date();
18064         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18065     },
18066     
18067     getDate: function() {
18068             var d = this.getUTCDate();
18069             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18070     },
18071     
18072     getUTCDate: function() {
18073             return this.date;
18074     },
18075     
18076     setDate: function(d) {
18077             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18078     },
18079     
18080     setUTCDate: function(d) {
18081             this.date = d;
18082             this.setValue(this.formatDate(this.date));
18083     },
18084         
18085     onRender: function(ct, position)
18086     {
18087         
18088         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18089         
18090         this.language = this.language || 'en';
18091         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18092         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18093         
18094         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18095         this.format = this.format || 'm/d/y';
18096         this.isInline = false;
18097         this.isInput = true;
18098         this.component = this.el.select('.add-on', true).first() || false;
18099         this.component = (this.component && this.component.length === 0) ? false : this.component;
18100         this.hasInput = this.component && this.inputEl().length;
18101         
18102         if (typeof(this.minViewMode === 'string')) {
18103             switch (this.minViewMode) {
18104                 case 'months':
18105                     this.minViewMode = 1;
18106                     break;
18107                 case 'years':
18108                     this.minViewMode = 2;
18109                     break;
18110                 default:
18111                     this.minViewMode = 0;
18112                     break;
18113             }
18114         }
18115         
18116         if (typeof(this.viewMode === 'string')) {
18117             switch (this.viewMode) {
18118                 case 'months':
18119                     this.viewMode = 1;
18120                     break;
18121                 case 'years':
18122                     this.viewMode = 2;
18123                     break;
18124                 default:
18125                     this.viewMode = 0;
18126                     break;
18127             }
18128         }
18129                 
18130         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18131         
18132 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18133         
18134         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18135         
18136         this.picker().on('mousedown', this.onMousedown, this);
18137         this.picker().on('click', this.onClick, this);
18138         
18139         this.picker().addClass('datepicker-dropdown');
18140         
18141         this.startViewMode = this.viewMode;
18142         
18143         if(this.singleMode){
18144             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18145                 v.setVisibilityMode(Roo.Element.DISPLAY);
18146                 v.hide();
18147             });
18148             
18149             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18150                 v.setStyle('width', '189px');
18151             });
18152         }
18153         
18154         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18155             if(!this.calendarWeeks){
18156                 v.remove();
18157                 return;
18158             }
18159             
18160             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18161             v.attr('colspan', function(i, val){
18162                 return parseInt(val) + 1;
18163             });
18164         });
18165                         
18166         
18167         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18168         
18169         this.setStartDate(this.startDate);
18170         this.setEndDate(this.endDate);
18171         
18172         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18173         
18174         this.fillDow();
18175         this.fillMonths();
18176         this.update();
18177         this.showMode();
18178         
18179         if(this.isInline) {
18180             this.show();
18181         }
18182     },
18183     
18184     picker : function()
18185     {
18186         return this.pickerEl;
18187 //        return this.el.select('.datepicker', true).first();
18188     },
18189     
18190     fillDow: function()
18191     {
18192         var dowCnt = this.weekStart;
18193         
18194         var dow = {
18195             tag: 'tr',
18196             cn: [
18197                 
18198             ]
18199         };
18200         
18201         if(this.calendarWeeks){
18202             dow.cn.push({
18203                 tag: 'th',
18204                 cls: 'cw',
18205                 html: '&nbsp;'
18206             })
18207         }
18208         
18209         while (dowCnt < this.weekStart + 7) {
18210             dow.cn.push({
18211                 tag: 'th',
18212                 cls: 'dow',
18213                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18214             });
18215         }
18216         
18217         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18218     },
18219     
18220     fillMonths: function()
18221     {    
18222         var i = 0;
18223         var months = this.picker().select('>.datepicker-months td', true).first();
18224         
18225         months.dom.innerHTML = '';
18226         
18227         while (i < 12) {
18228             var month = {
18229                 tag: 'span',
18230                 cls: 'month',
18231                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18232             };
18233             
18234             months.createChild(month);
18235         }
18236         
18237     },
18238     
18239     update: function()
18240     {
18241         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;
18242         
18243         if (this.date < this.startDate) {
18244             this.viewDate = new Date(this.startDate);
18245         } else if (this.date > this.endDate) {
18246             this.viewDate = new Date(this.endDate);
18247         } else {
18248             this.viewDate = new Date(this.date);
18249         }
18250         
18251         this.fill();
18252     },
18253     
18254     fill: function() 
18255     {
18256         var d = new Date(this.viewDate),
18257                 year = d.getUTCFullYear(),
18258                 month = d.getUTCMonth(),
18259                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18260                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18261                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18262                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18263                 currentDate = this.date && this.date.valueOf(),
18264                 today = this.UTCToday();
18265         
18266         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18267         
18268 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18269         
18270 //        this.picker.select('>tfoot th.today').
18271 //                                              .text(dates[this.language].today)
18272 //                                              .toggle(this.todayBtn !== false);
18273     
18274         this.updateNavArrows();
18275         this.fillMonths();
18276                                                 
18277         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18278         
18279         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18280          
18281         prevMonth.setUTCDate(day);
18282         
18283         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18284         
18285         var nextMonth = new Date(prevMonth);
18286         
18287         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18288         
18289         nextMonth = nextMonth.valueOf();
18290         
18291         var fillMonths = false;
18292         
18293         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18294         
18295         while(prevMonth.valueOf() < nextMonth) {
18296             var clsName = '';
18297             
18298             if (prevMonth.getUTCDay() === this.weekStart) {
18299                 if(fillMonths){
18300                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18301                 }
18302                     
18303                 fillMonths = {
18304                     tag: 'tr',
18305                     cn: []
18306                 };
18307                 
18308                 if(this.calendarWeeks){
18309                     // ISO 8601: First week contains first thursday.
18310                     // ISO also states week starts on Monday, but we can be more abstract here.
18311                     var
18312                     // Start of current week: based on weekstart/current date
18313                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18314                     // Thursday of this week
18315                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18316                     // First Thursday of year, year from thursday
18317                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18318                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18319                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18320                     
18321                     fillMonths.cn.push({
18322                         tag: 'td',
18323                         cls: 'cw',
18324                         html: calWeek
18325                     });
18326                 }
18327             }
18328             
18329             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18330                 clsName += ' old';
18331             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18332                 clsName += ' new';
18333             }
18334             if (this.todayHighlight &&
18335                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18336                 prevMonth.getUTCMonth() == today.getMonth() &&
18337                 prevMonth.getUTCDate() == today.getDate()) {
18338                 clsName += ' today';
18339             }
18340             
18341             if (currentDate && prevMonth.valueOf() === currentDate) {
18342                 clsName += ' active';
18343             }
18344             
18345             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18346                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18347                     clsName += ' disabled';
18348             }
18349             
18350             fillMonths.cn.push({
18351                 tag: 'td',
18352                 cls: 'day ' + clsName,
18353                 html: prevMonth.getDate()
18354             });
18355             
18356             prevMonth.setDate(prevMonth.getDate()+1);
18357         }
18358           
18359         var currentYear = this.date && this.date.getUTCFullYear();
18360         var currentMonth = this.date && this.date.getUTCMonth();
18361         
18362         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18363         
18364         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18365             v.removeClass('active');
18366             
18367             if(currentYear === year && k === currentMonth){
18368                 v.addClass('active');
18369             }
18370             
18371             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18372                 v.addClass('disabled');
18373             }
18374             
18375         });
18376         
18377         
18378         year = parseInt(year/10, 10) * 10;
18379         
18380         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18381         
18382         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18383         
18384         year -= 1;
18385         for (var i = -1; i < 11; i++) {
18386             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18387                 tag: 'span',
18388                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18389                 html: year
18390             });
18391             
18392             year += 1;
18393         }
18394     },
18395     
18396     showMode: function(dir) 
18397     {
18398         if (dir) {
18399             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18400         }
18401         
18402         Roo.each(this.picker().select('>div',true).elements, function(v){
18403             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18404             v.hide();
18405         });
18406         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18407     },
18408     
18409     place: function()
18410     {
18411         if(this.isInline) {
18412             return;
18413         }
18414         
18415         this.picker().removeClass(['bottom', 'top']);
18416         
18417         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18418             /*
18419              * place to the top of element!
18420              *
18421              */
18422             
18423             this.picker().addClass('top');
18424             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18425             
18426             return;
18427         }
18428         
18429         this.picker().addClass('bottom');
18430         
18431         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18432     },
18433     
18434     parseDate : function(value)
18435     {
18436         if(!value || value instanceof Date){
18437             return value;
18438         }
18439         var v = Date.parseDate(value, this.format);
18440         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18441             v = Date.parseDate(value, 'Y-m-d');
18442         }
18443         if(!v && this.altFormats){
18444             if(!this.altFormatsArray){
18445                 this.altFormatsArray = this.altFormats.split("|");
18446             }
18447             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18448                 v = Date.parseDate(value, this.altFormatsArray[i]);
18449             }
18450         }
18451         return v;
18452     },
18453     
18454     formatDate : function(date, fmt)
18455     {   
18456         return (!date || !(date instanceof Date)) ?
18457         date : date.dateFormat(fmt || this.format);
18458     },
18459     
18460     onFocus : function()
18461     {
18462         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18463         this.show();
18464     },
18465     
18466     onBlur : function()
18467     {
18468         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18469         
18470         var d = this.inputEl().getValue();
18471         
18472         this.setValue(d);
18473                 
18474         this.hide();
18475     },
18476     
18477     show : function()
18478     {
18479         this.picker().show();
18480         this.update();
18481         this.place();
18482         
18483         this.fireEvent('show', this, this.date);
18484     },
18485     
18486     hide : function()
18487     {
18488         if(this.isInline) {
18489             return;
18490         }
18491         this.picker().hide();
18492         this.viewMode = this.startViewMode;
18493         this.showMode();
18494         
18495         this.fireEvent('hide', this, this.date);
18496         
18497     },
18498     
18499     onMousedown: function(e)
18500     {
18501         e.stopPropagation();
18502         e.preventDefault();
18503     },
18504     
18505     keyup: function(e)
18506     {
18507         Roo.bootstrap.DateField.superclass.keyup.call(this);
18508         this.update();
18509     },
18510
18511     setValue: function(v)
18512     {
18513         if(this.fireEvent('beforeselect', this, v) !== false){
18514             var d = new Date(this.parseDate(v) ).clearTime();
18515         
18516             if(isNaN(d.getTime())){
18517                 this.date = this.viewDate = '';
18518                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18519                 return;
18520             }
18521
18522             v = this.formatDate(d);
18523
18524             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18525
18526             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18527
18528             this.update();
18529
18530             this.fireEvent('select', this, this.date);
18531         }
18532     },
18533     
18534     getValue: function()
18535     {
18536         return this.formatDate(this.date);
18537     },
18538     
18539     fireKey: function(e)
18540     {
18541         if (!this.picker().isVisible()){
18542             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18543                 this.show();
18544             }
18545             return;
18546         }
18547         
18548         var dateChanged = false,
18549         dir, day, month,
18550         newDate, newViewDate;
18551         
18552         switch(e.keyCode){
18553             case 27: // escape
18554                 this.hide();
18555                 e.preventDefault();
18556                 break;
18557             case 37: // left
18558             case 39: // right
18559                 if (!this.keyboardNavigation) {
18560                     break;
18561                 }
18562                 dir = e.keyCode == 37 ? -1 : 1;
18563                 
18564                 if (e.ctrlKey){
18565                     newDate = this.moveYear(this.date, dir);
18566                     newViewDate = this.moveYear(this.viewDate, dir);
18567                 } else if (e.shiftKey){
18568                     newDate = this.moveMonth(this.date, dir);
18569                     newViewDate = this.moveMonth(this.viewDate, dir);
18570                 } else {
18571                     newDate = new Date(this.date);
18572                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18573                     newViewDate = new Date(this.viewDate);
18574                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18575                 }
18576                 if (this.dateWithinRange(newDate)){
18577                     this.date = newDate;
18578                     this.viewDate = newViewDate;
18579                     this.setValue(this.formatDate(this.date));
18580 //                    this.update();
18581                     e.preventDefault();
18582                     dateChanged = true;
18583                 }
18584                 break;
18585             case 38: // up
18586             case 40: // down
18587                 if (!this.keyboardNavigation) {
18588                     break;
18589                 }
18590                 dir = e.keyCode == 38 ? -1 : 1;
18591                 if (e.ctrlKey){
18592                     newDate = this.moveYear(this.date, dir);
18593                     newViewDate = this.moveYear(this.viewDate, dir);
18594                 } else if (e.shiftKey){
18595                     newDate = this.moveMonth(this.date, dir);
18596                     newViewDate = this.moveMonth(this.viewDate, dir);
18597                 } else {
18598                     newDate = new Date(this.date);
18599                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18600                     newViewDate = new Date(this.viewDate);
18601                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18602                 }
18603                 if (this.dateWithinRange(newDate)){
18604                     this.date = newDate;
18605                     this.viewDate = newViewDate;
18606                     this.setValue(this.formatDate(this.date));
18607 //                    this.update();
18608                     e.preventDefault();
18609                     dateChanged = true;
18610                 }
18611                 break;
18612             case 13: // enter
18613                 this.setValue(this.formatDate(this.date));
18614                 this.hide();
18615                 e.preventDefault();
18616                 break;
18617             case 9: // tab
18618                 this.setValue(this.formatDate(this.date));
18619                 this.hide();
18620                 break;
18621             case 16: // shift
18622             case 17: // ctrl
18623             case 18: // alt
18624                 break;
18625             default :
18626                 this.hide();
18627                 
18628         }
18629     },
18630     
18631     
18632     onClick: function(e) 
18633     {
18634         e.stopPropagation();
18635         e.preventDefault();
18636         
18637         var target = e.getTarget();
18638         
18639         if(target.nodeName.toLowerCase() === 'i'){
18640             target = Roo.get(target).dom.parentNode;
18641         }
18642         
18643         var nodeName = target.nodeName;
18644         var className = target.className;
18645         var html = target.innerHTML;
18646         //Roo.log(nodeName);
18647         
18648         switch(nodeName.toLowerCase()) {
18649             case 'th':
18650                 switch(className) {
18651                     case 'switch':
18652                         this.showMode(1);
18653                         break;
18654                     case 'prev':
18655                     case 'next':
18656                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18657                         switch(this.viewMode){
18658                                 case 0:
18659                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18660                                         break;
18661                                 case 1:
18662                                 case 2:
18663                                         this.viewDate = this.moveYear(this.viewDate, dir);
18664                                         break;
18665                         }
18666                         this.fill();
18667                         break;
18668                     case 'today':
18669                         var date = new Date();
18670                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18671 //                        this.fill()
18672                         this.setValue(this.formatDate(this.date));
18673                         
18674                         this.hide();
18675                         break;
18676                 }
18677                 break;
18678             case 'span':
18679                 if (className.indexOf('disabled') < 0) {
18680                     this.viewDate.setUTCDate(1);
18681                     if (className.indexOf('month') > -1) {
18682                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18683                     } else {
18684                         var year = parseInt(html, 10) || 0;
18685                         this.viewDate.setUTCFullYear(year);
18686                         
18687                     }
18688                     
18689                     if(this.singleMode){
18690                         this.setValue(this.formatDate(this.viewDate));
18691                         this.hide();
18692                         return;
18693                     }
18694                     
18695                     this.showMode(-1);
18696                     this.fill();
18697                 }
18698                 break;
18699                 
18700             case 'td':
18701                 //Roo.log(className);
18702                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18703                     var day = parseInt(html, 10) || 1;
18704                     var year = this.viewDate.getUTCFullYear(),
18705                         month = this.viewDate.getUTCMonth();
18706
18707                     if (className.indexOf('old') > -1) {
18708                         if(month === 0 ){
18709                             month = 11;
18710                             year -= 1;
18711                         }else{
18712                             month -= 1;
18713                         }
18714                     } else if (className.indexOf('new') > -1) {
18715                         if (month == 11) {
18716                             month = 0;
18717                             year += 1;
18718                         } else {
18719                             month += 1;
18720                         }
18721                     }
18722                     //Roo.log([year,month,day]);
18723                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18724                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18725 //                    this.fill();
18726                     //Roo.log(this.formatDate(this.date));
18727                     this.setValue(this.formatDate(this.date));
18728                     this.hide();
18729                 }
18730                 break;
18731         }
18732     },
18733     
18734     setStartDate: function(startDate)
18735     {
18736         this.startDate = startDate || -Infinity;
18737         if (this.startDate !== -Infinity) {
18738             this.startDate = this.parseDate(this.startDate);
18739         }
18740         this.update();
18741         this.updateNavArrows();
18742     },
18743
18744     setEndDate: function(endDate)
18745     {
18746         this.endDate = endDate || Infinity;
18747         if (this.endDate !== Infinity) {
18748             this.endDate = this.parseDate(this.endDate);
18749         }
18750         this.update();
18751         this.updateNavArrows();
18752     },
18753     
18754     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18755     {
18756         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18757         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18758             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18759         }
18760         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18761             return parseInt(d, 10);
18762         });
18763         this.update();
18764         this.updateNavArrows();
18765     },
18766     
18767     updateNavArrows: function() 
18768     {
18769         if(this.singleMode){
18770             return;
18771         }
18772         
18773         var d = new Date(this.viewDate),
18774         year = d.getUTCFullYear(),
18775         month = d.getUTCMonth();
18776         
18777         Roo.each(this.picker().select('.prev', true).elements, function(v){
18778             v.show();
18779             switch (this.viewMode) {
18780                 case 0:
18781
18782                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18783                         v.hide();
18784                     }
18785                     break;
18786                 case 1:
18787                 case 2:
18788                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18789                         v.hide();
18790                     }
18791                     break;
18792             }
18793         });
18794         
18795         Roo.each(this.picker().select('.next', true).elements, function(v){
18796             v.show();
18797             switch (this.viewMode) {
18798                 case 0:
18799
18800                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18801                         v.hide();
18802                     }
18803                     break;
18804                 case 1:
18805                 case 2:
18806                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18807                         v.hide();
18808                     }
18809                     break;
18810             }
18811         })
18812     },
18813     
18814     moveMonth: function(date, dir)
18815     {
18816         if (!dir) {
18817             return date;
18818         }
18819         var new_date = new Date(date.valueOf()),
18820         day = new_date.getUTCDate(),
18821         month = new_date.getUTCMonth(),
18822         mag = Math.abs(dir),
18823         new_month, test;
18824         dir = dir > 0 ? 1 : -1;
18825         if (mag == 1){
18826             test = dir == -1
18827             // If going back one month, make sure month is not current month
18828             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18829             ? function(){
18830                 return new_date.getUTCMonth() == month;
18831             }
18832             // If going forward one month, make sure month is as expected
18833             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18834             : function(){
18835                 return new_date.getUTCMonth() != new_month;
18836             };
18837             new_month = month + dir;
18838             new_date.setUTCMonth(new_month);
18839             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18840             if (new_month < 0 || new_month > 11) {
18841                 new_month = (new_month + 12) % 12;
18842             }
18843         } else {
18844             // For magnitudes >1, move one month at a time...
18845             for (var i=0; i<mag; i++) {
18846                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18847                 new_date = this.moveMonth(new_date, dir);
18848             }
18849             // ...then reset the day, keeping it in the new month
18850             new_month = new_date.getUTCMonth();
18851             new_date.setUTCDate(day);
18852             test = function(){
18853                 return new_month != new_date.getUTCMonth();
18854             };
18855         }
18856         // Common date-resetting loop -- if date is beyond end of month, make it
18857         // end of month
18858         while (test()){
18859             new_date.setUTCDate(--day);
18860             new_date.setUTCMonth(new_month);
18861         }
18862         return new_date;
18863     },
18864
18865     moveYear: function(date, dir)
18866     {
18867         return this.moveMonth(date, dir*12);
18868     },
18869
18870     dateWithinRange: function(date)
18871     {
18872         return date >= this.startDate && date <= this.endDate;
18873     },
18874
18875     
18876     remove: function() 
18877     {
18878         this.picker().remove();
18879     },
18880     
18881     validateValue : function(value)
18882     {
18883         if(value.length < 1)  {
18884             if(this.allowBlank){
18885                 return true;
18886             }
18887             return false;
18888         }
18889         
18890         if(value.length < this.minLength){
18891             return false;
18892         }
18893         if(value.length > this.maxLength){
18894             return false;
18895         }
18896         if(this.vtype){
18897             var vt = Roo.form.VTypes;
18898             if(!vt[this.vtype](value, this)){
18899                 return false;
18900             }
18901         }
18902         if(typeof this.validator == "function"){
18903             var msg = this.validator(value);
18904             if(msg !== true){
18905                 return false;
18906             }
18907         }
18908         
18909         if(this.regex && !this.regex.test(value)){
18910             return false;
18911         }
18912         
18913         if(typeof(this.parseDate(value)) == 'undefined'){
18914             return false;
18915         }
18916         
18917         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18918             return false;
18919         }      
18920         
18921         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18922             return false;
18923         } 
18924         
18925         
18926         return true;
18927     }
18928    
18929 });
18930
18931 Roo.apply(Roo.bootstrap.DateField,  {
18932     
18933     head : {
18934         tag: 'thead',
18935         cn: [
18936         {
18937             tag: 'tr',
18938             cn: [
18939             {
18940                 tag: 'th',
18941                 cls: 'prev',
18942                 html: '<i class="fa fa-arrow-left"/>'
18943             },
18944             {
18945                 tag: 'th',
18946                 cls: 'switch',
18947                 colspan: '5'
18948             },
18949             {
18950                 tag: 'th',
18951                 cls: 'next',
18952                 html: '<i class="fa fa-arrow-right"/>'
18953             }
18954
18955             ]
18956         }
18957         ]
18958     },
18959     
18960     content : {
18961         tag: 'tbody',
18962         cn: [
18963         {
18964             tag: 'tr',
18965             cn: [
18966             {
18967                 tag: 'td',
18968                 colspan: '7'
18969             }
18970             ]
18971         }
18972         ]
18973     },
18974     
18975     footer : {
18976         tag: 'tfoot',
18977         cn: [
18978         {
18979             tag: 'tr',
18980             cn: [
18981             {
18982                 tag: 'th',
18983                 colspan: '7',
18984                 cls: 'today'
18985             }
18986                     
18987             ]
18988         }
18989         ]
18990     },
18991     
18992     dates:{
18993         en: {
18994             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18995             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18996             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18997             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18998             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18999             today: "Today"
19000         }
19001     },
19002     
19003     modes: [
19004     {
19005         clsName: 'days',
19006         navFnc: 'Month',
19007         navStep: 1
19008     },
19009     {
19010         clsName: 'months',
19011         navFnc: 'FullYear',
19012         navStep: 1
19013     },
19014     {
19015         clsName: 'years',
19016         navFnc: 'FullYear',
19017         navStep: 10
19018     }]
19019 });
19020
19021 Roo.apply(Roo.bootstrap.DateField,  {
19022   
19023     template : {
19024         tag: 'div',
19025         cls: 'datepicker dropdown-menu roo-dynamic',
19026         cn: [
19027         {
19028             tag: 'div',
19029             cls: 'datepicker-days',
19030             cn: [
19031             {
19032                 tag: 'table',
19033                 cls: 'table-condensed',
19034                 cn:[
19035                 Roo.bootstrap.DateField.head,
19036                 {
19037                     tag: 'tbody'
19038                 },
19039                 Roo.bootstrap.DateField.footer
19040                 ]
19041             }
19042             ]
19043         },
19044         {
19045             tag: 'div',
19046             cls: 'datepicker-months',
19047             cn: [
19048             {
19049                 tag: 'table',
19050                 cls: 'table-condensed',
19051                 cn:[
19052                 Roo.bootstrap.DateField.head,
19053                 Roo.bootstrap.DateField.content,
19054                 Roo.bootstrap.DateField.footer
19055                 ]
19056             }
19057             ]
19058         },
19059         {
19060             tag: 'div',
19061             cls: 'datepicker-years',
19062             cn: [
19063             {
19064                 tag: 'table',
19065                 cls: 'table-condensed',
19066                 cn:[
19067                 Roo.bootstrap.DateField.head,
19068                 Roo.bootstrap.DateField.content,
19069                 Roo.bootstrap.DateField.footer
19070                 ]
19071             }
19072             ]
19073         }
19074         ]
19075     }
19076 });
19077
19078  
19079
19080  /*
19081  * - LGPL
19082  *
19083  * TimeField
19084  * 
19085  */
19086
19087 /**
19088  * @class Roo.bootstrap.TimeField
19089  * @extends Roo.bootstrap.Input
19090  * Bootstrap DateField class
19091  * 
19092  * 
19093  * @constructor
19094  * Create a new TimeField
19095  * @param {Object} config The config object
19096  */
19097
19098 Roo.bootstrap.TimeField = function(config){
19099     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19100     this.addEvents({
19101             /**
19102              * @event show
19103              * Fires when this field show.
19104              * @param {Roo.bootstrap.DateField} thisthis
19105              * @param {Mixed} date The date value
19106              */
19107             show : true,
19108             /**
19109              * @event show
19110              * Fires when this field hide.
19111              * @param {Roo.bootstrap.DateField} this
19112              * @param {Mixed} date The date value
19113              */
19114             hide : true,
19115             /**
19116              * @event select
19117              * Fires when select a date.
19118              * @param {Roo.bootstrap.DateField} this
19119              * @param {Mixed} date The date value
19120              */
19121             select : true
19122         });
19123 };
19124
19125 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19126     
19127     /**
19128      * @cfg {String} format
19129      * The default time format string which can be overriden for localization support.  The format must be
19130      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19131      */
19132     format : "H:i",
19133        
19134     onRender: function(ct, position)
19135     {
19136         
19137         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19138                 
19139         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19140         
19141         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19142         
19143         this.pop = this.picker().select('>.datepicker-time',true).first();
19144         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19145         
19146         this.picker().on('mousedown', this.onMousedown, this);
19147         this.picker().on('click', this.onClick, this);
19148         
19149         this.picker().addClass('datepicker-dropdown');
19150     
19151         this.fillTime();
19152         this.update();
19153             
19154         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19155         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19156         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19157         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19158         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19159         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19160
19161     },
19162     
19163     fireKey: function(e){
19164         if (!this.picker().isVisible()){
19165             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19166                 this.show();
19167             }
19168             return;
19169         }
19170
19171         e.preventDefault();
19172         
19173         switch(e.keyCode){
19174             case 27: // escape
19175                 this.hide();
19176                 break;
19177             case 37: // left
19178             case 39: // right
19179                 this.onTogglePeriod();
19180                 break;
19181             case 38: // up
19182                 this.onIncrementMinutes();
19183                 break;
19184             case 40: // down
19185                 this.onDecrementMinutes();
19186                 break;
19187             case 13: // enter
19188             case 9: // tab
19189                 this.setTime();
19190                 break;
19191         }
19192     },
19193     
19194     onClick: function(e) {
19195         e.stopPropagation();
19196         e.preventDefault();
19197     },
19198     
19199     picker : function()
19200     {
19201         return this.el.select('.datepicker', true).first();
19202     },
19203     
19204     fillTime: function()
19205     {    
19206         var time = this.pop.select('tbody', true).first();
19207         
19208         time.dom.innerHTML = '';
19209         
19210         time.createChild({
19211             tag: 'tr',
19212             cn: [
19213                 {
19214                     tag: 'td',
19215                     cn: [
19216                         {
19217                             tag: 'a',
19218                             href: '#',
19219                             cls: 'btn',
19220                             cn: [
19221                                 {
19222                                     tag: 'span',
19223                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19224                                 }
19225                             ]
19226                         } 
19227                     ]
19228                 },
19229                 {
19230                     tag: 'td',
19231                     cls: 'separator'
19232                 },
19233                 {
19234                     tag: 'td',
19235                     cn: [
19236                         {
19237                             tag: 'a',
19238                             href: '#',
19239                             cls: 'btn',
19240                             cn: [
19241                                 {
19242                                     tag: 'span',
19243                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19244                                 }
19245                             ]
19246                         }
19247                     ]
19248                 },
19249                 {
19250                     tag: 'td',
19251                     cls: 'separator'
19252                 }
19253             ]
19254         });
19255         
19256         time.createChild({
19257             tag: 'tr',
19258             cn: [
19259                 {
19260                     tag: 'td',
19261                     cn: [
19262                         {
19263                             tag: 'span',
19264                             cls: 'timepicker-hour',
19265                             html: '00'
19266                         }  
19267                     ]
19268                 },
19269                 {
19270                     tag: 'td',
19271                     cls: 'separator',
19272                     html: ':'
19273                 },
19274                 {
19275                     tag: 'td',
19276                     cn: [
19277                         {
19278                             tag: 'span',
19279                             cls: 'timepicker-minute',
19280                             html: '00'
19281                         }  
19282                     ]
19283                 },
19284                 {
19285                     tag: 'td',
19286                     cls: 'separator'
19287                 },
19288                 {
19289                     tag: 'td',
19290                     cn: [
19291                         {
19292                             tag: 'button',
19293                             type: 'button',
19294                             cls: 'btn btn-primary period',
19295                             html: 'AM'
19296                             
19297                         }
19298                     ]
19299                 }
19300             ]
19301         });
19302         
19303         time.createChild({
19304             tag: 'tr',
19305             cn: [
19306                 {
19307                     tag: 'td',
19308                     cn: [
19309                         {
19310                             tag: 'a',
19311                             href: '#',
19312                             cls: 'btn',
19313                             cn: [
19314                                 {
19315                                     tag: 'span',
19316                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19317                                 }
19318                             ]
19319                         }
19320                     ]
19321                 },
19322                 {
19323                     tag: 'td',
19324                     cls: 'separator'
19325                 },
19326                 {
19327                     tag: 'td',
19328                     cn: [
19329                         {
19330                             tag: 'a',
19331                             href: '#',
19332                             cls: 'btn',
19333                             cn: [
19334                                 {
19335                                     tag: 'span',
19336                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19337                                 }
19338                             ]
19339                         }
19340                     ]
19341                 },
19342                 {
19343                     tag: 'td',
19344                     cls: 'separator'
19345                 }
19346             ]
19347         });
19348         
19349     },
19350     
19351     update: function()
19352     {
19353         
19354         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19355         
19356         this.fill();
19357     },
19358     
19359     fill: function() 
19360     {
19361         var hours = this.time.getHours();
19362         var minutes = this.time.getMinutes();
19363         var period = 'AM';
19364         
19365         if(hours > 11){
19366             period = 'PM';
19367         }
19368         
19369         if(hours == 0){
19370             hours = 12;
19371         }
19372         
19373         
19374         if(hours > 12){
19375             hours = hours - 12;
19376         }
19377         
19378         if(hours < 10){
19379             hours = '0' + hours;
19380         }
19381         
19382         if(minutes < 10){
19383             minutes = '0' + minutes;
19384         }
19385         
19386         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19387         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19388         this.pop.select('button', true).first().dom.innerHTML = period;
19389         
19390     },
19391     
19392     place: function()
19393     {   
19394         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19395         
19396         var cls = ['bottom'];
19397         
19398         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19399             cls.pop();
19400             cls.push('top');
19401         }
19402         
19403         cls.push('right');
19404         
19405         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19406             cls.pop();
19407             cls.push('left');
19408         }
19409         
19410         this.picker().addClass(cls.join('-'));
19411         
19412         var _this = this;
19413         
19414         Roo.each(cls, function(c){
19415             if(c == 'bottom'){
19416                 _this.picker().setTop(_this.inputEl().getHeight());
19417                 return;
19418             }
19419             if(c == 'top'){
19420                 _this.picker().setTop(0 - _this.picker().getHeight());
19421                 return;
19422             }
19423             
19424             if(c == 'left'){
19425                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19426                 return;
19427             }
19428             if(c == 'right'){
19429                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19430                 return;
19431             }
19432         });
19433         
19434     },
19435   
19436     onFocus : function()
19437     {
19438         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19439         this.show();
19440     },
19441     
19442     onBlur : function()
19443     {
19444         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19445         this.hide();
19446     },
19447     
19448     show : function()
19449     {
19450         this.picker().show();
19451         this.pop.show();
19452         this.update();
19453         this.place();
19454         
19455         this.fireEvent('show', this, this.date);
19456     },
19457     
19458     hide : function()
19459     {
19460         this.picker().hide();
19461         this.pop.hide();
19462         
19463         this.fireEvent('hide', this, this.date);
19464     },
19465     
19466     setTime : function()
19467     {
19468         this.hide();
19469         this.setValue(this.time.format(this.format));
19470         
19471         this.fireEvent('select', this, this.date);
19472         
19473         
19474     },
19475     
19476     onMousedown: function(e){
19477         e.stopPropagation();
19478         e.preventDefault();
19479     },
19480     
19481     onIncrementHours: function()
19482     {
19483         Roo.log('onIncrementHours');
19484         this.time = this.time.add(Date.HOUR, 1);
19485         this.update();
19486         
19487     },
19488     
19489     onDecrementHours: function()
19490     {
19491         Roo.log('onDecrementHours');
19492         this.time = this.time.add(Date.HOUR, -1);
19493         this.update();
19494     },
19495     
19496     onIncrementMinutes: function()
19497     {
19498         Roo.log('onIncrementMinutes');
19499         this.time = this.time.add(Date.MINUTE, 1);
19500         this.update();
19501     },
19502     
19503     onDecrementMinutes: function()
19504     {
19505         Roo.log('onDecrementMinutes');
19506         this.time = this.time.add(Date.MINUTE, -1);
19507         this.update();
19508     },
19509     
19510     onTogglePeriod: function()
19511     {
19512         Roo.log('onTogglePeriod');
19513         this.time = this.time.add(Date.HOUR, 12);
19514         this.update();
19515     }
19516     
19517    
19518 });
19519
19520 Roo.apply(Roo.bootstrap.TimeField,  {
19521     
19522     content : {
19523         tag: 'tbody',
19524         cn: [
19525             {
19526                 tag: 'tr',
19527                 cn: [
19528                 {
19529                     tag: 'td',
19530                     colspan: '7'
19531                 }
19532                 ]
19533             }
19534         ]
19535     },
19536     
19537     footer : {
19538         tag: 'tfoot',
19539         cn: [
19540             {
19541                 tag: 'tr',
19542                 cn: [
19543                 {
19544                     tag: 'th',
19545                     colspan: '7',
19546                     cls: '',
19547                     cn: [
19548                         {
19549                             tag: 'button',
19550                             cls: 'btn btn-info ok',
19551                             html: 'OK'
19552                         }
19553                     ]
19554                 }
19555
19556                 ]
19557             }
19558         ]
19559     }
19560 });
19561
19562 Roo.apply(Roo.bootstrap.TimeField,  {
19563   
19564     template : {
19565         tag: 'div',
19566         cls: 'datepicker dropdown-menu',
19567         cn: [
19568             {
19569                 tag: 'div',
19570                 cls: 'datepicker-time',
19571                 cn: [
19572                 {
19573                     tag: 'table',
19574                     cls: 'table-condensed',
19575                     cn:[
19576                     Roo.bootstrap.TimeField.content,
19577                     Roo.bootstrap.TimeField.footer
19578                     ]
19579                 }
19580                 ]
19581             }
19582         ]
19583     }
19584 });
19585
19586  
19587
19588  /*
19589  * - LGPL
19590  *
19591  * MonthField
19592  * 
19593  */
19594
19595 /**
19596  * @class Roo.bootstrap.MonthField
19597  * @extends Roo.bootstrap.Input
19598  * Bootstrap MonthField class
19599  * 
19600  * @cfg {String} language default en
19601  * 
19602  * @constructor
19603  * Create a new MonthField
19604  * @param {Object} config The config object
19605  */
19606
19607 Roo.bootstrap.MonthField = function(config){
19608     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19609     
19610     this.addEvents({
19611         /**
19612          * @event show
19613          * Fires when this field show.
19614          * @param {Roo.bootstrap.MonthField} this
19615          * @param {Mixed} date The date value
19616          */
19617         show : true,
19618         /**
19619          * @event show
19620          * Fires when this field hide.
19621          * @param {Roo.bootstrap.MonthField} this
19622          * @param {Mixed} date The date value
19623          */
19624         hide : true,
19625         /**
19626          * @event select
19627          * Fires when select a date.
19628          * @param {Roo.bootstrap.MonthField} this
19629          * @param {String} oldvalue The old value
19630          * @param {String} newvalue The new value
19631          */
19632         select : true
19633     });
19634 };
19635
19636 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19637     
19638     onRender: function(ct, position)
19639     {
19640         
19641         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19642         
19643         this.language = this.language || 'en';
19644         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19645         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19646         
19647         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19648         this.isInline = false;
19649         this.isInput = true;
19650         this.component = this.el.select('.add-on', true).first() || false;
19651         this.component = (this.component && this.component.length === 0) ? false : this.component;
19652         this.hasInput = this.component && this.inputEL().length;
19653         
19654         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19655         
19656         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19657         
19658         this.picker().on('mousedown', this.onMousedown, this);
19659         this.picker().on('click', this.onClick, this);
19660         
19661         this.picker().addClass('datepicker-dropdown');
19662         
19663         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19664             v.setStyle('width', '189px');
19665         });
19666         
19667         this.fillMonths();
19668         
19669         this.update();
19670         
19671         if(this.isInline) {
19672             this.show();
19673         }
19674         
19675     },
19676     
19677     setValue: function(v, suppressEvent)
19678     {   
19679         var o = this.getValue();
19680         
19681         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19682         
19683         this.update();
19684
19685         if(suppressEvent !== true){
19686             this.fireEvent('select', this, o, v);
19687         }
19688         
19689     },
19690     
19691     getValue: function()
19692     {
19693         return this.value;
19694     },
19695     
19696     onClick: function(e) 
19697     {
19698         e.stopPropagation();
19699         e.preventDefault();
19700         
19701         var target = e.getTarget();
19702         
19703         if(target.nodeName.toLowerCase() === 'i'){
19704             target = Roo.get(target).dom.parentNode;
19705         }
19706         
19707         var nodeName = target.nodeName;
19708         var className = target.className;
19709         var html = target.innerHTML;
19710         
19711         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19712             return;
19713         }
19714         
19715         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19716         
19717         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19718         
19719         this.hide();
19720                         
19721     },
19722     
19723     picker : function()
19724     {
19725         return this.pickerEl;
19726     },
19727     
19728     fillMonths: function()
19729     {    
19730         var i = 0;
19731         var months = this.picker().select('>.datepicker-months td', true).first();
19732         
19733         months.dom.innerHTML = '';
19734         
19735         while (i < 12) {
19736             var month = {
19737                 tag: 'span',
19738                 cls: 'month',
19739                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19740             };
19741             
19742             months.createChild(month);
19743         }
19744         
19745     },
19746     
19747     update: function()
19748     {
19749         var _this = this;
19750         
19751         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19752             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19753         }
19754         
19755         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19756             e.removeClass('active');
19757             
19758             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19759                 e.addClass('active');
19760             }
19761         })
19762     },
19763     
19764     place: function()
19765     {
19766         if(this.isInline) {
19767             return;
19768         }
19769         
19770         this.picker().removeClass(['bottom', 'top']);
19771         
19772         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19773             /*
19774              * place to the top of element!
19775              *
19776              */
19777             
19778             this.picker().addClass('top');
19779             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19780             
19781             return;
19782         }
19783         
19784         this.picker().addClass('bottom');
19785         
19786         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19787     },
19788     
19789     onFocus : function()
19790     {
19791         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19792         this.show();
19793     },
19794     
19795     onBlur : function()
19796     {
19797         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19798         
19799         var d = this.inputEl().getValue();
19800         
19801         this.setValue(d);
19802                 
19803         this.hide();
19804     },
19805     
19806     show : function()
19807     {
19808         this.picker().show();
19809         this.picker().select('>.datepicker-months', true).first().show();
19810         this.update();
19811         this.place();
19812         
19813         this.fireEvent('show', this, this.date);
19814     },
19815     
19816     hide : function()
19817     {
19818         if(this.isInline) {
19819             return;
19820         }
19821         this.picker().hide();
19822         this.fireEvent('hide', this, this.date);
19823         
19824     },
19825     
19826     onMousedown: function(e)
19827     {
19828         e.stopPropagation();
19829         e.preventDefault();
19830     },
19831     
19832     keyup: function(e)
19833     {
19834         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19835         this.update();
19836     },
19837
19838     fireKey: function(e)
19839     {
19840         if (!this.picker().isVisible()){
19841             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19842                 this.show();
19843             }
19844             return;
19845         }
19846         
19847         var dir;
19848         
19849         switch(e.keyCode){
19850             case 27: // escape
19851                 this.hide();
19852                 e.preventDefault();
19853                 break;
19854             case 37: // left
19855             case 39: // right
19856                 dir = e.keyCode == 37 ? -1 : 1;
19857                 
19858                 this.vIndex = this.vIndex + dir;
19859                 
19860                 if(this.vIndex < 0){
19861                     this.vIndex = 0;
19862                 }
19863                 
19864                 if(this.vIndex > 11){
19865                     this.vIndex = 11;
19866                 }
19867                 
19868                 if(isNaN(this.vIndex)){
19869                     this.vIndex = 0;
19870                 }
19871                 
19872                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19873                 
19874                 break;
19875             case 38: // up
19876             case 40: // down
19877                 
19878                 dir = e.keyCode == 38 ? -1 : 1;
19879                 
19880                 this.vIndex = this.vIndex + dir * 4;
19881                 
19882                 if(this.vIndex < 0){
19883                     this.vIndex = 0;
19884                 }
19885                 
19886                 if(this.vIndex > 11){
19887                     this.vIndex = 11;
19888                 }
19889                 
19890                 if(isNaN(this.vIndex)){
19891                     this.vIndex = 0;
19892                 }
19893                 
19894                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19895                 break;
19896                 
19897             case 13: // enter
19898                 
19899                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19900                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19901                 }
19902                 
19903                 this.hide();
19904                 e.preventDefault();
19905                 break;
19906             case 9: // tab
19907                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19908                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19909                 }
19910                 this.hide();
19911                 break;
19912             case 16: // shift
19913             case 17: // ctrl
19914             case 18: // alt
19915                 break;
19916             default :
19917                 this.hide();
19918                 
19919         }
19920     },
19921     
19922     remove: function() 
19923     {
19924         this.picker().remove();
19925     }
19926    
19927 });
19928
19929 Roo.apply(Roo.bootstrap.MonthField,  {
19930     
19931     content : {
19932         tag: 'tbody',
19933         cn: [
19934         {
19935             tag: 'tr',
19936             cn: [
19937             {
19938                 tag: 'td',
19939                 colspan: '7'
19940             }
19941             ]
19942         }
19943         ]
19944     },
19945     
19946     dates:{
19947         en: {
19948             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19949             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19950         }
19951     }
19952 });
19953
19954 Roo.apply(Roo.bootstrap.MonthField,  {
19955   
19956     template : {
19957         tag: 'div',
19958         cls: 'datepicker dropdown-menu roo-dynamic',
19959         cn: [
19960             {
19961                 tag: 'div',
19962                 cls: 'datepicker-months',
19963                 cn: [
19964                 {
19965                     tag: 'table',
19966                     cls: 'table-condensed',
19967                     cn:[
19968                         Roo.bootstrap.DateField.content
19969                     ]
19970                 }
19971                 ]
19972             }
19973         ]
19974     }
19975 });
19976
19977  
19978
19979  
19980  /*
19981  * - LGPL
19982  *
19983  * CheckBox
19984  * 
19985  */
19986
19987 /**
19988  * @class Roo.bootstrap.CheckBox
19989  * @extends Roo.bootstrap.Input
19990  * Bootstrap CheckBox class
19991  * 
19992  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19993  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19994  * @cfg {String} boxLabel The text that appears beside the checkbox
19995  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19996  * @cfg {Boolean} checked initnal the element
19997  * @cfg {Boolean} inline inline the element (default false)
19998  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19999  * 
20000  * @constructor
20001  * Create a new CheckBox
20002  * @param {Object} config The config object
20003  */
20004
20005 Roo.bootstrap.CheckBox = function(config){
20006     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20007    
20008     this.addEvents({
20009         /**
20010         * @event check
20011         * Fires when the element is checked or unchecked.
20012         * @param {Roo.bootstrap.CheckBox} this This input
20013         * @param {Boolean} checked The new checked value
20014         */
20015        check : true
20016     });
20017     
20018 };
20019
20020 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20021   
20022     inputType: 'checkbox',
20023     inputValue: 1,
20024     valueOff: 0,
20025     boxLabel: false,
20026     checked: false,
20027     weight : false,
20028     inline: false,
20029     
20030     getAutoCreate : function()
20031     {
20032         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20033         
20034         var id = Roo.id();
20035         
20036         var cfg = {};
20037         
20038         cfg.cls = 'form-group ' + this.inputType; //input-group
20039         
20040         if(this.inline){
20041             cfg.cls += ' ' + this.inputType + '-inline';
20042         }
20043         
20044         var input =  {
20045             tag: 'input',
20046             id : id,
20047             type : this.inputType,
20048             value : this.inputValue,
20049             cls : 'roo-' + this.inputType, //'form-box',
20050             placeholder : this.placeholder || ''
20051             
20052         };
20053         
20054         if(this.inputType != 'radio'){
20055             var hidden =  {
20056                 tag: 'input',
20057                 type : 'hidden',
20058                 cls : 'roo-hidden-value',
20059                 value : this.checked ? this.valueOff : this.inputValue
20060             };
20061         }
20062         
20063             
20064         if (this.weight) { // Validity check?
20065             cfg.cls += " " + this.inputType + "-" + this.weight;
20066         }
20067         
20068         if (this.disabled) {
20069             input.disabled=true;
20070         }
20071         
20072         if(this.checked){
20073             input.checked = this.checked;
20074             
20075         }
20076         
20077         
20078         if (this.name) {
20079             
20080             input.name = this.name;
20081             
20082             if(this.inputType != 'radio'){
20083                 hidden.name = this.name;
20084                 input.name = '_hidden_' + this.name;
20085             }
20086         }
20087         
20088         if (this.size) {
20089             input.cls += ' input-' + this.size;
20090         }
20091         
20092         var settings=this;
20093         
20094         ['xs','sm','md','lg'].map(function(size){
20095             if (settings[size]) {
20096                 cfg.cls += ' col-' + size + '-' + settings[size];
20097             }
20098         });
20099         
20100         var inputblock = input;
20101          
20102         if (this.before || this.after) {
20103             
20104             inputblock = {
20105                 cls : 'input-group',
20106                 cn :  [] 
20107             };
20108             
20109             if (this.before) {
20110                 inputblock.cn.push({
20111                     tag :'span',
20112                     cls : 'input-group-addon',
20113                     html : this.before
20114                 });
20115             }
20116             
20117             inputblock.cn.push(input);
20118             
20119             if(this.inputType != 'radio'){
20120                 inputblock.cn.push(hidden);
20121             }
20122             
20123             if (this.after) {
20124                 inputblock.cn.push({
20125                     tag :'span',
20126                     cls : 'input-group-addon',
20127                     html : this.after
20128                 });
20129             }
20130             
20131         }
20132         
20133         if (align ==='left' && this.fieldLabel.length) {
20134 //                Roo.log("left and has label");
20135             cfg.cn = [
20136                 {
20137                     tag: 'label',
20138                     'for' :  id,
20139                     cls : 'control-label',
20140                     html : this.fieldLabel
20141
20142                 },
20143                 {
20144                     cls : "", 
20145                     cn: [
20146                         inputblock
20147                     ]
20148                 }
20149             ];
20150             
20151             if(this.labelWidth > 12){
20152                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20153             }
20154             
20155             if(this.labelWidth < 13 && this.labelmd == 0){
20156                 this.labelmd = this.labelWidth;
20157             }
20158             
20159             if(this.labellg > 0){
20160                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20161                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20162             }
20163             
20164             if(this.labelmd > 0){
20165                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20166                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20167             }
20168             
20169             if(this.labelsm > 0){
20170                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20171                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20172             }
20173             
20174             if(this.labelxs > 0){
20175                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20176                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20177             }
20178             
20179         } else if ( this.fieldLabel.length) {
20180 //                Roo.log(" label");
20181                 cfg.cn = [
20182                    
20183                     {
20184                         tag: this.boxLabel ? 'span' : 'label',
20185                         'for': id,
20186                         cls: 'control-label box-input-label',
20187                         //cls : 'input-group-addon',
20188                         html : this.fieldLabel
20189                         
20190                     },
20191                     
20192                     inputblock
20193                     
20194                 ];
20195
20196         } else {
20197             
20198 //                Roo.log(" no label && no align");
20199                 cfg.cn = [  inputblock ] ;
20200                 
20201                 
20202         }
20203         
20204         if(this.boxLabel){
20205              var boxLabelCfg = {
20206                 tag: 'label',
20207                 //'for': id, // box label is handled by onclick - so no for...
20208                 cls: 'box-label',
20209                 html: this.boxLabel
20210             };
20211             
20212             if(this.tooltip){
20213                 boxLabelCfg.tooltip = this.tooltip;
20214             }
20215              
20216             cfg.cn.push(boxLabelCfg);
20217         }
20218         
20219         if(this.inputType != 'radio'){
20220             cfg.cn.push(hidden);
20221         }
20222         
20223         return cfg;
20224         
20225     },
20226     
20227     /**
20228      * return the real input element.
20229      */
20230     inputEl: function ()
20231     {
20232         return this.el.select('input.roo-' + this.inputType,true).first();
20233     },
20234     hiddenEl: function ()
20235     {
20236         return this.el.select('input.roo-hidden-value',true).first();
20237     },
20238     
20239     labelEl: function()
20240     {
20241         return this.el.select('label.control-label',true).first();
20242     },
20243     /* depricated... */
20244     
20245     label: function()
20246     {
20247         return this.labelEl();
20248     },
20249     
20250     boxLabelEl: function()
20251     {
20252         return this.el.select('label.box-label',true).first();
20253     },
20254     
20255     initEvents : function()
20256     {
20257 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20258         
20259         this.inputEl().on('click', this.onClick,  this);
20260         
20261         if (this.boxLabel) { 
20262             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20263         }
20264         
20265         this.startValue = this.getValue();
20266         
20267         if(this.groupId){
20268             Roo.bootstrap.CheckBox.register(this);
20269         }
20270     },
20271     
20272     onClick : function()
20273     {   
20274         this.setChecked(!this.checked);
20275     },
20276     
20277     setChecked : function(state,suppressEvent)
20278     {
20279         this.startValue = this.getValue();
20280
20281         if(this.inputType == 'radio'){
20282             
20283             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20284                 e.dom.checked = false;
20285             });
20286             
20287             this.inputEl().dom.checked = true;
20288             
20289             this.inputEl().dom.value = this.inputValue;
20290             
20291             if(suppressEvent !== true){
20292                 this.fireEvent('check', this, true);
20293             }
20294             
20295             this.validate();
20296             
20297             return;
20298         }
20299         
20300         this.checked = state;
20301         
20302         this.inputEl().dom.checked = state;
20303         
20304         
20305         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20306         
20307         if(suppressEvent !== true){
20308             this.fireEvent('check', this, state);
20309         }
20310         
20311         this.validate();
20312     },
20313     
20314     getValue : function()
20315     {
20316         if(this.inputType == 'radio'){
20317             return this.getGroupValue();
20318         }
20319         
20320         return this.hiddenEl().dom.value;
20321         
20322     },
20323     
20324     getGroupValue : function()
20325     {
20326         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20327             return '';
20328         }
20329         
20330         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20331     },
20332     
20333     setValue : function(v,suppressEvent)
20334     {
20335         if(this.inputType == 'radio'){
20336             this.setGroupValue(v, suppressEvent);
20337             return;
20338         }
20339         
20340         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20341         
20342         this.validate();
20343     },
20344     
20345     setGroupValue : function(v, suppressEvent)
20346     {
20347         this.startValue = this.getValue();
20348         
20349         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20350             e.dom.checked = false;
20351             
20352             if(e.dom.value == v){
20353                 e.dom.checked = true;
20354             }
20355         });
20356         
20357         if(suppressEvent !== true){
20358             this.fireEvent('check', this, true);
20359         }
20360
20361         this.validate();
20362         
20363         return;
20364     },
20365     
20366     validate : function()
20367     {
20368         if(
20369                 this.disabled || 
20370                 (this.inputType == 'radio' && this.validateRadio()) ||
20371                 (this.inputType == 'checkbox' && this.validateCheckbox())
20372         ){
20373             this.markValid();
20374             return true;
20375         }
20376         
20377         this.markInvalid();
20378         return false;
20379     },
20380     
20381     validateRadio : function()
20382     {
20383         if(this.allowBlank){
20384             return true;
20385         }
20386         
20387         var valid = false;
20388         
20389         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20390             if(!e.dom.checked){
20391                 return;
20392             }
20393             
20394             valid = true;
20395             
20396             return false;
20397         });
20398         
20399         return valid;
20400     },
20401     
20402     validateCheckbox : function()
20403     {
20404         if(!this.groupId){
20405             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20406             //return (this.getValue() == this.inputValue) ? true : false;
20407         }
20408         
20409         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20410         
20411         if(!group){
20412             return false;
20413         }
20414         
20415         var r = false;
20416         
20417         for(var i in group){
20418             if(r){
20419                 break;
20420             }
20421             
20422             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20423         }
20424         
20425         return r;
20426     },
20427     
20428     /**
20429      * Mark this field as valid
20430      */
20431     markValid : function()
20432     {
20433         var _this = this;
20434         
20435         this.fireEvent('valid', this);
20436         
20437         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20438         
20439         if(this.groupId){
20440             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20441         }
20442         
20443         if(label){
20444             label.markValid();
20445         }
20446
20447         if(this.inputType == 'radio'){
20448             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20449                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20450                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20451             });
20452             
20453             return;
20454         }
20455
20456         if(!this.groupId){
20457             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20458             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20459             return;
20460         }
20461         
20462         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20463         
20464         if(!group){
20465             return;
20466         }
20467         
20468         for(var i in group){
20469             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20470             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20471         }
20472     },
20473     
20474      /**
20475      * Mark this field as invalid
20476      * @param {String} msg The validation message
20477      */
20478     markInvalid : function(msg)
20479     {
20480         if(this.allowBlank){
20481             return;
20482         }
20483         
20484         var _this = this;
20485         
20486         this.fireEvent('invalid', this, msg);
20487         
20488         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20489         
20490         if(this.groupId){
20491             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20492         }
20493         
20494         if(label){
20495             label.markInvalid();
20496         }
20497             
20498         if(this.inputType == 'radio'){
20499             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20500                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20501                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20502             });
20503             
20504             return;
20505         }
20506         
20507         if(!this.groupId){
20508             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20509             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20510             return;
20511         }
20512         
20513         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20514         
20515         if(!group){
20516             return;
20517         }
20518         
20519         for(var i in group){
20520             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20521             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20522         }
20523         
20524     },
20525     
20526     clearInvalid : function()
20527     {
20528         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20529         
20530         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20531         
20532         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20533         
20534         if (label) {
20535             label.iconEl.removeClass(label.validClass);
20536             label.iconEl.removeClass(label.invalidClass);
20537         }
20538     },
20539     
20540     disable : function()
20541     {
20542         if(this.inputType != 'radio'){
20543             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20544             return;
20545         }
20546         
20547         var _this = this;
20548         
20549         if(this.rendered){
20550             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20551                 _this.getActionEl().addClass(this.disabledClass);
20552                 e.dom.disabled = true;
20553             });
20554         }
20555         
20556         this.disabled = true;
20557         this.fireEvent("disable", this);
20558         return this;
20559     },
20560
20561     enable : function()
20562     {
20563         if(this.inputType != 'radio'){
20564             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20565             return;
20566         }
20567         
20568         var _this = this;
20569         
20570         if(this.rendered){
20571             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20572                 _this.getActionEl().removeClass(this.disabledClass);
20573                 e.dom.disabled = false;
20574             });
20575         }
20576         
20577         this.disabled = false;
20578         this.fireEvent("enable", this);
20579         return this;
20580     }
20581
20582 });
20583
20584 Roo.apply(Roo.bootstrap.CheckBox, {
20585     
20586     groups: {},
20587     
20588      /**
20589     * register a CheckBox Group
20590     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20591     */
20592     register : function(checkbox)
20593     {
20594         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20595             this.groups[checkbox.groupId] = {};
20596         }
20597         
20598         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20599             return;
20600         }
20601         
20602         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20603         
20604     },
20605     /**
20606     * fetch a CheckBox Group based on the group ID
20607     * @param {string} the group ID
20608     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20609     */
20610     get: function(groupId) {
20611         if (typeof(this.groups[groupId]) == 'undefined') {
20612             return false;
20613         }
20614         
20615         return this.groups[groupId] ;
20616     }
20617     
20618     
20619 });
20620 /*
20621  * - LGPL
20622  *
20623  * RadioItem
20624  * 
20625  */
20626
20627 /**
20628  * @class Roo.bootstrap.Radio
20629  * @extends Roo.bootstrap.Component
20630  * Bootstrap Radio class
20631  * @cfg {String} boxLabel - the label associated
20632  * @cfg {String} value - the value of radio
20633  * 
20634  * @constructor
20635  * Create a new Radio
20636  * @param {Object} config The config object
20637  */
20638 Roo.bootstrap.Radio = function(config){
20639     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20640     
20641 };
20642
20643 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20644     
20645     boxLabel : '',
20646     
20647     value : '',
20648     
20649     getAutoCreate : function()
20650     {
20651         var cfg = {
20652             tag : 'div',
20653             cls : 'form-group radio',
20654             cn : [
20655                 {
20656                     tag : 'label',
20657                     cls : 'box-label',
20658                     html : this.boxLabel
20659                 }
20660             ]
20661         };
20662         
20663         return cfg;
20664     },
20665     
20666     initEvents : function() 
20667     {
20668         this.parent().register(this);
20669         
20670         this.el.on('click', this.onClick, this);
20671         
20672     },
20673     
20674     onClick : function()
20675     {
20676         this.setChecked(true);
20677     },
20678     
20679     setChecked : function(state, suppressEvent)
20680     {
20681         this.parent().setValue(this.value, suppressEvent);
20682         
20683     }
20684     
20685 });
20686  
20687
20688  /*
20689  * - LGPL
20690  *
20691  * Input
20692  * 
20693  */
20694
20695 /**
20696  * @class Roo.bootstrap.SecurePass
20697  * @extends Roo.bootstrap.Input
20698  * Bootstrap SecurePass class
20699  *
20700  * 
20701  * @constructor
20702  * Create a new SecurePass
20703  * @param {Object} config The config object
20704  */
20705  
20706 Roo.bootstrap.SecurePass = function (config) {
20707     // these go here, so the translation tool can replace them..
20708     this.errors = {
20709         PwdEmpty: "Please type a password, and then retype it to confirm.",
20710         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20711         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20712         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20713         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20714         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20715         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20716         TooWeak: "Your password is Too Weak."
20717     },
20718     this.meterLabel = "Password strength:";
20719     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20720     this.meterClass = ["roo-password-meter-tooweak", 
20721                        "roo-password-meter-weak", 
20722                        "roo-password-meter-medium", 
20723                        "roo-password-meter-strong", 
20724                        "roo-password-meter-grey"],    
20725     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20726 }
20727
20728 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20729     /**
20730      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20731      * {
20732      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20733      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20734      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20735      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20736      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20737      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20738      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20739      * })
20740      */
20741     // private
20742     
20743     meterWidth: 300,
20744     errorMsg :'',    
20745     errors: {},
20746     imageRoot: '/',
20747     /**
20748      * @cfg {String/Object} Label for the strength meter (defaults to
20749      * 'Password strength:')
20750      */
20751     // private
20752     meterLabel: '',
20753     /**
20754      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20755      * ['Weak', 'Medium', 'Strong'])
20756      */
20757     // private    
20758     pwdStrengths: [],    
20759     // private
20760     strength: 0,
20761     // private
20762     _lastPwd: null,
20763     // private
20764     kCapitalLetter: 0,
20765     kSmallLetter: 1,
20766     kDigit: 2,
20767     kPunctuation: 3,
20768     
20769     insecure: false,
20770     // private
20771     initEvents: function () {
20772         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20773
20774         if (this.el.is('input[type=password]') && Roo.isSafari) {
20775             this.el.on('keydown', this.SafariOnKeyDown, this);
20776         }
20777
20778         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20779     },
20780     // private
20781     onRender: function (ct, position) {
20782         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20783         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20784         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20785
20786         this.trigger.createChild({
20787                    cn: [
20788                     {
20789                     //id: 'PwdMeter',
20790                     tag: 'div',
20791                     cls: 'roo-password-meter-grey col-xs-12',
20792                     style: {
20793                         //width: 0,
20794                         //width: this.meterWidth + 'px'                                                
20795                         }
20796                     },
20797                     {                            
20798                          cls: 'roo-password-meter-text'                          
20799                     }
20800                 ]            
20801         });
20802
20803         /*
20804         this.trigger.createChild({
20805             tag: 'div',
20806             cls: 'roo-password-meter-container col-xs-12',
20807             style: {               
20808                 //width: this.meterWidth + 'px'
20809             },
20810             cn: {
20811                 tag: 'div',
20812                 cls: 'roo-password-meter-grey',
20813                 style: {
20814                     //width: this.meterWidth + 'px'                                        
20815                 },
20816                 cn: [
20817                     {
20818                     //id: 'PwdMeter',
20819                     tag: 'div',
20820                     cls: 'roo-password-meter-grey col-xs-12',
20821                     style: {
20822                         //width: 0,
20823                         //width: this.meterWidth + 'px'                                                
20824                         }
20825                     },
20826                     {                            
20827                          cls: 'roo-password-meter-text'                          
20828                     }
20829                 ]                
20830             }
20831         });
20832         */
20833         if (this.hideTrigger) {
20834             this.trigger.setDisplayed(false);
20835         }
20836         this.setSize(this.width || '', this.height || '');
20837     },
20838     // private
20839     onDestroy: function () {
20840         if (this.trigger) {
20841             this.trigger.removeAllListeners();
20842             this.trigger.remove();
20843         }
20844         if (this.wrap) {
20845             this.wrap.remove();
20846         }
20847         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20848     },
20849     // private
20850     checkStrength: function () {
20851         var pwd = this.inputEl().getValue();
20852         if (pwd == this._lastPwd) {
20853             return;
20854         }
20855
20856         var strength;
20857         if (this.ClientSideStrongPassword(pwd)) {
20858             strength = 3;
20859         } else if (this.ClientSideMediumPassword(pwd)) {
20860             strength = 2;
20861         } else if (this.ClientSideWeakPassword(pwd)) {
20862             strength = 1;
20863         } else {
20864             strength = 0;
20865         }
20866         
20867         console.log('strength1: ' + strength);
20868         
20869         //var pm = this.trigger.child('div/div/div').dom;
20870         var pm = this.trigger.child('div/div');
20871         pm.removeClass(this.meterClass);
20872         pm.addClass(this.meterClass[strength]);
20873                 
20874         
20875         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20876                 
20877         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20878         
20879         this._lastPwd = pwd;
20880     },
20881     reset: function () {
20882         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20883         
20884         this._lastPwd = '';
20885         
20886         var pm = this.trigger.child('div/div');
20887         pm.removeClass(this.meterClass);
20888         pm.addClass('roo-password-meter-grey');        
20889         
20890         
20891         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20892         
20893         pt.innerHTML = '';
20894         this.inputEl().dom.type='password';
20895     },
20896     // private
20897     validateValue: function (value) {
20898         
20899         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20900             return false;
20901         }
20902         if (value.length == 0) {
20903             if (this.allowBlank) {
20904                 this.clearInvalid();
20905                 return true;
20906             }
20907
20908             this.markInvalid(this.errors.PwdEmpty);
20909             this.errorMsg = this.errors.PwdEmpty;
20910             return false;
20911         }
20912         
20913         if(this.insecure){
20914             return true;
20915         }
20916         
20917         if ('[\x21-\x7e]*'.match(value)) {
20918             this.markInvalid(this.errors.PwdBadChar);
20919             this.errorMsg = this.errors.PwdBadChar;
20920             return false;
20921         }
20922         if (value.length < 6) {
20923             this.markInvalid(this.errors.PwdShort);
20924             this.errorMsg = this.errors.PwdShort;
20925             return false;
20926         }
20927         if (value.length > 16) {
20928             this.markInvalid(this.errors.PwdLong);
20929             this.errorMsg = this.errors.PwdLong;
20930             return false;
20931         }
20932         var strength;
20933         if (this.ClientSideStrongPassword(value)) {
20934             strength = 3;
20935         } else if (this.ClientSideMediumPassword(value)) {
20936             strength = 2;
20937         } else if (this.ClientSideWeakPassword(value)) {
20938             strength = 1;
20939         } else {
20940             strength = 0;
20941         }
20942
20943         
20944         if (strength < 2) {
20945             //this.markInvalid(this.errors.TooWeak);
20946             this.errorMsg = this.errors.TooWeak;
20947             //return false;
20948         }
20949         
20950         
20951         console.log('strength2: ' + strength);
20952         
20953         //var pm = this.trigger.child('div/div/div').dom;
20954         
20955         var pm = this.trigger.child('div/div');
20956         pm.removeClass(this.meterClass);
20957         pm.addClass(this.meterClass[strength]);
20958                 
20959         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20960                 
20961         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20962         
20963         this.errorMsg = ''; 
20964         return true;
20965     },
20966     // private
20967     CharacterSetChecks: function (type) {
20968         this.type = type;
20969         this.fResult = false;
20970     },
20971     // private
20972     isctype: function (character, type) {
20973         switch (type) { //why needed break after return in js ? very odd bug
20974             case this.kCapitalLetter:
20975                 if (character >= 'A' && character <= 'Z') {
20976                     return true;
20977                 }
20978                 break;
20979             case this.kSmallLetter:
20980                 if (character >= 'a' && character <= 'z') {
20981                     return true;
20982                 }
20983                 break;
20984             case this.kDigit:
20985                 if (character >= '0' && character <= '9') {
20986                     return true;
20987                 }
20988                 break;
20989             case this.kPunctuation:
20990                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
20991                     return true;
20992                 }
20993                 break;
20994             default:
20995                 return false;
20996         }
20997
20998     },
20999     // private
21000     IsLongEnough: function (pwd, size) {
21001         return !(pwd == null || isNaN(size) || pwd.length < size);
21002     },
21003     // private
21004     SpansEnoughCharacterSets: function (word, nb) {
21005         if (!this.IsLongEnough(word, nb))
21006         {
21007             return false;
21008         }
21009
21010         var characterSetChecks = new Array(
21011                 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21012                 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation));
21013         for (var index = 0; index < word.length; ++index) {
21014             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21015                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21016                     characterSetChecks[nCharSet].fResult = true;
21017                     break;
21018                 }
21019             }
21020         }
21021
21022         var nCharSets = 0;
21023         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21024             if (characterSetChecks[nCharSet].fResult) {
21025                 ++nCharSets;
21026             }
21027         }
21028
21029         if (nCharSets < nb) {
21030             return false;
21031         }
21032         return true;
21033     },
21034     // private
21035     ClientSideStrongPassword: function (pwd) {
21036         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21037     },
21038     // private
21039     ClientSideMediumPassword: function (pwd) {
21040         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21041     },
21042     // private
21043     ClientSideWeakPassword: function (pwd) {
21044         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21045     }
21046           
21047 })//<script type="text/javascript">
21048
21049 /*
21050  * Based  Ext JS Library 1.1.1
21051  * Copyright(c) 2006-2007, Ext JS, LLC.
21052  * LGPL
21053  *
21054  */
21055  
21056 /**
21057  * @class Roo.HtmlEditorCore
21058  * @extends Roo.Component
21059  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21060  *
21061  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21062  */
21063
21064 Roo.HtmlEditorCore = function(config){
21065     
21066     
21067     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21068     
21069     
21070     this.addEvents({
21071         /**
21072          * @event initialize
21073          * Fires when the editor is fully initialized (including the iframe)
21074          * @param {Roo.HtmlEditorCore} this
21075          */
21076         initialize: true,
21077         /**
21078          * @event activate
21079          * Fires when the editor is first receives the focus. Any insertion must wait
21080          * until after this event.
21081          * @param {Roo.HtmlEditorCore} this
21082          */
21083         activate: true,
21084          /**
21085          * @event beforesync
21086          * Fires before the textarea is updated with content from the editor iframe. Return false
21087          * to cancel the sync.
21088          * @param {Roo.HtmlEditorCore} this
21089          * @param {String} html
21090          */
21091         beforesync: true,
21092          /**
21093          * @event beforepush
21094          * Fires before the iframe editor is updated with content from the textarea. Return false
21095          * to cancel the push.
21096          * @param {Roo.HtmlEditorCore} this
21097          * @param {String} html
21098          */
21099         beforepush: true,
21100          /**
21101          * @event sync
21102          * Fires when the textarea is updated with content from the editor iframe.
21103          * @param {Roo.HtmlEditorCore} this
21104          * @param {String} html
21105          */
21106         sync: true,
21107          /**
21108          * @event push
21109          * Fires when the iframe editor is updated with content from the textarea.
21110          * @param {Roo.HtmlEditorCore} this
21111          * @param {String} html
21112          */
21113         push: true,
21114         
21115         /**
21116          * @event editorevent
21117          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21118          * @param {Roo.HtmlEditorCore} this
21119          */
21120         editorevent: true
21121         
21122     });
21123     
21124     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21125     
21126     // defaults : white / black...
21127     this.applyBlacklists();
21128     
21129     
21130     
21131 };
21132
21133
21134 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21135
21136
21137      /**
21138      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21139      */
21140     
21141     owner : false,
21142     
21143      /**
21144      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21145      *                        Roo.resizable.
21146      */
21147     resizable : false,
21148      /**
21149      * @cfg {Number} height (in pixels)
21150      */   
21151     height: 300,
21152    /**
21153      * @cfg {Number} width (in pixels)
21154      */   
21155     width: 500,
21156     
21157     /**
21158      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21159      * 
21160      */
21161     stylesheets: false,
21162     
21163     // id of frame..
21164     frameId: false,
21165     
21166     // private properties
21167     validationEvent : false,
21168     deferHeight: true,
21169     initialized : false,
21170     activated : false,
21171     sourceEditMode : false,
21172     onFocus : Roo.emptyFn,
21173     iframePad:3,
21174     hideMode:'offsets',
21175     
21176     clearUp: true,
21177     
21178     // blacklist + whitelisted elements..
21179     black: false,
21180     white: false,
21181      
21182     
21183
21184     /**
21185      * Protected method that will not generally be called directly. It
21186      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21187      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21188      */
21189     getDocMarkup : function(){
21190         // body styles..
21191         var st = '';
21192         
21193         // inherit styels from page...?? 
21194         if (this.stylesheets === false) {
21195             
21196             Roo.get(document.head).select('style').each(function(node) {
21197                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21198             });
21199             
21200             Roo.get(document.head).select('link').each(function(node) { 
21201                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21202             });
21203             
21204         } else if (!this.stylesheets.length) {
21205                 // simple..
21206                 st = '<style type="text/css">' +
21207                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21208                    '</style>';
21209         } else { 
21210             
21211         }
21212         
21213         st +=  '<style type="text/css">' +
21214             'IMG { cursor: pointer } ' +
21215         '</style>';
21216
21217         
21218         return '<html><head>' + st  +
21219             //<style type="text/css">' +
21220             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21221             //'</style>' +
21222             ' </head><body class="roo-htmleditor-body"></body></html>';
21223     },
21224
21225     // private
21226     onRender : function(ct, position)
21227     {
21228         var _t = this;
21229         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21230         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21231         
21232         
21233         this.el.dom.style.border = '0 none';
21234         this.el.dom.setAttribute('tabIndex', -1);
21235         this.el.addClass('x-hidden hide');
21236         
21237         
21238         
21239         if(Roo.isIE){ // fix IE 1px bogus margin
21240             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21241         }
21242        
21243         
21244         this.frameId = Roo.id();
21245         
21246          
21247         
21248         var iframe = this.owner.wrap.createChild({
21249             tag: 'iframe',
21250             cls: 'form-control', // bootstrap..
21251             id: this.frameId,
21252             name: this.frameId,
21253             frameBorder : 'no',
21254             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21255         }, this.el
21256         );
21257         
21258         
21259         this.iframe = iframe.dom;
21260
21261          this.assignDocWin();
21262         
21263         this.doc.designMode = 'on';
21264        
21265         this.doc.open();
21266         this.doc.write(this.getDocMarkup());
21267         this.doc.close();
21268
21269         
21270         var task = { // must defer to wait for browser to be ready
21271             run : function(){
21272                 //console.log("run task?" + this.doc.readyState);
21273                 this.assignDocWin();
21274                 if(this.doc.body || this.doc.readyState == 'complete'){
21275                     try {
21276                         this.doc.designMode="on";
21277                     } catch (e) {
21278                         return;
21279                     }
21280                     Roo.TaskMgr.stop(task);
21281                     this.initEditor.defer(10, this);
21282                 }
21283             },
21284             interval : 10,
21285             duration: 10000,
21286             scope: this
21287         };
21288         Roo.TaskMgr.start(task);
21289
21290     },
21291
21292     // private
21293     onResize : function(w, h)
21294     {
21295          Roo.log('resize: ' +w + ',' + h );
21296         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21297         if(!this.iframe){
21298             return;
21299         }
21300         if(typeof w == 'number'){
21301             
21302             this.iframe.style.width = w + 'px';
21303         }
21304         if(typeof h == 'number'){
21305             
21306             this.iframe.style.height = h + 'px';
21307             if(this.doc){
21308                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21309             }
21310         }
21311         
21312     },
21313
21314     /**
21315      * Toggles the editor between standard and source edit mode.
21316      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21317      */
21318     toggleSourceEdit : function(sourceEditMode){
21319         
21320         this.sourceEditMode = sourceEditMode === true;
21321         
21322         if(this.sourceEditMode){
21323  
21324             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21325             
21326         }else{
21327             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21328             //this.iframe.className = '';
21329             this.deferFocus();
21330         }
21331         //this.setSize(this.owner.wrap.getSize());
21332         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21333     },
21334
21335     
21336   
21337
21338     /**
21339      * Protected method that will not generally be called directly. If you need/want
21340      * custom HTML cleanup, this is the method you should override.
21341      * @param {String} html The HTML to be cleaned
21342      * return {String} The cleaned HTML
21343      */
21344     cleanHtml : function(html){
21345         html = String(html);
21346         if(html.length > 5){
21347             if(Roo.isSafari){ // strip safari nonsense
21348                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21349             }
21350         }
21351         if(html == '&nbsp;'){
21352             html = '';
21353         }
21354         return html;
21355     },
21356
21357     /**
21358      * HTML Editor -> Textarea
21359      * Protected method that will not generally be called directly. Syncs the contents
21360      * of the editor iframe with the textarea.
21361      */
21362     syncValue : function(){
21363         if(this.initialized){
21364             var bd = (this.doc.body || this.doc.documentElement);
21365             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21366             var html = bd.innerHTML;
21367             if(Roo.isSafari){
21368                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21369                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21370                 if(m && m[1]){
21371                     html = '<div style="'+m[0]+'">' + html + '</div>';
21372                 }
21373             }
21374             html = this.cleanHtml(html);
21375             // fix up the special chars.. normaly like back quotes in word...
21376             // however we do not want to do this with chinese..
21377             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21378                 var cc = b.charCodeAt();
21379                 if (
21380                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21381                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21382                     (cc >= 0xf900 && cc < 0xfb00 )
21383                 ) {
21384                         return b;
21385                 }
21386                 return "&#"+cc+";" 
21387             });
21388             if(this.owner.fireEvent('beforesync', this, html) !== false){
21389                 this.el.dom.value = html;
21390                 this.owner.fireEvent('sync', this, html);
21391             }
21392         }
21393     },
21394
21395     /**
21396      * Protected method that will not generally be called directly. Pushes the value of the textarea
21397      * into the iframe editor.
21398      */
21399     pushValue : function(){
21400         if(this.initialized){
21401             var v = this.el.dom.value.trim();
21402             
21403 //            if(v.length < 1){
21404 //                v = '&#160;';
21405 //            }
21406             
21407             if(this.owner.fireEvent('beforepush', this, v) !== false){
21408                 var d = (this.doc.body || this.doc.documentElement);
21409                 d.innerHTML = v;
21410                 this.cleanUpPaste();
21411                 this.el.dom.value = d.innerHTML;
21412                 this.owner.fireEvent('push', this, v);
21413             }
21414         }
21415     },
21416
21417     // private
21418     deferFocus : function(){
21419         this.focus.defer(10, this);
21420     },
21421
21422     // doc'ed in Field
21423     focus : function(){
21424         if(this.win && !this.sourceEditMode){
21425             this.win.focus();
21426         }else{
21427             this.el.focus();
21428         }
21429     },
21430     
21431     assignDocWin: function()
21432     {
21433         var iframe = this.iframe;
21434         
21435          if(Roo.isIE){
21436             this.doc = iframe.contentWindow.document;
21437             this.win = iframe.contentWindow;
21438         } else {
21439 //            if (!Roo.get(this.frameId)) {
21440 //                return;
21441 //            }
21442 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21443 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21444             
21445             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21446                 return;
21447             }
21448             
21449             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21450             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21451         }
21452     },
21453     
21454     // private
21455     initEditor : function(){
21456         //console.log("INIT EDITOR");
21457         this.assignDocWin();
21458         
21459         
21460         
21461         this.doc.designMode="on";
21462         this.doc.open();
21463         this.doc.write(this.getDocMarkup());
21464         this.doc.close();
21465         
21466         var dbody = (this.doc.body || this.doc.documentElement);
21467         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21468         // this copies styles from the containing element into thsi one..
21469         // not sure why we need all of this..
21470         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21471         
21472         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21473         //ss['background-attachment'] = 'fixed'; // w3c
21474         dbody.bgProperties = 'fixed'; // ie
21475         //Roo.DomHelper.applyStyles(dbody, ss);
21476         Roo.EventManager.on(this.doc, {
21477             //'mousedown': this.onEditorEvent,
21478             'mouseup': this.onEditorEvent,
21479             'dblclick': this.onEditorEvent,
21480             'click': this.onEditorEvent,
21481             'keyup': this.onEditorEvent,
21482             buffer:100,
21483             scope: this
21484         });
21485         if(Roo.isGecko){
21486             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21487         }
21488         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21489             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21490         }
21491         this.initialized = true;
21492
21493         this.owner.fireEvent('initialize', this);
21494         this.pushValue();
21495     },
21496
21497     // private
21498     onDestroy : function(){
21499         
21500         
21501         
21502         if(this.rendered){
21503             
21504             //for (var i =0; i < this.toolbars.length;i++) {
21505             //    // fixme - ask toolbars for heights?
21506             //    this.toolbars[i].onDestroy();
21507            // }
21508             
21509             //this.wrap.dom.innerHTML = '';
21510             //this.wrap.remove();
21511         }
21512     },
21513
21514     // private
21515     onFirstFocus : function(){
21516         
21517         this.assignDocWin();
21518         
21519         
21520         this.activated = true;
21521          
21522     
21523         if(Roo.isGecko){ // prevent silly gecko errors
21524             this.win.focus();
21525             var s = this.win.getSelection();
21526             if(!s.focusNode || s.focusNode.nodeType != 3){
21527                 var r = s.getRangeAt(0);
21528                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21529                 r.collapse(true);
21530                 this.deferFocus();
21531             }
21532             try{
21533                 this.execCmd('useCSS', true);
21534                 this.execCmd('styleWithCSS', false);
21535             }catch(e){}
21536         }
21537         this.owner.fireEvent('activate', this);
21538     },
21539
21540     // private
21541     adjustFont: function(btn){
21542         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21543         //if(Roo.isSafari){ // safari
21544         //    adjust *= 2;
21545        // }
21546         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21547         if(Roo.isSafari){ // safari
21548             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21549             v =  (v < 10) ? 10 : v;
21550             v =  (v > 48) ? 48 : v;
21551             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21552             
21553         }
21554         
21555         
21556         v = Math.max(1, v+adjust);
21557         
21558         this.execCmd('FontSize', v  );
21559     },
21560
21561     onEditorEvent : function(e)
21562     {
21563         this.owner.fireEvent('editorevent', this, e);
21564       //  this.updateToolbar();
21565         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21566     },
21567
21568     insertTag : function(tg)
21569     {
21570         // could be a bit smarter... -> wrap the current selected tRoo..
21571         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21572             
21573             range = this.createRange(this.getSelection());
21574             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21575             wrappingNode.appendChild(range.extractContents());
21576             range.insertNode(wrappingNode);
21577
21578             return;
21579             
21580             
21581             
21582         }
21583         this.execCmd("formatblock",   tg);
21584         
21585     },
21586     
21587     insertText : function(txt)
21588     {
21589         
21590         
21591         var range = this.createRange();
21592         range.deleteContents();
21593                //alert(Sender.getAttribute('label'));
21594                
21595         range.insertNode(this.doc.createTextNode(txt));
21596     } ,
21597     
21598      
21599
21600     /**
21601      * Executes a Midas editor command on the editor document and performs necessary focus and
21602      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21603      * @param {String} cmd The Midas command
21604      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21605      */
21606     relayCmd : function(cmd, value){
21607         this.win.focus();
21608         this.execCmd(cmd, value);
21609         this.owner.fireEvent('editorevent', this);
21610         //this.updateToolbar();
21611         this.owner.deferFocus();
21612     },
21613
21614     /**
21615      * Executes a Midas editor command directly on the editor document.
21616      * For visual commands, you should use {@link #relayCmd} instead.
21617      * <b>This should only be called after the editor is initialized.</b>
21618      * @param {String} cmd The Midas command
21619      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21620      */
21621     execCmd : function(cmd, value){
21622         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21623         this.syncValue();
21624     },
21625  
21626  
21627    
21628     /**
21629      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21630      * to insert tRoo.
21631      * @param {String} text | dom node.. 
21632      */
21633     insertAtCursor : function(text)
21634     {
21635         
21636         
21637         
21638         if(!this.activated){
21639             return;
21640         }
21641         /*
21642         if(Roo.isIE){
21643             this.win.focus();
21644             var r = this.doc.selection.createRange();
21645             if(r){
21646                 r.collapse(true);
21647                 r.pasteHTML(text);
21648                 this.syncValue();
21649                 this.deferFocus();
21650             
21651             }
21652             return;
21653         }
21654         */
21655         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21656             this.win.focus();
21657             
21658             
21659             // from jquery ui (MIT licenced)
21660             var range, node;
21661             var win = this.win;
21662             
21663             if (win.getSelection && win.getSelection().getRangeAt) {
21664                 range = win.getSelection().getRangeAt(0);
21665                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21666                 range.insertNode(node);
21667             } else if (win.document.selection && win.document.selection.createRange) {
21668                 // no firefox support
21669                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21670                 win.document.selection.createRange().pasteHTML(txt);
21671             } else {
21672                 // no firefox support
21673                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21674                 this.execCmd('InsertHTML', txt);
21675             } 
21676             
21677             this.syncValue();
21678             
21679             this.deferFocus();
21680         }
21681     },
21682  // private
21683     mozKeyPress : function(e){
21684         if(e.ctrlKey){
21685             var c = e.getCharCode(), cmd;
21686           
21687             if(c > 0){
21688                 c = String.fromCharCode(c).toLowerCase();
21689                 switch(c){
21690                     case 'b':
21691                         cmd = 'bold';
21692                         break;
21693                     case 'i':
21694                         cmd = 'italic';
21695                         break;
21696                     
21697                     case 'u':
21698                         cmd = 'underline';
21699                         break;
21700                     
21701                     case 'v':
21702                         this.cleanUpPaste.defer(100, this);
21703                         return;
21704                         
21705                 }
21706                 if(cmd){
21707                     this.win.focus();
21708                     this.execCmd(cmd);
21709                     this.deferFocus();
21710                     e.preventDefault();
21711                 }
21712                 
21713             }
21714         }
21715     },
21716
21717     // private
21718     fixKeys : function(){ // load time branching for fastest keydown performance
21719         if(Roo.isIE){
21720             return function(e){
21721                 var k = e.getKey(), r;
21722                 if(k == e.TAB){
21723                     e.stopEvent();
21724                     r = this.doc.selection.createRange();
21725                     if(r){
21726                         r.collapse(true);
21727                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21728                         this.deferFocus();
21729                     }
21730                     return;
21731                 }
21732                 
21733                 if(k == e.ENTER){
21734                     r = this.doc.selection.createRange();
21735                     if(r){
21736                         var target = r.parentElement();
21737                         if(!target || target.tagName.toLowerCase() != 'li'){
21738                             e.stopEvent();
21739                             r.pasteHTML('<br />');
21740                             r.collapse(false);
21741                             r.select();
21742                         }
21743                     }
21744                 }
21745                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21746                     this.cleanUpPaste.defer(100, this);
21747                     return;
21748                 }
21749                 
21750                 
21751             };
21752         }else if(Roo.isOpera){
21753             return function(e){
21754                 var k = e.getKey();
21755                 if(k == e.TAB){
21756                     e.stopEvent();
21757                     this.win.focus();
21758                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21759                     this.deferFocus();
21760                 }
21761                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21762                     this.cleanUpPaste.defer(100, this);
21763                     return;
21764                 }
21765                 
21766             };
21767         }else if(Roo.isSafari){
21768             return function(e){
21769                 var k = e.getKey();
21770                 
21771                 if(k == e.TAB){
21772                     e.stopEvent();
21773                     this.execCmd('InsertText','\t');
21774                     this.deferFocus();
21775                     return;
21776                 }
21777                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21778                     this.cleanUpPaste.defer(100, this);
21779                     return;
21780                 }
21781                 
21782              };
21783         }
21784     }(),
21785     
21786     getAllAncestors: function()
21787     {
21788         var p = this.getSelectedNode();
21789         var a = [];
21790         if (!p) {
21791             a.push(p); // push blank onto stack..
21792             p = this.getParentElement();
21793         }
21794         
21795         
21796         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21797             a.push(p);
21798             p = p.parentNode;
21799         }
21800         a.push(this.doc.body);
21801         return a;
21802     },
21803     lastSel : false,
21804     lastSelNode : false,
21805     
21806     
21807     getSelection : function() 
21808     {
21809         this.assignDocWin();
21810         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21811     },
21812     
21813     getSelectedNode: function() 
21814     {
21815         // this may only work on Gecko!!!
21816         
21817         // should we cache this!!!!
21818         
21819         
21820         
21821          
21822         var range = this.createRange(this.getSelection()).cloneRange();
21823         
21824         if (Roo.isIE) {
21825             var parent = range.parentElement();
21826             while (true) {
21827                 var testRange = range.duplicate();
21828                 testRange.moveToElementText(parent);
21829                 if (testRange.inRange(range)) {
21830                     break;
21831                 }
21832                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21833                     break;
21834                 }
21835                 parent = parent.parentElement;
21836             }
21837             return parent;
21838         }
21839         
21840         // is ancestor a text element.
21841         var ac =  range.commonAncestorContainer;
21842         if (ac.nodeType == 3) {
21843             ac = ac.parentNode;
21844         }
21845         
21846         var ar = ac.childNodes;
21847          
21848         var nodes = [];
21849         var other_nodes = [];
21850         var has_other_nodes = false;
21851         for (var i=0;i<ar.length;i++) {
21852             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21853                 continue;
21854             }
21855             // fullly contained node.
21856             
21857             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21858                 nodes.push(ar[i]);
21859                 continue;
21860             }
21861             
21862             // probably selected..
21863             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21864                 other_nodes.push(ar[i]);
21865                 continue;
21866             }
21867             // outer..
21868             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21869                 continue;
21870             }
21871             
21872             
21873             has_other_nodes = true;
21874         }
21875         if (!nodes.length && other_nodes.length) {
21876             nodes= other_nodes;
21877         }
21878         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21879             return false;
21880         }
21881         
21882         return nodes[0];
21883     },
21884     createRange: function(sel)
21885     {
21886         // this has strange effects when using with 
21887         // top toolbar - not sure if it's a great idea.
21888         //this.editor.contentWindow.focus();
21889         if (typeof sel != "undefined") {
21890             try {
21891                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21892             } catch(e) {
21893                 return this.doc.createRange();
21894             }
21895         } else {
21896             return this.doc.createRange();
21897         }
21898     },
21899     getParentElement: function()
21900     {
21901         
21902         this.assignDocWin();
21903         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21904         
21905         var range = this.createRange(sel);
21906          
21907         try {
21908             var p = range.commonAncestorContainer;
21909             while (p.nodeType == 3) { // text node
21910                 p = p.parentNode;
21911             }
21912             return p;
21913         } catch (e) {
21914             return null;
21915         }
21916     
21917     },
21918     /***
21919      *
21920      * Range intersection.. the hard stuff...
21921      *  '-1' = before
21922      *  '0' = hits..
21923      *  '1' = after.
21924      *         [ -- selected range --- ]
21925      *   [fail]                        [fail]
21926      *
21927      *    basically..
21928      *      if end is before start or  hits it. fail.
21929      *      if start is after end or hits it fail.
21930      *
21931      *   if either hits (but other is outside. - then it's not 
21932      *   
21933      *    
21934      **/
21935     
21936     
21937     // @see http://www.thismuchiknow.co.uk/?p=64.
21938     rangeIntersectsNode : function(range, node)
21939     {
21940         var nodeRange = node.ownerDocument.createRange();
21941         try {
21942             nodeRange.selectNode(node);
21943         } catch (e) {
21944             nodeRange.selectNodeContents(node);
21945         }
21946     
21947         var rangeStartRange = range.cloneRange();
21948         rangeStartRange.collapse(true);
21949     
21950         var rangeEndRange = range.cloneRange();
21951         rangeEndRange.collapse(false);
21952     
21953         var nodeStartRange = nodeRange.cloneRange();
21954         nodeStartRange.collapse(true);
21955     
21956         var nodeEndRange = nodeRange.cloneRange();
21957         nodeEndRange.collapse(false);
21958     
21959         return rangeStartRange.compareBoundaryPoints(
21960                  Range.START_TO_START, nodeEndRange) == -1 &&
21961                rangeEndRange.compareBoundaryPoints(
21962                  Range.START_TO_START, nodeStartRange) == 1;
21963         
21964          
21965     },
21966     rangeCompareNode : function(range, node)
21967     {
21968         var nodeRange = node.ownerDocument.createRange();
21969         try {
21970             nodeRange.selectNode(node);
21971         } catch (e) {
21972             nodeRange.selectNodeContents(node);
21973         }
21974         
21975         
21976         range.collapse(true);
21977     
21978         nodeRange.collapse(true);
21979      
21980         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21981         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21982          
21983         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21984         
21985         var nodeIsBefore   =  ss == 1;
21986         var nodeIsAfter    = ee == -1;
21987         
21988         if (nodeIsBefore && nodeIsAfter) {
21989             return 0; // outer
21990         }
21991         if (!nodeIsBefore && nodeIsAfter) {
21992             return 1; //right trailed.
21993         }
21994         
21995         if (nodeIsBefore && !nodeIsAfter) {
21996             return 2;  // left trailed.
21997         }
21998         // fully contined.
21999         return 3;
22000     },
22001
22002     // private? - in a new class?
22003     cleanUpPaste :  function()
22004     {
22005         // cleans up the whole document..
22006         Roo.log('cleanuppaste');
22007         
22008         this.cleanUpChildren(this.doc.body);
22009         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22010         if (clean != this.doc.body.innerHTML) {
22011             this.doc.body.innerHTML = clean;
22012         }
22013         
22014     },
22015     
22016     cleanWordChars : function(input) {// change the chars to hex code
22017         var he = Roo.HtmlEditorCore;
22018         
22019         var output = input;
22020         Roo.each(he.swapCodes, function(sw) { 
22021             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22022             
22023             output = output.replace(swapper, sw[1]);
22024         });
22025         
22026         return output;
22027     },
22028     
22029     
22030     cleanUpChildren : function (n)
22031     {
22032         if (!n.childNodes.length) {
22033             return;
22034         }
22035         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22036            this.cleanUpChild(n.childNodes[i]);
22037         }
22038     },
22039     
22040     
22041         
22042     
22043     cleanUpChild : function (node)
22044     {
22045         var ed = this;
22046         //console.log(node);
22047         if (node.nodeName == "#text") {
22048             // clean up silly Windows -- stuff?
22049             return; 
22050         }
22051         if (node.nodeName == "#comment") {
22052             node.parentNode.removeChild(node);
22053             // clean up silly Windows -- stuff?
22054             return; 
22055         }
22056         var lcname = node.tagName.toLowerCase();
22057         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22058         // whitelist of tags..
22059         
22060         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22061             // remove node.
22062             node.parentNode.removeChild(node);
22063             return;
22064             
22065         }
22066         
22067         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22068         
22069         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22070         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22071         
22072         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22073         //    remove_keep_children = true;
22074         //}
22075         
22076         if (remove_keep_children) {
22077             this.cleanUpChildren(node);
22078             // inserts everything just before this node...
22079             while (node.childNodes.length) {
22080                 var cn = node.childNodes[0];
22081                 node.removeChild(cn);
22082                 node.parentNode.insertBefore(cn, node);
22083             }
22084             node.parentNode.removeChild(node);
22085             return;
22086         }
22087         
22088         if (!node.attributes || !node.attributes.length) {
22089             this.cleanUpChildren(node);
22090             return;
22091         }
22092         
22093         function cleanAttr(n,v)
22094         {
22095             
22096             if (v.match(/^\./) || v.match(/^\//)) {
22097                 return;
22098             }
22099             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22100                 return;
22101             }
22102             if (v.match(/^#/)) {
22103                 return;
22104             }
22105 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22106             node.removeAttribute(n);
22107             
22108         }
22109         
22110         var cwhite = this.cwhite;
22111         var cblack = this.cblack;
22112             
22113         function cleanStyle(n,v)
22114         {
22115             if (v.match(/expression/)) { //XSS?? should we even bother..
22116                 node.removeAttribute(n);
22117                 return;
22118             }
22119             
22120             var parts = v.split(/;/);
22121             var clean = [];
22122             
22123             Roo.each(parts, function(p) {
22124                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22125                 if (!p.length) {
22126                     return true;
22127                 }
22128                 var l = p.split(':').shift().replace(/\s+/g,'');
22129                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22130                 
22131                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22132 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22133                     //node.removeAttribute(n);
22134                     return true;
22135                 }
22136                 //Roo.log()
22137                 // only allow 'c whitelisted system attributes'
22138                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22139 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22140                     //node.removeAttribute(n);
22141                     return true;
22142                 }
22143                 
22144                 
22145                  
22146                 
22147                 clean.push(p);
22148                 return true;
22149             });
22150             if (clean.length) { 
22151                 node.setAttribute(n, clean.join(';'));
22152             } else {
22153                 node.removeAttribute(n);
22154             }
22155             
22156         }
22157         
22158         
22159         for (var i = node.attributes.length-1; i > -1 ; i--) {
22160             var a = node.attributes[i];
22161             //console.log(a);
22162             
22163             if (a.name.toLowerCase().substr(0,2)=='on')  {
22164                 node.removeAttribute(a.name);
22165                 continue;
22166             }
22167             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22168                 node.removeAttribute(a.name);
22169                 continue;
22170             }
22171             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22172                 cleanAttr(a.name,a.value); // fixme..
22173                 continue;
22174             }
22175             if (a.name == 'style') {
22176                 cleanStyle(a.name,a.value);
22177                 continue;
22178             }
22179             /// clean up MS crap..
22180             // tecnically this should be a list of valid class'es..
22181             
22182             
22183             if (a.name == 'class') {
22184                 if (a.value.match(/^Mso/)) {
22185                     node.className = '';
22186                 }
22187                 
22188                 if (a.value.match(/body/)) {
22189                     node.className = '';
22190                 }
22191                 continue;
22192             }
22193             
22194             // style cleanup!?
22195             // class cleanup?
22196             
22197         }
22198         
22199         
22200         this.cleanUpChildren(node);
22201         
22202         
22203     },
22204     
22205     /**
22206      * Clean up MS wordisms...
22207      */
22208     cleanWord : function(node)
22209     {
22210         
22211         
22212         if (!node) {
22213             this.cleanWord(this.doc.body);
22214             return;
22215         }
22216         if (node.nodeName == "#text") {
22217             // clean up silly Windows -- stuff?
22218             return; 
22219         }
22220         if (node.nodeName == "#comment") {
22221             node.parentNode.removeChild(node);
22222             // clean up silly Windows -- stuff?
22223             return; 
22224         }
22225         
22226         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22227             node.parentNode.removeChild(node);
22228             return;
22229         }
22230         
22231         // remove - but keep children..
22232         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22233             while (node.childNodes.length) {
22234                 var cn = node.childNodes[0];
22235                 node.removeChild(cn);
22236                 node.parentNode.insertBefore(cn, node);
22237             }
22238             node.parentNode.removeChild(node);
22239             this.iterateChildren(node, this.cleanWord);
22240             return;
22241         }
22242         // clean styles
22243         if (node.className.length) {
22244             
22245             var cn = node.className.split(/\W+/);
22246             var cna = [];
22247             Roo.each(cn, function(cls) {
22248                 if (cls.match(/Mso[a-zA-Z]+/)) {
22249                     return;
22250                 }
22251                 cna.push(cls);
22252             });
22253             node.className = cna.length ? cna.join(' ') : '';
22254             if (!cna.length) {
22255                 node.removeAttribute("class");
22256             }
22257         }
22258         
22259         if (node.hasAttribute("lang")) {
22260             node.removeAttribute("lang");
22261         }
22262         
22263         if (node.hasAttribute("style")) {
22264             
22265             var styles = node.getAttribute("style").split(";");
22266             var nstyle = [];
22267             Roo.each(styles, function(s) {
22268                 if (!s.match(/:/)) {
22269                     return;
22270                 }
22271                 var kv = s.split(":");
22272                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22273                     return;
22274                 }
22275                 // what ever is left... we allow.
22276                 nstyle.push(s);
22277             });
22278             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22279             if (!nstyle.length) {
22280                 node.removeAttribute('style');
22281             }
22282         }
22283         this.iterateChildren(node, this.cleanWord);
22284         
22285         
22286         
22287     },
22288     /**
22289      * iterateChildren of a Node, calling fn each time, using this as the scole..
22290      * @param {DomNode} node node to iterate children of.
22291      * @param {Function} fn method of this class to call on each item.
22292      */
22293     iterateChildren : function(node, fn)
22294     {
22295         if (!node.childNodes.length) {
22296                 return;
22297         }
22298         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22299            fn.call(this, node.childNodes[i])
22300         }
22301     },
22302     
22303     
22304     /**
22305      * cleanTableWidths.
22306      *
22307      * Quite often pasting from word etc.. results in tables with column and widths.
22308      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22309      *
22310      */
22311     cleanTableWidths : function(node)
22312     {
22313          
22314          
22315         if (!node) {
22316             this.cleanTableWidths(this.doc.body);
22317             return;
22318         }
22319         
22320         // ignore list...
22321         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22322             return; 
22323         }
22324         Roo.log(node.tagName);
22325         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22326             this.iterateChildren(node, this.cleanTableWidths);
22327             return;
22328         }
22329         if (node.hasAttribute('width')) {
22330             node.removeAttribute('width');
22331         }
22332         
22333          
22334         if (node.hasAttribute("style")) {
22335             // pretty basic...
22336             
22337             var styles = node.getAttribute("style").split(";");
22338             var nstyle = [];
22339             Roo.each(styles, function(s) {
22340                 if (!s.match(/:/)) {
22341                     return;
22342                 }
22343                 var kv = s.split(":");
22344                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22345                     return;
22346                 }
22347                 // what ever is left... we allow.
22348                 nstyle.push(s);
22349             });
22350             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22351             if (!nstyle.length) {
22352                 node.removeAttribute('style');
22353             }
22354         }
22355         
22356         this.iterateChildren(node, this.cleanTableWidths);
22357         
22358         
22359     },
22360     
22361     
22362     
22363     
22364     domToHTML : function(currentElement, depth, nopadtext) {
22365         
22366         depth = depth || 0;
22367         nopadtext = nopadtext || false;
22368     
22369         if (!currentElement) {
22370             return this.domToHTML(this.doc.body);
22371         }
22372         
22373         //Roo.log(currentElement);
22374         var j;
22375         var allText = false;
22376         var nodeName = currentElement.nodeName;
22377         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22378         
22379         if  (nodeName == '#text') {
22380             
22381             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22382         }
22383         
22384         
22385         var ret = '';
22386         if (nodeName != 'BODY') {
22387              
22388             var i = 0;
22389             // Prints the node tagName, such as <A>, <IMG>, etc
22390             if (tagName) {
22391                 var attr = [];
22392                 for(i = 0; i < currentElement.attributes.length;i++) {
22393                     // quoting?
22394                     var aname = currentElement.attributes.item(i).name;
22395                     if (!currentElement.attributes.item(i).value.length) {
22396                         continue;
22397                     }
22398                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22399                 }
22400                 
22401                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22402             } 
22403             else {
22404                 
22405                 // eack
22406             }
22407         } else {
22408             tagName = false;
22409         }
22410         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22411             return ret;
22412         }
22413         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22414             nopadtext = true;
22415         }
22416         
22417         
22418         // Traverse the tree
22419         i = 0;
22420         var currentElementChild = currentElement.childNodes.item(i);
22421         var allText = true;
22422         var innerHTML  = '';
22423         lastnode = '';
22424         while (currentElementChild) {
22425             // Formatting code (indent the tree so it looks nice on the screen)
22426             var nopad = nopadtext;
22427             if (lastnode == 'SPAN') {
22428                 nopad  = true;
22429             }
22430             // text
22431             if  (currentElementChild.nodeName == '#text') {
22432                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22433                 toadd = nopadtext ? toadd : toadd.trim();
22434                 if (!nopad && toadd.length > 80) {
22435                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22436                 }
22437                 innerHTML  += toadd;
22438                 
22439                 i++;
22440                 currentElementChild = currentElement.childNodes.item(i);
22441                 lastNode = '';
22442                 continue;
22443             }
22444             allText = false;
22445             
22446             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22447                 
22448             // Recursively traverse the tree structure of the child node
22449             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22450             lastnode = currentElementChild.nodeName;
22451             i++;
22452             currentElementChild=currentElement.childNodes.item(i);
22453         }
22454         
22455         ret += innerHTML;
22456         
22457         if (!allText) {
22458                 // The remaining code is mostly for formatting the tree
22459             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22460         }
22461         
22462         
22463         if (tagName) {
22464             ret+= "</"+tagName+">";
22465         }
22466         return ret;
22467         
22468     },
22469         
22470     applyBlacklists : function()
22471     {
22472         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22473         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22474         
22475         this.white = [];
22476         this.black = [];
22477         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22478             if (b.indexOf(tag) > -1) {
22479                 return;
22480             }
22481             this.white.push(tag);
22482             
22483         }, this);
22484         
22485         Roo.each(w, function(tag) {
22486             if (b.indexOf(tag) > -1) {
22487                 return;
22488             }
22489             if (this.white.indexOf(tag) > -1) {
22490                 return;
22491             }
22492             this.white.push(tag);
22493             
22494         }, this);
22495         
22496         
22497         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22498             if (w.indexOf(tag) > -1) {
22499                 return;
22500             }
22501             this.black.push(tag);
22502             
22503         }, this);
22504         
22505         Roo.each(b, function(tag) {
22506             if (w.indexOf(tag) > -1) {
22507                 return;
22508             }
22509             if (this.black.indexOf(tag) > -1) {
22510                 return;
22511             }
22512             this.black.push(tag);
22513             
22514         }, this);
22515         
22516         
22517         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22518         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22519         
22520         this.cwhite = [];
22521         this.cblack = [];
22522         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22523             if (b.indexOf(tag) > -1) {
22524                 return;
22525             }
22526             this.cwhite.push(tag);
22527             
22528         }, this);
22529         
22530         Roo.each(w, function(tag) {
22531             if (b.indexOf(tag) > -1) {
22532                 return;
22533             }
22534             if (this.cwhite.indexOf(tag) > -1) {
22535                 return;
22536             }
22537             this.cwhite.push(tag);
22538             
22539         }, this);
22540         
22541         
22542         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22543             if (w.indexOf(tag) > -1) {
22544                 return;
22545             }
22546             this.cblack.push(tag);
22547             
22548         }, this);
22549         
22550         Roo.each(b, function(tag) {
22551             if (w.indexOf(tag) > -1) {
22552                 return;
22553             }
22554             if (this.cblack.indexOf(tag) > -1) {
22555                 return;
22556             }
22557             this.cblack.push(tag);
22558             
22559         }, this);
22560     },
22561     
22562     setStylesheets : function(stylesheets)
22563     {
22564         if(typeof(stylesheets) == 'string'){
22565             Roo.get(this.iframe.contentDocument.head).createChild({
22566                 tag : 'link',
22567                 rel : 'stylesheet',
22568                 type : 'text/css',
22569                 href : stylesheets
22570             });
22571             
22572             return;
22573         }
22574         var _this = this;
22575      
22576         Roo.each(stylesheets, function(s) {
22577             if(!s.length){
22578                 return;
22579             }
22580             
22581             Roo.get(_this.iframe.contentDocument.head).createChild({
22582                 tag : 'link',
22583                 rel : 'stylesheet',
22584                 type : 'text/css',
22585                 href : s
22586             });
22587         });
22588
22589         
22590     },
22591     
22592     removeStylesheets : function()
22593     {
22594         var _this = this;
22595         
22596         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22597             s.remove();
22598         });
22599     }
22600     
22601     // hide stuff that is not compatible
22602     /**
22603      * @event blur
22604      * @hide
22605      */
22606     /**
22607      * @event change
22608      * @hide
22609      */
22610     /**
22611      * @event focus
22612      * @hide
22613      */
22614     /**
22615      * @event specialkey
22616      * @hide
22617      */
22618     /**
22619      * @cfg {String} fieldClass @hide
22620      */
22621     /**
22622      * @cfg {String} focusClass @hide
22623      */
22624     /**
22625      * @cfg {String} autoCreate @hide
22626      */
22627     /**
22628      * @cfg {String} inputType @hide
22629      */
22630     /**
22631      * @cfg {String} invalidClass @hide
22632      */
22633     /**
22634      * @cfg {String} invalidText @hide
22635      */
22636     /**
22637      * @cfg {String} msgFx @hide
22638      */
22639     /**
22640      * @cfg {String} validateOnBlur @hide
22641      */
22642 });
22643
22644 Roo.HtmlEditorCore.white = [
22645         'area', 'br', 'img', 'input', 'hr', 'wbr',
22646         
22647        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22648        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22649        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22650        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22651        'table',   'ul',         'xmp', 
22652        
22653        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22654       'thead',   'tr', 
22655      
22656       'dir', 'menu', 'ol', 'ul', 'dl',
22657        
22658       'embed',  'object'
22659 ];
22660
22661
22662 Roo.HtmlEditorCore.black = [
22663     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22664         'applet', // 
22665         'base',   'basefont', 'bgsound', 'blink',  'body', 
22666         'frame',  'frameset', 'head',    'html',   'ilayer', 
22667         'iframe', 'layer',  'link',     'meta',    'object',   
22668         'script', 'style' ,'title',  'xml' // clean later..
22669 ];
22670 Roo.HtmlEditorCore.clean = [
22671     'script', 'style', 'title', 'xml'
22672 ];
22673 Roo.HtmlEditorCore.remove = [
22674     'font'
22675 ];
22676 // attributes..
22677
22678 Roo.HtmlEditorCore.ablack = [
22679     'on'
22680 ];
22681     
22682 Roo.HtmlEditorCore.aclean = [ 
22683     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22684 ];
22685
22686 // protocols..
22687 Roo.HtmlEditorCore.pwhite= [
22688         'http',  'https',  'mailto'
22689 ];
22690
22691 // white listed style attributes.
22692 Roo.HtmlEditorCore.cwhite= [
22693       //  'text-align', /// default is to allow most things..
22694       
22695          
22696 //        'font-size'//??
22697 ];
22698
22699 // black listed style attributes.
22700 Roo.HtmlEditorCore.cblack= [
22701       //  'font-size' -- this can be set by the project 
22702 ];
22703
22704
22705 Roo.HtmlEditorCore.swapCodes   =[ 
22706     [    8211, "--" ], 
22707     [    8212, "--" ], 
22708     [    8216,  "'" ],  
22709     [    8217, "'" ],  
22710     [    8220, '"' ],  
22711     [    8221, '"' ],  
22712     [    8226, "*" ],  
22713     [    8230, "..." ]
22714 ]; 
22715
22716     /*
22717  * - LGPL
22718  *
22719  * HtmlEditor
22720  * 
22721  */
22722
22723 /**
22724  * @class Roo.bootstrap.HtmlEditor
22725  * @extends Roo.bootstrap.TextArea
22726  * Bootstrap HtmlEditor class
22727
22728  * @constructor
22729  * Create a new HtmlEditor
22730  * @param {Object} config The config object
22731  */
22732
22733 Roo.bootstrap.HtmlEditor = function(config){
22734     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22735     if (!this.toolbars) {
22736         this.toolbars = [];
22737     }
22738     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22739     this.addEvents({
22740             /**
22741              * @event initialize
22742              * Fires when the editor is fully initialized (including the iframe)
22743              * @param {HtmlEditor} this
22744              */
22745             initialize: true,
22746             /**
22747              * @event activate
22748              * Fires when the editor is first receives the focus. Any insertion must wait
22749              * until after this event.
22750              * @param {HtmlEditor} this
22751              */
22752             activate: true,
22753              /**
22754              * @event beforesync
22755              * Fires before the textarea is updated with content from the editor iframe. Return false
22756              * to cancel the sync.
22757              * @param {HtmlEditor} this
22758              * @param {String} html
22759              */
22760             beforesync: true,
22761              /**
22762              * @event beforepush
22763              * Fires before the iframe editor is updated with content from the textarea. Return false
22764              * to cancel the push.
22765              * @param {HtmlEditor} this
22766              * @param {String} html
22767              */
22768             beforepush: true,
22769              /**
22770              * @event sync
22771              * Fires when the textarea is updated with content from the editor iframe.
22772              * @param {HtmlEditor} this
22773              * @param {String} html
22774              */
22775             sync: true,
22776              /**
22777              * @event push
22778              * Fires when the iframe editor is updated with content from the textarea.
22779              * @param {HtmlEditor} this
22780              * @param {String} html
22781              */
22782             push: true,
22783              /**
22784              * @event editmodechange
22785              * Fires when the editor switches edit modes
22786              * @param {HtmlEditor} this
22787              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22788              */
22789             editmodechange: true,
22790             /**
22791              * @event editorevent
22792              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22793              * @param {HtmlEditor} this
22794              */
22795             editorevent: true,
22796             /**
22797              * @event firstfocus
22798              * Fires when on first focus - needed by toolbars..
22799              * @param {HtmlEditor} this
22800              */
22801             firstfocus: true,
22802             /**
22803              * @event autosave
22804              * Auto save the htmlEditor value as a file into Events
22805              * @param {HtmlEditor} this
22806              */
22807             autosave: true,
22808             /**
22809              * @event savedpreview
22810              * preview the saved version of htmlEditor
22811              * @param {HtmlEditor} this
22812              */
22813             savedpreview: true
22814         });
22815 };
22816
22817
22818 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22819     
22820     
22821       /**
22822      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22823      */
22824     toolbars : false,
22825    
22826      /**
22827      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22828      *                        Roo.resizable.
22829      */
22830     resizable : false,
22831      /**
22832      * @cfg {Number} height (in pixels)
22833      */   
22834     height: 300,
22835    /**
22836      * @cfg {Number} width (in pixels)
22837      */   
22838     width: false,
22839     
22840     /**
22841      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22842      * 
22843      */
22844     stylesheets: false,
22845     
22846     // id of frame..
22847     frameId: false,
22848     
22849     // private properties
22850     validationEvent : false,
22851     deferHeight: true,
22852     initialized : false,
22853     activated : false,
22854     
22855     onFocus : Roo.emptyFn,
22856     iframePad:3,
22857     hideMode:'offsets',
22858     
22859     
22860     tbContainer : false,
22861     
22862     toolbarContainer :function() {
22863         return this.wrap.select('.x-html-editor-tb',true).first();
22864     },
22865
22866     /**
22867      * Protected method that will not generally be called directly. It
22868      * is called when the editor creates its toolbar. Override this method if you need to
22869      * add custom toolbar buttons.
22870      * @param {HtmlEditor} editor
22871      */
22872     createToolbar : function(){
22873         
22874         Roo.log("create toolbars");
22875         
22876         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22877         this.toolbars[0].render(this.toolbarContainer());
22878         
22879         return;
22880         
22881 //        if (!editor.toolbars || !editor.toolbars.length) {
22882 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22883 //        }
22884 //        
22885 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22886 //            editor.toolbars[i] = Roo.factory(
22887 //                    typeof(editor.toolbars[i]) == 'string' ?
22888 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22889 //                Roo.bootstrap.HtmlEditor);
22890 //            editor.toolbars[i].init(editor);
22891 //        }
22892     },
22893
22894      
22895     // private
22896     onRender : function(ct, position)
22897     {
22898        // Roo.log("Call onRender: " + this.xtype);
22899         var _t = this;
22900         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22901       
22902         this.wrap = this.inputEl().wrap({
22903             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22904         });
22905         
22906         this.editorcore.onRender(ct, position);
22907          
22908         if (this.resizable) {
22909             this.resizeEl = new Roo.Resizable(this.wrap, {
22910                 pinned : true,
22911                 wrap: true,
22912                 dynamic : true,
22913                 minHeight : this.height,
22914                 height: this.height,
22915                 handles : this.resizable,
22916                 width: this.width,
22917                 listeners : {
22918                     resize : function(r, w, h) {
22919                         _t.onResize(w,h); // -something
22920                     }
22921                 }
22922             });
22923             
22924         }
22925         this.createToolbar(this);
22926        
22927         
22928         if(!this.width && this.resizable){
22929             this.setSize(this.wrap.getSize());
22930         }
22931         if (this.resizeEl) {
22932             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22933             // should trigger onReize..
22934         }
22935         
22936     },
22937
22938     // private
22939     onResize : function(w, h)
22940     {
22941         Roo.log('resize: ' +w + ',' + h );
22942         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22943         var ew = false;
22944         var eh = false;
22945         
22946         if(this.inputEl() ){
22947             if(typeof w == 'number'){
22948                 var aw = w - this.wrap.getFrameWidth('lr');
22949                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22950                 ew = aw;
22951             }
22952             if(typeof h == 'number'){
22953                  var tbh = -11;  // fixme it needs to tool bar size!
22954                 for (var i =0; i < this.toolbars.length;i++) {
22955                     // fixme - ask toolbars for heights?
22956                     tbh += this.toolbars[i].el.getHeight();
22957                     //if (this.toolbars[i].footer) {
22958                     //    tbh += this.toolbars[i].footer.el.getHeight();
22959                     //}
22960                 }
22961               
22962                 
22963                 
22964                 
22965                 
22966                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22967                 ah -= 5; // knock a few pixes off for look..
22968                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22969                 var eh = ah;
22970             }
22971         }
22972         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22973         this.editorcore.onResize(ew,eh);
22974         
22975     },
22976
22977     /**
22978      * Toggles the editor between standard and source edit mode.
22979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22980      */
22981     toggleSourceEdit : function(sourceEditMode)
22982     {
22983         this.editorcore.toggleSourceEdit(sourceEditMode);
22984         
22985         if(this.editorcore.sourceEditMode){
22986             Roo.log('editor - showing textarea');
22987             
22988 //            Roo.log('in');
22989 //            Roo.log(this.syncValue());
22990             this.syncValue();
22991             this.inputEl().removeClass(['hide', 'x-hidden']);
22992             this.inputEl().dom.removeAttribute('tabIndex');
22993             this.inputEl().focus();
22994         }else{
22995             Roo.log('editor - hiding textarea');
22996 //            Roo.log('out')
22997 //            Roo.log(this.pushValue()); 
22998             this.pushValue();
22999             
23000             this.inputEl().addClass(['hide', 'x-hidden']);
23001             this.inputEl().dom.setAttribute('tabIndex', -1);
23002             //this.deferFocus();
23003         }
23004          
23005         if(this.resizable){
23006             this.setSize(this.wrap.getSize());
23007         }
23008         
23009         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23010     },
23011  
23012     // private (for BoxComponent)
23013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23014
23015     // private (for BoxComponent)
23016     getResizeEl : function(){
23017         return this.wrap;
23018     },
23019
23020     // private (for BoxComponent)
23021     getPositionEl : function(){
23022         return this.wrap;
23023     },
23024
23025     // private
23026     initEvents : function(){
23027         this.originalValue = this.getValue();
23028     },
23029
23030 //    /**
23031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23032 //     * @method
23033 //     */
23034 //    markInvalid : Roo.emptyFn,
23035 //    /**
23036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23037 //     * @method
23038 //     */
23039 //    clearInvalid : Roo.emptyFn,
23040
23041     setValue : function(v){
23042         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23043         this.editorcore.pushValue();
23044     },
23045
23046      
23047     // private
23048     deferFocus : function(){
23049         this.focus.defer(10, this);
23050     },
23051
23052     // doc'ed in Field
23053     focus : function(){
23054         this.editorcore.focus();
23055         
23056     },
23057       
23058
23059     // private
23060     onDestroy : function(){
23061         
23062         
23063         
23064         if(this.rendered){
23065             
23066             for (var i =0; i < this.toolbars.length;i++) {
23067                 // fixme - ask toolbars for heights?
23068                 this.toolbars[i].onDestroy();
23069             }
23070             
23071             this.wrap.dom.innerHTML = '';
23072             this.wrap.remove();
23073         }
23074     },
23075
23076     // private
23077     onFirstFocus : function(){
23078         //Roo.log("onFirstFocus");
23079         this.editorcore.onFirstFocus();
23080          for (var i =0; i < this.toolbars.length;i++) {
23081             this.toolbars[i].onFirstFocus();
23082         }
23083         
23084     },
23085     
23086     // private
23087     syncValue : function()
23088     {   
23089         this.editorcore.syncValue();
23090     },
23091     
23092     pushValue : function()
23093     {   
23094         this.editorcore.pushValue();
23095     }
23096      
23097     
23098     // hide stuff that is not compatible
23099     /**
23100      * @event blur
23101      * @hide
23102      */
23103     /**
23104      * @event change
23105      * @hide
23106      */
23107     /**
23108      * @event focus
23109      * @hide
23110      */
23111     /**
23112      * @event specialkey
23113      * @hide
23114      */
23115     /**
23116      * @cfg {String} fieldClass @hide
23117      */
23118     /**
23119      * @cfg {String} focusClass @hide
23120      */
23121     /**
23122      * @cfg {String} autoCreate @hide
23123      */
23124     /**
23125      * @cfg {String} inputType @hide
23126      */
23127     /**
23128      * @cfg {String} invalidClass @hide
23129      */
23130     /**
23131      * @cfg {String} invalidText @hide
23132      */
23133     /**
23134      * @cfg {String} msgFx @hide
23135      */
23136     /**
23137      * @cfg {String} validateOnBlur @hide
23138      */
23139 });
23140  
23141     
23142    
23143    
23144    
23145       
23146 Roo.namespace('Roo.bootstrap.htmleditor');
23147 /**
23148  * @class Roo.bootstrap.HtmlEditorToolbar1
23149  * Basic Toolbar
23150  * 
23151  * Usage:
23152  *
23153  new Roo.bootstrap.HtmlEditor({
23154     ....
23155     toolbars : [
23156         new Roo.bootstrap.HtmlEditorToolbar1({
23157             disable : { fonts: 1 , format: 1, ..., ... , ...],
23158             btns : [ .... ]
23159         })
23160     }
23161      
23162  * 
23163  * @cfg {Object} disable List of elements to disable..
23164  * @cfg {Array} btns List of additional buttons.
23165  * 
23166  * 
23167  * NEEDS Extra CSS? 
23168  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23169  */
23170  
23171 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23172 {
23173     
23174     Roo.apply(this, config);
23175     
23176     // default disabled, based on 'good practice'..
23177     this.disable = this.disable || {};
23178     Roo.applyIf(this.disable, {
23179         fontSize : true,
23180         colors : true,
23181         specialElements : true
23182     });
23183     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23184     
23185     this.editor = config.editor;
23186     this.editorcore = config.editor.editorcore;
23187     
23188     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23189     
23190     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23191     // dont call parent... till later.
23192 }
23193 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23194      
23195     bar : true,
23196     
23197     editor : false,
23198     editorcore : false,
23199     
23200     
23201     formats : [
23202         "p" ,  
23203         "h1","h2","h3","h4","h5","h6", 
23204         "pre", "code", 
23205         "abbr", "acronym", "address", "cite", "samp", "var",
23206         'div','span'
23207     ],
23208     
23209     onRender : function(ct, position)
23210     {
23211        // Roo.log("Call onRender: " + this.xtype);
23212         
23213        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23214        Roo.log(this.el);
23215        this.el.dom.style.marginBottom = '0';
23216        var _this = this;
23217        var editorcore = this.editorcore;
23218        var editor= this.editor;
23219        
23220        var children = [];
23221        var btn = function(id,cmd , toggle, handler){
23222        
23223             var  event = toggle ? 'toggle' : 'click';
23224        
23225             var a = {
23226                 size : 'sm',
23227                 xtype: 'Button',
23228                 xns: Roo.bootstrap,
23229                 glyphicon : id,
23230                 cmd : id || cmd,
23231                 enableToggle:toggle !== false,
23232                 //html : 'submit'
23233                 pressed : toggle ? false : null,
23234                 listeners : {}
23235             };
23236             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23237                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23238             };
23239             children.push(a);
23240             return a;
23241        }
23242         
23243         var style = {
23244                 xtype: 'Button',
23245                 size : 'sm',
23246                 xns: Roo.bootstrap,
23247                 glyphicon : 'font',
23248                 //html : 'submit'
23249                 menu : {
23250                     xtype: 'Menu',
23251                     xns: Roo.bootstrap,
23252                     items:  []
23253                 }
23254         };
23255         Roo.each(this.formats, function(f) {
23256             style.menu.items.push({
23257                 xtype :'MenuItem',
23258                 xns: Roo.bootstrap,
23259                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23260                 tagname : f,
23261                 listeners : {
23262                     click : function()
23263                     {
23264                         editorcore.insertTag(this.tagname);
23265                         editor.focus();
23266                     }
23267                 }
23268                 
23269             });
23270         });
23271          children.push(style);   
23272             
23273             
23274         btn('bold',false,true);
23275         btn('italic',false,true);
23276         btn('align-left', 'justifyleft',true);
23277         btn('align-center', 'justifycenter',true);
23278         btn('align-right' , 'justifyright',true);
23279         btn('link', false, false, function(btn) {
23280             //Roo.log("create link?");
23281             var url = prompt(this.createLinkText, this.defaultLinkValue);
23282             if(url && url != 'http:/'+'/'){
23283                 this.editorcore.relayCmd('createlink', url);
23284             }
23285         }),
23286         btn('list','insertunorderedlist',true);
23287         btn('pencil', false,true, function(btn){
23288                 Roo.log(this);
23289                 
23290                 this.toggleSourceEdit(btn.pressed);
23291         });
23292         /*
23293         var cog = {
23294                 xtype: 'Button',
23295                 size : 'sm',
23296                 xns: Roo.bootstrap,
23297                 glyphicon : 'cog',
23298                 //html : 'submit'
23299                 menu : {
23300                     xtype: 'Menu',
23301                     xns: Roo.bootstrap,
23302                     items:  []
23303                 }
23304         };
23305         
23306         cog.menu.items.push({
23307             xtype :'MenuItem',
23308             xns: Roo.bootstrap,
23309             html : Clean styles,
23310             tagname : f,
23311             listeners : {
23312                 click : function()
23313                 {
23314                     editorcore.insertTag(this.tagname);
23315                     editor.focus();
23316                 }
23317             }
23318             
23319         });
23320        */
23321         
23322          
23323        this.xtype = 'NavSimplebar';
23324         
23325         for(var i=0;i< children.length;i++) {
23326             
23327             this.buttons.add(this.addxtypeChild(children[i]));
23328             
23329         }
23330         
23331         editor.on('editorevent', this.updateToolbar, this);
23332     },
23333     onBtnClick : function(id)
23334     {
23335        this.editorcore.relayCmd(id);
23336        this.editorcore.focus();
23337     },
23338     
23339     /**
23340      * Protected method that will not generally be called directly. It triggers
23341      * a toolbar update by reading the markup state of the current selection in the editor.
23342      */
23343     updateToolbar: function(){
23344
23345         if(!this.editorcore.activated){
23346             this.editor.onFirstFocus(); // is this neeed?
23347             return;
23348         }
23349
23350         var btns = this.buttons; 
23351         var doc = this.editorcore.doc;
23352         btns.get('bold').setActive(doc.queryCommandState('bold'));
23353         btns.get('italic').setActive(doc.queryCommandState('italic'));
23354         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23355         
23356         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23357         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23358         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23359         
23360         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23361         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23362          /*
23363         
23364         var ans = this.editorcore.getAllAncestors();
23365         if (this.formatCombo) {
23366             
23367             
23368             var store = this.formatCombo.store;
23369             this.formatCombo.setValue("");
23370             for (var i =0; i < ans.length;i++) {
23371                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23372                     // select it..
23373                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23374                     break;
23375                 }
23376             }
23377         }
23378         
23379         
23380         
23381         // hides menus... - so this cant be on a menu...
23382         Roo.bootstrap.MenuMgr.hideAll();
23383         */
23384         Roo.bootstrap.MenuMgr.hideAll();
23385         //this.editorsyncValue();
23386     },
23387     onFirstFocus: function() {
23388         this.buttons.each(function(item){
23389            item.enable();
23390         });
23391     },
23392     toggleSourceEdit : function(sourceEditMode){
23393         
23394           
23395         if(sourceEditMode){
23396             Roo.log("disabling buttons");
23397            this.buttons.each( function(item){
23398                 if(item.cmd != 'pencil'){
23399                     item.disable();
23400                 }
23401             });
23402           
23403         }else{
23404             Roo.log("enabling buttons");
23405             if(this.editorcore.initialized){
23406                 this.buttons.each( function(item){
23407                     item.enable();
23408                 });
23409             }
23410             
23411         }
23412         Roo.log("calling toggole on editor");
23413         // tell the editor that it's been pressed..
23414         this.editor.toggleSourceEdit(sourceEditMode);
23415        
23416     }
23417 });
23418
23419
23420
23421
23422
23423 /**
23424  * @class Roo.bootstrap.Table.AbstractSelectionModel
23425  * @extends Roo.util.Observable
23426  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23427  * implemented by descendant classes.  This class should not be directly instantiated.
23428  * @constructor
23429  */
23430 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23431     this.locked = false;
23432     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23433 };
23434
23435
23436 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23437     /** @ignore Called by the grid automatically. Do not call directly. */
23438     init : function(grid){
23439         this.grid = grid;
23440         this.initEvents();
23441     },
23442
23443     /**
23444      * Locks the selections.
23445      */
23446     lock : function(){
23447         this.locked = true;
23448     },
23449
23450     /**
23451      * Unlocks the selections.
23452      */
23453     unlock : function(){
23454         this.locked = false;
23455     },
23456
23457     /**
23458      * Returns true if the selections are locked.
23459      * @return {Boolean}
23460      */
23461     isLocked : function(){
23462         return this.locked;
23463     }
23464 });
23465 /**
23466  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23467  * @class Roo.bootstrap.Table.RowSelectionModel
23468  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23469  * It supports multiple selections and keyboard selection/navigation. 
23470  * @constructor
23471  * @param {Object} config
23472  */
23473
23474 Roo.bootstrap.Table.RowSelectionModel = function(config){
23475     Roo.apply(this, config);
23476     this.selections = new Roo.util.MixedCollection(false, function(o){
23477         return o.id;
23478     });
23479
23480     this.last = false;
23481     this.lastActive = false;
23482
23483     this.addEvents({
23484         /**
23485              * @event selectionchange
23486              * Fires when the selection changes
23487              * @param {SelectionModel} this
23488              */
23489             "selectionchange" : true,
23490         /**
23491              * @event afterselectionchange
23492              * Fires after the selection changes (eg. by key press or clicking)
23493              * @param {SelectionModel} this
23494              */
23495             "afterselectionchange" : true,
23496         /**
23497              * @event beforerowselect
23498              * Fires when a row is selected being selected, return false to cancel.
23499              * @param {SelectionModel} this
23500              * @param {Number} rowIndex The selected index
23501              * @param {Boolean} keepExisting False if other selections will be cleared
23502              */
23503             "beforerowselect" : true,
23504         /**
23505              * @event rowselect
23506              * Fires when a row is selected.
23507              * @param {SelectionModel} this
23508              * @param {Number} rowIndex The selected index
23509              * @param {Roo.data.Record} r The record
23510              */
23511             "rowselect" : true,
23512         /**
23513              * @event rowdeselect
23514              * Fires when a row is deselected.
23515              * @param {SelectionModel} this
23516              * @param {Number} rowIndex The selected index
23517              */
23518         "rowdeselect" : true
23519     });
23520     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23521     this.locked = false;
23522  };
23523
23524 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23525     /**
23526      * @cfg {Boolean} singleSelect
23527      * True to allow selection of only one row at a time (defaults to false)
23528      */
23529     singleSelect : false,
23530
23531     // private
23532     initEvents : function()
23533     {
23534
23535         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23536         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23537         //}else{ // allow click to work like normal
23538          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23539         //}
23540         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23541         this.grid.on("rowclick", this.handleMouseDown, this);
23542         
23543         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23544             "up" : function(e){
23545                 if(!e.shiftKey){
23546                     this.selectPrevious(e.shiftKey);
23547                 }else if(this.last !== false && this.lastActive !== false){
23548                     var last = this.last;
23549                     this.selectRange(this.last,  this.lastActive-1);
23550                     this.grid.getView().focusRow(this.lastActive);
23551                     if(last !== false){
23552                         this.last = last;
23553                     }
23554                 }else{
23555                     this.selectFirstRow();
23556                 }
23557                 this.fireEvent("afterselectionchange", this);
23558             },
23559             "down" : function(e){
23560                 if(!e.shiftKey){
23561                     this.selectNext(e.shiftKey);
23562                 }else if(this.last !== false && this.lastActive !== false){
23563                     var last = this.last;
23564                     this.selectRange(this.last,  this.lastActive+1);
23565                     this.grid.getView().focusRow(this.lastActive);
23566                     if(last !== false){
23567                         this.last = last;
23568                     }
23569                 }else{
23570                     this.selectFirstRow();
23571                 }
23572                 this.fireEvent("afterselectionchange", this);
23573             },
23574             scope: this
23575         });
23576         this.grid.store.on('load', function(){
23577             this.selections.clear();
23578         },this);
23579         /*
23580         var view = this.grid.view;
23581         view.on("refresh", this.onRefresh, this);
23582         view.on("rowupdated", this.onRowUpdated, this);
23583         view.on("rowremoved", this.onRemove, this);
23584         */
23585     },
23586
23587     // private
23588     onRefresh : function()
23589     {
23590         var ds = this.grid.store, i, v = this.grid.view;
23591         var s = this.selections;
23592         s.each(function(r){
23593             if((i = ds.indexOfId(r.id)) != -1){
23594                 v.onRowSelect(i);
23595             }else{
23596                 s.remove(r);
23597             }
23598         });
23599     },
23600
23601     // private
23602     onRemove : function(v, index, r){
23603         this.selections.remove(r);
23604     },
23605
23606     // private
23607     onRowUpdated : function(v, index, r){
23608         if(this.isSelected(r)){
23609             v.onRowSelect(index);
23610         }
23611     },
23612
23613     /**
23614      * Select records.
23615      * @param {Array} records The records to select
23616      * @param {Boolean} keepExisting (optional) True to keep existing selections
23617      */
23618     selectRecords : function(records, keepExisting)
23619     {
23620         if(!keepExisting){
23621             this.clearSelections();
23622         }
23623             var ds = this.grid.store;
23624         for(var i = 0, len = records.length; i < len; i++){
23625             this.selectRow(ds.indexOf(records[i]), true);
23626         }
23627     },
23628
23629     /**
23630      * Gets the number of selected rows.
23631      * @return {Number}
23632      */
23633     getCount : function(){
23634         return this.selections.length;
23635     },
23636
23637     /**
23638      * Selects the first row in the grid.
23639      */
23640     selectFirstRow : function(){
23641         this.selectRow(0);
23642     },
23643
23644     /**
23645      * Select the last row.
23646      * @param {Boolean} keepExisting (optional) True to keep existing selections
23647      */
23648     selectLastRow : function(keepExisting){
23649         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23650         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23651     },
23652
23653     /**
23654      * Selects the row immediately following the last selected row.
23655      * @param {Boolean} keepExisting (optional) True to keep existing selections
23656      */
23657     selectNext : function(keepExisting)
23658     {
23659             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23660             this.selectRow(this.last+1, keepExisting);
23661             this.grid.getView().focusRow(this.last);
23662         }
23663     },
23664
23665     /**
23666      * Selects the row that precedes the last selected row.
23667      * @param {Boolean} keepExisting (optional) True to keep existing selections
23668      */
23669     selectPrevious : function(keepExisting){
23670         if(this.last){
23671             this.selectRow(this.last-1, keepExisting);
23672             this.grid.getView().focusRow(this.last);
23673         }
23674     },
23675
23676     /**
23677      * Returns the selected records
23678      * @return {Array} Array of selected records
23679      */
23680     getSelections : function(){
23681         return [].concat(this.selections.items);
23682     },
23683
23684     /**
23685      * Returns the first selected record.
23686      * @return {Record}
23687      */
23688     getSelected : function(){
23689         return this.selections.itemAt(0);
23690     },
23691
23692
23693     /**
23694      * Clears all selections.
23695      */
23696     clearSelections : function(fast)
23697     {
23698         if(this.locked) {
23699             return;
23700         }
23701         if(fast !== true){
23702                 var ds = this.grid.store;
23703             var s = this.selections;
23704             s.each(function(r){
23705                 this.deselectRow(ds.indexOfId(r.id));
23706             }, this);
23707             s.clear();
23708         }else{
23709             this.selections.clear();
23710         }
23711         this.last = false;
23712     },
23713
23714
23715     /**
23716      * Selects all rows.
23717      */
23718     selectAll : function(){
23719         if(this.locked) {
23720             return;
23721         }
23722         this.selections.clear();
23723         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23724             this.selectRow(i, true);
23725         }
23726     },
23727
23728     /**
23729      * Returns True if there is a selection.
23730      * @return {Boolean}
23731      */
23732     hasSelection : function(){
23733         return this.selections.length > 0;
23734     },
23735
23736     /**
23737      * Returns True if the specified row is selected.
23738      * @param {Number/Record} record The record or index of the record to check
23739      * @return {Boolean}
23740      */
23741     isSelected : function(index){
23742             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23743         return (r && this.selections.key(r.id) ? true : false);
23744     },
23745
23746     /**
23747      * Returns True if the specified record id is selected.
23748      * @param {String} id The id of record to check
23749      * @return {Boolean}
23750      */
23751     isIdSelected : function(id){
23752         return (this.selections.key(id) ? true : false);
23753     },
23754
23755
23756     // private
23757     handleMouseDBClick : function(e, t){
23758         
23759     },
23760     // private
23761     handleMouseDown : function(e, t)
23762     {
23763             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23764         if(this.isLocked() || rowIndex < 0 ){
23765             return;
23766         };
23767         if(e.shiftKey && this.last !== false){
23768             var last = this.last;
23769             this.selectRange(last, rowIndex, e.ctrlKey);
23770             this.last = last; // reset the last
23771             t.focus();
23772     
23773         }else{
23774             var isSelected = this.isSelected(rowIndex);
23775             //Roo.log("select row:" + rowIndex);
23776             if(isSelected){
23777                 this.deselectRow(rowIndex);
23778             } else {
23779                         this.selectRow(rowIndex, true);
23780             }
23781     
23782             /*
23783                 if(e.button !== 0 && isSelected){
23784                 alert('rowIndex 2: ' + rowIndex);
23785                     view.focusRow(rowIndex);
23786                 }else if(e.ctrlKey && isSelected){
23787                     this.deselectRow(rowIndex);
23788                 }else if(!isSelected){
23789                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23790                     view.focusRow(rowIndex);
23791                 }
23792             */
23793         }
23794         this.fireEvent("afterselectionchange", this);
23795     },
23796     // private
23797     handleDragableRowClick :  function(grid, rowIndex, e) 
23798     {
23799         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23800             this.selectRow(rowIndex, false);
23801             grid.view.focusRow(rowIndex);
23802              this.fireEvent("afterselectionchange", this);
23803         }
23804     },
23805     
23806     /**
23807      * Selects multiple rows.
23808      * @param {Array} rows Array of the indexes of the row to select
23809      * @param {Boolean} keepExisting (optional) True to keep existing selections
23810      */
23811     selectRows : function(rows, keepExisting){
23812         if(!keepExisting){
23813             this.clearSelections();
23814         }
23815         for(var i = 0, len = rows.length; i < len; i++){
23816             this.selectRow(rows[i], true);
23817         }
23818     },
23819
23820     /**
23821      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23822      * @param {Number} startRow The index of the first row in the range
23823      * @param {Number} endRow The index of the last row in the range
23824      * @param {Boolean} keepExisting (optional) True to retain existing selections
23825      */
23826     selectRange : function(startRow, endRow, keepExisting){
23827         if(this.locked) {
23828             return;
23829         }
23830         if(!keepExisting){
23831             this.clearSelections();
23832         }
23833         if(startRow <= endRow){
23834             for(var i = startRow; i <= endRow; i++){
23835                 this.selectRow(i, true);
23836             }
23837         }else{
23838             for(var i = startRow; i >= endRow; i--){
23839                 this.selectRow(i, true);
23840             }
23841         }
23842     },
23843
23844     /**
23845      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23846      * @param {Number} startRow The index of the first row in the range
23847      * @param {Number} endRow The index of the last row in the range
23848      */
23849     deselectRange : function(startRow, endRow, preventViewNotify){
23850         if(this.locked) {
23851             return;
23852         }
23853         for(var i = startRow; i <= endRow; i++){
23854             this.deselectRow(i, preventViewNotify);
23855         }
23856     },
23857
23858     /**
23859      * Selects a row.
23860      * @param {Number} row The index of the row to select
23861      * @param {Boolean} keepExisting (optional) True to keep existing selections
23862      */
23863     selectRow : function(index, keepExisting, preventViewNotify)
23864     {
23865             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23866             return;
23867         }
23868         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23869             if(!keepExisting || this.singleSelect){
23870                 this.clearSelections();
23871             }
23872             
23873             var r = this.grid.store.getAt(index);
23874             //console.log('selectRow - record id :' + r.id);
23875             
23876             this.selections.add(r);
23877             this.last = this.lastActive = index;
23878             if(!preventViewNotify){
23879                 var proxy = new Roo.Element(
23880                                 this.grid.getRowDom(index)
23881                 );
23882                 proxy.addClass('bg-info info');
23883             }
23884             this.fireEvent("rowselect", this, index, r);
23885             this.fireEvent("selectionchange", this);
23886         }
23887     },
23888
23889     /**
23890      * Deselects a row.
23891      * @param {Number} row The index of the row to deselect
23892      */
23893     deselectRow : function(index, preventViewNotify)
23894     {
23895         if(this.locked) {
23896             return;
23897         }
23898         if(this.last == index){
23899             this.last = false;
23900         }
23901         if(this.lastActive == index){
23902             this.lastActive = false;
23903         }
23904         
23905         var r = this.grid.store.getAt(index);
23906         if (!r) {
23907             return;
23908         }
23909         
23910         this.selections.remove(r);
23911         //.console.log('deselectRow - record id :' + r.id);
23912         if(!preventViewNotify){
23913         
23914             var proxy = new Roo.Element(
23915                 this.grid.getRowDom(index)
23916             );
23917             proxy.removeClass('bg-info info');
23918         }
23919         this.fireEvent("rowdeselect", this, index);
23920         this.fireEvent("selectionchange", this);
23921     },
23922
23923     // private
23924     restoreLast : function(){
23925         if(this._last){
23926             this.last = this._last;
23927         }
23928     },
23929
23930     // private
23931     acceptsNav : function(row, col, cm){
23932         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23933     },
23934
23935     // private
23936     onEditorKey : function(field, e){
23937         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23938         if(k == e.TAB){
23939             e.stopEvent();
23940             ed.completeEdit();
23941             if(e.shiftKey){
23942                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23943             }else{
23944                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23945             }
23946         }else if(k == e.ENTER && !e.ctrlKey){
23947             e.stopEvent();
23948             ed.completeEdit();
23949             if(e.shiftKey){
23950                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23951             }else{
23952                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23953             }
23954         }else if(k == e.ESC){
23955             ed.cancelEdit();
23956         }
23957         if(newCell){
23958             g.startEditing(newCell[0], newCell[1]);
23959         }
23960     }
23961 });
23962 /*
23963  * Based on:
23964  * Ext JS Library 1.1.1
23965  * Copyright(c) 2006-2007, Ext JS, LLC.
23966  *
23967  * Originally Released Under LGPL - original licence link has changed is not relivant.
23968  *
23969  * Fork - LGPL
23970  * <script type="text/javascript">
23971  */
23972  
23973 /**
23974  * @class Roo.bootstrap.PagingToolbar
23975  * @extends Roo.bootstrap.NavSimplebar
23976  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23977  * @constructor
23978  * Create a new PagingToolbar
23979  * @param {Object} config The config object
23980  * @param {Roo.data.Store} store
23981  */
23982 Roo.bootstrap.PagingToolbar = function(config)
23983 {
23984     // old args format still supported... - xtype is prefered..
23985         // created from xtype...
23986     
23987     this.ds = config.dataSource;
23988     
23989     if (config.store && !this.ds) {
23990         this.store= Roo.factory(config.store, Roo.data);
23991         this.ds = this.store;
23992         this.ds.xmodule = this.xmodule || false;
23993     }
23994     
23995     this.toolbarItems = [];
23996     if (config.items) {
23997         this.toolbarItems = config.items;
23998     }
23999     
24000     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24001     
24002     this.cursor = 0;
24003     
24004     if (this.ds) { 
24005         this.bind(this.ds);
24006     }
24007     
24008     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24009     
24010 };
24011
24012 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24013     /**
24014      * @cfg {Roo.data.Store} dataSource
24015      * The underlying data store providing the paged data
24016      */
24017     /**
24018      * @cfg {String/HTMLElement/Element} container
24019      * container The id or element that will contain the toolbar
24020      */
24021     /**
24022      * @cfg {Boolean} displayInfo
24023      * True to display the displayMsg (defaults to false)
24024      */
24025     /**
24026      * @cfg {Number} pageSize
24027      * The number of records to display per page (defaults to 20)
24028      */
24029     pageSize: 20,
24030     /**
24031      * @cfg {String} displayMsg
24032      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24033      */
24034     displayMsg : 'Displaying {0} - {1} of {2}',
24035     /**
24036      * @cfg {String} emptyMsg
24037      * The message to display when no records are found (defaults to "No data to display")
24038      */
24039     emptyMsg : 'No data to display',
24040     /**
24041      * Customizable piece of the default paging text (defaults to "Page")
24042      * @type String
24043      */
24044     beforePageText : "Page",
24045     /**
24046      * Customizable piece of the default paging text (defaults to "of %0")
24047      * @type String
24048      */
24049     afterPageText : "of {0}",
24050     /**
24051      * Customizable piece of the default paging text (defaults to "First Page")
24052      * @type String
24053      */
24054     firstText : "First Page",
24055     /**
24056      * Customizable piece of the default paging text (defaults to "Previous Page")
24057      * @type String
24058      */
24059     prevText : "Previous Page",
24060     /**
24061      * Customizable piece of the default paging text (defaults to "Next Page")
24062      * @type String
24063      */
24064     nextText : "Next Page",
24065     /**
24066      * Customizable piece of the default paging text (defaults to "Last Page")
24067      * @type String
24068      */
24069     lastText : "Last Page",
24070     /**
24071      * Customizable piece of the default paging text (defaults to "Refresh")
24072      * @type String
24073      */
24074     refreshText : "Refresh",
24075
24076     buttons : false,
24077     // private
24078     onRender : function(ct, position) 
24079     {
24080         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24081         this.navgroup.parentId = this.id;
24082         this.navgroup.onRender(this.el, null);
24083         // add the buttons to the navgroup
24084         
24085         if(this.displayInfo){
24086             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24087             this.displayEl = this.el.select('.x-paging-info', true).first();
24088 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24089 //            this.displayEl = navel.el.select('span',true).first();
24090         }
24091         
24092         var _this = this;
24093         
24094         if(this.buttons){
24095             Roo.each(_this.buttons, function(e){ // this might need to use render????
24096                Roo.factory(e).onRender(_this.el, null);
24097             });
24098         }
24099             
24100         Roo.each(_this.toolbarItems, function(e) {
24101             _this.navgroup.addItem(e);
24102         });
24103         
24104         
24105         this.first = this.navgroup.addItem({
24106             tooltip: this.firstText,
24107             cls: "prev",
24108             icon : 'fa fa-backward',
24109             disabled: true,
24110             preventDefault: true,
24111             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24112         });
24113         
24114         this.prev =  this.navgroup.addItem({
24115             tooltip: this.prevText,
24116             cls: "prev",
24117             icon : 'fa fa-step-backward',
24118             disabled: true,
24119             preventDefault: true,
24120             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24121         });
24122     //this.addSeparator();
24123         
24124         
24125         var field = this.navgroup.addItem( {
24126             tagtype : 'span',
24127             cls : 'x-paging-position',
24128             
24129             html : this.beforePageText  +
24130                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24131                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24132          } ); //?? escaped?
24133         
24134         this.field = field.el.select('input', true).first();
24135         this.field.on("keydown", this.onPagingKeydown, this);
24136         this.field.on("focus", function(){this.dom.select();});
24137     
24138     
24139         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24140         //this.field.setHeight(18);
24141         //this.addSeparator();
24142         this.next = this.navgroup.addItem({
24143             tooltip: this.nextText,
24144             cls: "next",
24145             html : ' <i class="fa fa-step-forward">',
24146             disabled: true,
24147             preventDefault: true,
24148             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24149         });
24150         this.last = this.navgroup.addItem({
24151             tooltip: this.lastText,
24152             icon : 'fa fa-forward',
24153             cls: "next",
24154             disabled: true,
24155             preventDefault: true,
24156             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24157         });
24158     //this.addSeparator();
24159         this.loading = this.navgroup.addItem({
24160             tooltip: this.refreshText,
24161             icon: 'fa fa-refresh',
24162             preventDefault: true,
24163             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24164         });
24165         
24166     },
24167
24168     // private
24169     updateInfo : function(){
24170         if(this.displayEl){
24171             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24172             var msg = count == 0 ?
24173                 this.emptyMsg :
24174                 String.format(
24175                     this.displayMsg,
24176                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24177                 );
24178             this.displayEl.update(msg);
24179         }
24180     },
24181
24182     // private
24183     onLoad : function(ds, r, o){
24184        this.cursor = o.params ? o.params.start : 0;
24185        var d = this.getPageData(),
24186             ap = d.activePage,
24187             ps = d.pages;
24188         
24189        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24190        this.field.dom.value = ap;
24191        this.first.setDisabled(ap == 1);
24192        this.prev.setDisabled(ap == 1);
24193        this.next.setDisabled(ap == ps);
24194        this.last.setDisabled(ap == ps);
24195        this.loading.enable();
24196        this.updateInfo();
24197     },
24198
24199     // private
24200     getPageData : function(){
24201         var total = this.ds.getTotalCount();
24202         return {
24203             total : total,
24204             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24205             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24206         };
24207     },
24208
24209     // private
24210     onLoadError : function(){
24211         this.loading.enable();
24212     },
24213
24214     // private
24215     onPagingKeydown : function(e){
24216         var k = e.getKey();
24217         var d = this.getPageData();
24218         if(k == e.RETURN){
24219             var v = this.field.dom.value, pageNum;
24220             if(!v || isNaN(pageNum = parseInt(v, 10))){
24221                 this.field.dom.value = d.activePage;
24222                 return;
24223             }
24224             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24225             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24226             e.stopEvent();
24227         }
24228         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))
24229         {
24230           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24231           this.field.dom.value = pageNum;
24232           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24233           e.stopEvent();
24234         }
24235         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24236         {
24237           var v = this.field.dom.value, pageNum; 
24238           var increment = (e.shiftKey) ? 10 : 1;
24239           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24240                 increment *= -1;
24241           }
24242           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24243             this.field.dom.value = d.activePage;
24244             return;
24245           }
24246           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24247           {
24248             this.field.dom.value = parseInt(v, 10) + increment;
24249             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24250             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24251           }
24252           e.stopEvent();
24253         }
24254     },
24255
24256     // private
24257     beforeLoad : function(){
24258         if(this.loading){
24259             this.loading.disable();
24260         }
24261     },
24262
24263     // private
24264     onClick : function(which){
24265         
24266         var ds = this.ds;
24267         if (!ds) {
24268             return;
24269         }
24270         
24271         switch(which){
24272             case "first":
24273                 ds.load({params:{start: 0, limit: this.pageSize}});
24274             break;
24275             case "prev":
24276                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24277             break;
24278             case "next":
24279                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24280             break;
24281             case "last":
24282                 var total = ds.getTotalCount();
24283                 var extra = total % this.pageSize;
24284                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24285                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24286             break;
24287             case "refresh":
24288                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24289             break;
24290         }
24291     },
24292
24293     /**
24294      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24295      * @param {Roo.data.Store} store The data store to unbind
24296      */
24297     unbind : function(ds){
24298         ds.un("beforeload", this.beforeLoad, this);
24299         ds.un("load", this.onLoad, this);
24300         ds.un("loadexception", this.onLoadError, this);
24301         ds.un("remove", this.updateInfo, this);
24302         ds.un("add", this.updateInfo, this);
24303         this.ds = undefined;
24304     },
24305
24306     /**
24307      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24308      * @param {Roo.data.Store} store The data store to bind
24309      */
24310     bind : function(ds){
24311         ds.on("beforeload", this.beforeLoad, this);
24312         ds.on("load", this.onLoad, this);
24313         ds.on("loadexception", this.onLoadError, this);
24314         ds.on("remove", this.updateInfo, this);
24315         ds.on("add", this.updateInfo, this);
24316         this.ds = ds;
24317     }
24318 });/*
24319  * - LGPL
24320  *
24321  * element
24322  * 
24323  */
24324
24325 /**
24326  * @class Roo.bootstrap.MessageBar
24327  * @extends Roo.bootstrap.Component
24328  * Bootstrap MessageBar class
24329  * @cfg {String} html contents of the MessageBar
24330  * @cfg {String} weight (info | success | warning | danger) default info
24331  * @cfg {String} beforeClass insert the bar before the given class
24332  * @cfg {Boolean} closable (true | false) default false
24333  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24334  * 
24335  * @constructor
24336  * Create a new Element
24337  * @param {Object} config The config object
24338  */
24339
24340 Roo.bootstrap.MessageBar = function(config){
24341     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24342 };
24343
24344 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24345     
24346     html: '',
24347     weight: 'info',
24348     closable: false,
24349     fixed: false,
24350     beforeClass: 'bootstrap-sticky-wrap',
24351     
24352     getAutoCreate : function(){
24353         
24354         var cfg = {
24355             tag: 'div',
24356             cls: 'alert alert-dismissable alert-' + this.weight,
24357             cn: [
24358                 {
24359                     tag: 'span',
24360                     cls: 'message',
24361                     html: this.html || ''
24362                 }
24363             ]
24364         };
24365         
24366         if(this.fixed){
24367             cfg.cls += ' alert-messages-fixed';
24368         }
24369         
24370         if(this.closable){
24371             cfg.cn.push({
24372                 tag: 'button',
24373                 cls: 'close',
24374                 html: 'x'
24375             });
24376         }
24377         
24378         return cfg;
24379     },
24380     
24381     onRender : function(ct, position)
24382     {
24383         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24384         
24385         if(!this.el){
24386             var cfg = Roo.apply({},  this.getAutoCreate());
24387             cfg.id = Roo.id();
24388             
24389             if (this.cls) {
24390                 cfg.cls += ' ' + this.cls;
24391             }
24392             if (this.style) {
24393                 cfg.style = this.style;
24394             }
24395             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24396             
24397             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24398         }
24399         
24400         this.el.select('>button.close').on('click', this.hide, this);
24401         
24402     },
24403     
24404     show : function()
24405     {
24406         if (!this.rendered) {
24407             this.render();
24408         }
24409         
24410         this.el.show();
24411         
24412         this.fireEvent('show', this);
24413         
24414     },
24415     
24416     hide : function()
24417     {
24418         if (!this.rendered) {
24419             this.render();
24420         }
24421         
24422         this.el.hide();
24423         
24424         this.fireEvent('hide', this);
24425     },
24426     
24427     update : function()
24428     {
24429 //        var e = this.el.dom.firstChild;
24430 //        
24431 //        if(this.closable){
24432 //            e = e.nextSibling;
24433 //        }
24434 //        
24435 //        e.data = this.html || '';
24436
24437         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24438     }
24439    
24440 });
24441
24442  
24443
24444      /*
24445  * - LGPL
24446  *
24447  * Graph
24448  * 
24449  */
24450
24451
24452 /**
24453  * @class Roo.bootstrap.Graph
24454  * @extends Roo.bootstrap.Component
24455  * Bootstrap Graph class
24456 > Prameters
24457  -sm {number} sm 4
24458  -md {number} md 5
24459  @cfg {String} graphtype  bar | vbar | pie
24460  @cfg {number} g_x coodinator | centre x (pie)
24461  @cfg {number} g_y coodinator | centre y (pie)
24462  @cfg {number} g_r radius (pie)
24463  @cfg {number} g_height height of the chart (respected by all elements in the set)
24464  @cfg {number} g_width width of the chart (respected by all elements in the set)
24465  @cfg {Object} title The title of the chart
24466     
24467  -{Array}  values
24468  -opts (object) options for the chart 
24469      o {
24470      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24471      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24472      o vgutter (number)
24473      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.
24474      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24475      o to
24476      o stretch (boolean)
24477      o }
24478  -opts (object) options for the pie
24479      o{
24480      o cut
24481      o startAngle (number)
24482      o endAngle (number)
24483      } 
24484  *
24485  * @constructor
24486  * Create a new Input
24487  * @param {Object} config The config object
24488  */
24489
24490 Roo.bootstrap.Graph = function(config){
24491     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24492     
24493     this.addEvents({
24494         // img events
24495         /**
24496          * @event click
24497          * The img click event for the img.
24498          * @param {Roo.EventObject} e
24499          */
24500         "click" : true
24501     });
24502 };
24503
24504 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24505     
24506     sm: 4,
24507     md: 5,
24508     graphtype: 'bar',
24509     g_height: 250,
24510     g_width: 400,
24511     g_x: 50,
24512     g_y: 50,
24513     g_r: 30,
24514     opts:{
24515         //g_colors: this.colors,
24516         g_type: 'soft',
24517         g_gutter: '20%'
24518
24519     },
24520     title : false,
24521
24522     getAutoCreate : function(){
24523         
24524         var cfg = {
24525             tag: 'div',
24526             html : null
24527         };
24528         
24529         
24530         return  cfg;
24531     },
24532
24533     onRender : function(ct,position){
24534         
24535         
24536         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24537         
24538         if (typeof(Raphael) == 'undefined') {
24539             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24540             return;
24541         }
24542         
24543         this.raphael = Raphael(this.el.dom);
24544         
24545                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24546                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24547                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24548                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24549                 /*
24550                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24551                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24552                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24553                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24554                 
24555                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24556                 r.barchart(330, 10, 300, 220, data1);
24557                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24558                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24559                 */
24560                 
24561                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24562                 // r.barchart(30, 30, 560, 250,  xdata, {
24563                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24564                 //     axis : "0 0 1 1",
24565                 //     axisxlabels :  xdata
24566                 //     //yvalues : cols,
24567                    
24568                 // });
24569 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24570 //        
24571 //        this.load(null,xdata,{
24572 //                axis : "0 0 1 1",
24573 //                axisxlabels :  xdata
24574 //                });
24575
24576     },
24577
24578     load : function(graphtype,xdata,opts)
24579     {
24580         this.raphael.clear();
24581         if(!graphtype) {
24582             graphtype = this.graphtype;
24583         }
24584         if(!opts){
24585             opts = this.opts;
24586         }
24587         var r = this.raphael,
24588             fin = function () {
24589                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24590             },
24591             fout = function () {
24592                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24593             },
24594             pfin = function() {
24595                 this.sector.stop();
24596                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24597
24598                 if (this.label) {
24599                     this.label[0].stop();
24600                     this.label[0].attr({ r: 7.5 });
24601                     this.label[1].attr({ "font-weight": 800 });
24602                 }
24603             },
24604             pfout = function() {
24605                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24606
24607                 if (this.label) {
24608                     this.label[0].animate({ r: 5 }, 500, "bounce");
24609                     this.label[1].attr({ "font-weight": 400 });
24610                 }
24611             };
24612
24613         switch(graphtype){
24614             case 'bar':
24615                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24616                 break;
24617             case 'hbar':
24618                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24619                 break;
24620             case 'pie':
24621 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24622 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24623 //            
24624                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24625                 
24626                 break;
24627
24628         }
24629         
24630         if(this.title){
24631             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24632         }
24633         
24634     },
24635     
24636     setTitle: function(o)
24637     {
24638         this.title = o;
24639     },
24640     
24641     initEvents: function() {
24642         
24643         if(!this.href){
24644             this.el.on('click', this.onClick, this);
24645         }
24646     },
24647     
24648     onClick : function(e)
24649     {
24650         Roo.log('img onclick');
24651         this.fireEvent('click', this, e);
24652     }
24653    
24654 });
24655
24656  
24657 /*
24658  * - LGPL
24659  *
24660  * numberBox
24661  * 
24662  */
24663 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24664
24665 /**
24666  * @class Roo.bootstrap.dash.NumberBox
24667  * @extends Roo.bootstrap.Component
24668  * Bootstrap NumberBox class
24669  * @cfg {String} headline Box headline
24670  * @cfg {String} content Box content
24671  * @cfg {String} icon Box icon
24672  * @cfg {String} footer Footer text
24673  * @cfg {String} fhref Footer href
24674  * 
24675  * @constructor
24676  * Create a new NumberBox
24677  * @param {Object} config The config object
24678  */
24679
24680
24681 Roo.bootstrap.dash.NumberBox = function(config){
24682     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24683     
24684 };
24685
24686 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24687     
24688     headline : '',
24689     content : '',
24690     icon : '',
24691     footer : '',
24692     fhref : '',
24693     ficon : '',
24694     
24695     getAutoCreate : function(){
24696         
24697         var cfg = {
24698             tag : 'div',
24699             cls : 'small-box ',
24700             cn : [
24701                 {
24702                     tag : 'div',
24703                     cls : 'inner',
24704                     cn :[
24705                         {
24706                             tag : 'h3',
24707                             cls : 'roo-headline',
24708                             html : this.headline
24709                         },
24710                         {
24711                             tag : 'p',
24712                             cls : 'roo-content',
24713                             html : this.content
24714                         }
24715                     ]
24716                 }
24717             ]
24718         };
24719         
24720         if(this.icon){
24721             cfg.cn.push({
24722                 tag : 'div',
24723                 cls : 'icon',
24724                 cn :[
24725                     {
24726                         tag : 'i',
24727                         cls : 'ion ' + this.icon
24728                     }
24729                 ]
24730             });
24731         }
24732         
24733         if(this.footer){
24734             var footer = {
24735                 tag : 'a',
24736                 cls : 'small-box-footer',
24737                 href : this.fhref || '#',
24738                 html : this.footer
24739             };
24740             
24741             cfg.cn.push(footer);
24742             
24743         }
24744         
24745         return  cfg;
24746     },
24747
24748     onRender : function(ct,position){
24749         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24750
24751
24752        
24753                 
24754     },
24755
24756     setHeadline: function (value)
24757     {
24758         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24759     },
24760     
24761     setFooter: function (value, href)
24762     {
24763         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24764         
24765         if(href){
24766             this.el.select('a.small-box-footer',true).first().attr('href', href);
24767         }
24768         
24769     },
24770
24771     setContent: function (value)
24772     {
24773         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24774     },
24775
24776     initEvents: function() 
24777     {   
24778         
24779     }
24780     
24781 });
24782
24783  
24784 /*
24785  * - LGPL
24786  *
24787  * TabBox
24788  * 
24789  */
24790 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24791
24792 /**
24793  * @class Roo.bootstrap.dash.TabBox
24794  * @extends Roo.bootstrap.Component
24795  * Bootstrap TabBox class
24796  * @cfg {String} title Title of the TabBox
24797  * @cfg {String} icon Icon of the TabBox
24798  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24799  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24800  * 
24801  * @constructor
24802  * Create a new TabBox
24803  * @param {Object} config The config object
24804  */
24805
24806
24807 Roo.bootstrap.dash.TabBox = function(config){
24808     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24809     this.addEvents({
24810         // raw events
24811         /**
24812          * @event addpane
24813          * When a pane is added
24814          * @param {Roo.bootstrap.dash.TabPane} pane
24815          */
24816         "addpane" : true,
24817         /**
24818          * @event activatepane
24819          * When a pane is activated
24820          * @param {Roo.bootstrap.dash.TabPane} pane
24821          */
24822         "activatepane" : true
24823         
24824          
24825     });
24826     
24827     this.panes = [];
24828 };
24829
24830 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24831
24832     title : '',
24833     icon : false,
24834     showtabs : true,
24835     tabScrollable : false,
24836     
24837     getChildContainer : function()
24838     {
24839         return this.el.select('.tab-content', true).first();
24840     },
24841     
24842     getAutoCreate : function(){
24843         
24844         var header = {
24845             tag: 'li',
24846             cls: 'pull-left header',
24847             html: this.title,
24848             cn : []
24849         };
24850         
24851         if(this.icon){
24852             header.cn.push({
24853                 tag: 'i',
24854                 cls: 'fa ' + this.icon
24855             });
24856         }
24857         
24858         var h = {
24859             tag: 'ul',
24860             cls: 'nav nav-tabs pull-right',
24861             cn: [
24862                 header
24863             ]
24864         };
24865         
24866         if(this.tabScrollable){
24867             h = {
24868                 tag: 'div',
24869                 cls: 'tab-header',
24870                 cn: [
24871                     {
24872                         tag: 'ul',
24873                         cls: 'nav nav-tabs pull-right',
24874                         cn: [
24875                             header
24876                         ]
24877                     }
24878                 ]
24879             };
24880         }
24881         
24882         var cfg = {
24883             tag: 'div',
24884             cls: 'nav-tabs-custom',
24885             cn: [
24886                 h,
24887                 {
24888                     tag: 'div',
24889                     cls: 'tab-content no-padding',
24890                     cn: []
24891                 }
24892             ]
24893         };
24894
24895         return  cfg;
24896     },
24897     initEvents : function()
24898     {
24899         //Roo.log('add add pane handler');
24900         this.on('addpane', this.onAddPane, this);
24901     },
24902      /**
24903      * Updates the box title
24904      * @param {String} html to set the title to.
24905      */
24906     setTitle : function(value)
24907     {
24908         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24909     },
24910     onAddPane : function(pane)
24911     {
24912         this.panes.push(pane);
24913         //Roo.log('addpane');
24914         //Roo.log(pane);
24915         // tabs are rendere left to right..
24916         if(!this.showtabs){
24917             return;
24918         }
24919         
24920         var ctr = this.el.select('.nav-tabs', true).first();
24921          
24922          
24923         var existing = ctr.select('.nav-tab',true);
24924         var qty = existing.getCount();;
24925         
24926         
24927         var tab = ctr.createChild({
24928             tag : 'li',
24929             cls : 'nav-tab' + (qty ? '' : ' active'),
24930             cn : [
24931                 {
24932                     tag : 'a',
24933                     href:'#',
24934                     html : pane.title
24935                 }
24936             ]
24937         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24938         pane.tab = tab;
24939         
24940         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24941         if (!qty) {
24942             pane.el.addClass('active');
24943         }
24944         
24945                 
24946     },
24947     onTabClick : function(ev,un,ob,pane)
24948     {
24949         //Roo.log('tab - prev default');
24950         ev.preventDefault();
24951         
24952         
24953         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24954         pane.tab.addClass('active');
24955         //Roo.log(pane.title);
24956         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24957         // technically we should have a deactivate event.. but maybe add later.
24958         // and it should not de-activate the selected tab...
24959         this.fireEvent('activatepane', pane);
24960         pane.el.addClass('active');
24961         pane.fireEvent('activate');
24962         
24963         
24964     },
24965     
24966     getActivePane : function()
24967     {
24968         var r = false;
24969         Roo.each(this.panes, function(p) {
24970             if(p.el.hasClass('active')){
24971                 r = p;
24972                 return false;
24973             }
24974             
24975             return;
24976         });
24977         
24978         return r;
24979     }
24980     
24981     
24982 });
24983
24984  
24985 /*
24986  * - LGPL
24987  *
24988  * Tab pane
24989  * 
24990  */
24991 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24992 /**
24993  * @class Roo.bootstrap.TabPane
24994  * @extends Roo.bootstrap.Component
24995  * Bootstrap TabPane class
24996  * @cfg {Boolean} active (false | true) Default false
24997  * @cfg {String} title title of panel
24998
24999  * 
25000  * @constructor
25001  * Create a new TabPane
25002  * @param {Object} config The config object
25003  */
25004
25005 Roo.bootstrap.dash.TabPane = function(config){
25006     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25007     
25008     this.addEvents({
25009         // raw events
25010         /**
25011          * @event activate
25012          * When a pane is activated
25013          * @param {Roo.bootstrap.dash.TabPane} pane
25014          */
25015         "activate" : true
25016          
25017     });
25018 };
25019
25020 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25021     
25022     active : false,
25023     title : '',
25024     
25025     // the tabBox that this is attached to.
25026     tab : false,
25027      
25028     getAutoCreate : function() 
25029     {
25030         var cfg = {
25031             tag: 'div',
25032             cls: 'tab-pane'
25033         };
25034         
25035         if(this.active){
25036             cfg.cls += ' active';
25037         }
25038         
25039         return cfg;
25040     },
25041     initEvents  : function()
25042     {
25043         //Roo.log('trigger add pane handler');
25044         this.parent().fireEvent('addpane', this)
25045     },
25046     
25047      /**
25048      * Updates the tab title 
25049      * @param {String} html to set the title to.
25050      */
25051     setTitle: function(str)
25052     {
25053         if (!this.tab) {
25054             return;
25055         }
25056         this.title = str;
25057         this.tab.select('a', true).first().dom.innerHTML = str;
25058         
25059     }
25060     
25061     
25062     
25063 });
25064
25065  
25066
25067
25068  /*
25069  * - LGPL
25070  *
25071  * menu
25072  * 
25073  */
25074 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25075
25076 /**
25077  * @class Roo.bootstrap.menu.Menu
25078  * @extends Roo.bootstrap.Component
25079  * Bootstrap Menu class - container for Menu
25080  * @cfg {String} html Text of the menu
25081  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25082  * @cfg {String} icon Font awesome icon
25083  * @cfg {String} pos Menu align to (top | bottom) default bottom
25084  * 
25085  * 
25086  * @constructor
25087  * Create a new Menu
25088  * @param {Object} config The config object
25089  */
25090
25091
25092 Roo.bootstrap.menu.Menu = function(config){
25093     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25094     
25095     this.addEvents({
25096         /**
25097          * @event beforeshow
25098          * Fires before this menu is displayed
25099          * @param {Roo.bootstrap.menu.Menu} this
25100          */
25101         beforeshow : true,
25102         /**
25103          * @event beforehide
25104          * Fires before this menu is hidden
25105          * @param {Roo.bootstrap.menu.Menu} this
25106          */
25107         beforehide : true,
25108         /**
25109          * @event show
25110          * Fires after this menu is displayed
25111          * @param {Roo.bootstrap.menu.Menu} this
25112          */
25113         show : true,
25114         /**
25115          * @event hide
25116          * Fires after this menu is hidden
25117          * @param {Roo.bootstrap.menu.Menu} this
25118          */
25119         hide : true,
25120         /**
25121          * @event click
25122          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25123          * @param {Roo.bootstrap.menu.Menu} this
25124          * @param {Roo.EventObject} e
25125          */
25126         click : true
25127     });
25128     
25129 };
25130
25131 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25132     
25133     submenu : false,
25134     html : '',
25135     weight : 'default',
25136     icon : false,
25137     pos : 'bottom',
25138     
25139     
25140     getChildContainer : function() {
25141         if(this.isSubMenu){
25142             return this.el;
25143         }
25144         
25145         return this.el.select('ul.dropdown-menu', true).first();  
25146     },
25147     
25148     getAutoCreate : function()
25149     {
25150         var text = [
25151             {
25152                 tag : 'span',
25153                 cls : 'roo-menu-text',
25154                 html : this.html
25155             }
25156         ];
25157         
25158         if(this.icon){
25159             text.unshift({
25160                 tag : 'i',
25161                 cls : 'fa ' + this.icon
25162             })
25163         }
25164         
25165         
25166         var cfg = {
25167             tag : 'div',
25168             cls : 'btn-group',
25169             cn : [
25170                 {
25171                     tag : 'button',
25172                     cls : 'dropdown-button btn btn-' + this.weight,
25173                     cn : text
25174                 },
25175                 {
25176                     tag : 'button',
25177                     cls : 'dropdown-toggle btn btn-' + this.weight,
25178                     cn : [
25179                         {
25180                             tag : 'span',
25181                             cls : 'caret'
25182                         }
25183                     ]
25184                 },
25185                 {
25186                     tag : 'ul',
25187                     cls : 'dropdown-menu'
25188                 }
25189             ]
25190             
25191         };
25192         
25193         if(this.pos == 'top'){
25194             cfg.cls += ' dropup';
25195         }
25196         
25197         if(this.isSubMenu){
25198             cfg = {
25199                 tag : 'ul',
25200                 cls : 'dropdown-menu'
25201             }
25202         }
25203         
25204         return cfg;
25205     },
25206     
25207     onRender : function(ct, position)
25208     {
25209         this.isSubMenu = ct.hasClass('dropdown-submenu');
25210         
25211         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25212     },
25213     
25214     initEvents : function() 
25215     {
25216         if(this.isSubMenu){
25217             return;
25218         }
25219         
25220         this.hidden = true;
25221         
25222         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25223         this.triggerEl.on('click', this.onTriggerPress, this);
25224         
25225         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25226         this.buttonEl.on('click', this.onClick, this);
25227         
25228     },
25229     
25230     list : function()
25231     {
25232         if(this.isSubMenu){
25233             return this.el;
25234         }
25235         
25236         return this.el.select('ul.dropdown-menu', true).first();
25237     },
25238     
25239     onClick : function(e)
25240     {
25241         this.fireEvent("click", this, e);
25242     },
25243     
25244     onTriggerPress  : function(e)
25245     {   
25246         if (this.isVisible()) {
25247             this.hide();
25248         } else {
25249             this.show();
25250         }
25251     },
25252     
25253     isVisible : function(){
25254         return !this.hidden;
25255     },
25256     
25257     show : function()
25258     {
25259         this.fireEvent("beforeshow", this);
25260         
25261         this.hidden = false;
25262         this.el.addClass('open');
25263         
25264         Roo.get(document).on("mouseup", this.onMouseUp, this);
25265         
25266         this.fireEvent("show", this);
25267         
25268         
25269     },
25270     
25271     hide : function()
25272     {
25273         this.fireEvent("beforehide", this);
25274         
25275         this.hidden = true;
25276         this.el.removeClass('open');
25277         
25278         Roo.get(document).un("mouseup", this.onMouseUp);
25279         
25280         this.fireEvent("hide", this);
25281     },
25282     
25283     onMouseUp : function()
25284     {
25285         this.hide();
25286     }
25287     
25288 });
25289
25290  
25291  /*
25292  * - LGPL
25293  *
25294  * menu item
25295  * 
25296  */
25297 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25298
25299 /**
25300  * @class Roo.bootstrap.menu.Item
25301  * @extends Roo.bootstrap.Component
25302  * Bootstrap MenuItem class
25303  * @cfg {Boolean} submenu (true | false) default false
25304  * @cfg {String} html text of the item
25305  * @cfg {String} href the link
25306  * @cfg {Boolean} disable (true | false) default false
25307  * @cfg {Boolean} preventDefault (true | false) default true
25308  * @cfg {String} icon Font awesome icon
25309  * @cfg {String} pos Submenu align to (left | right) default right 
25310  * 
25311  * 
25312  * @constructor
25313  * Create a new Item
25314  * @param {Object} config The config object
25315  */
25316
25317
25318 Roo.bootstrap.menu.Item = function(config){
25319     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25320     this.addEvents({
25321         /**
25322          * @event mouseover
25323          * Fires when the mouse is hovering over this menu
25324          * @param {Roo.bootstrap.menu.Item} this
25325          * @param {Roo.EventObject} e
25326          */
25327         mouseover : true,
25328         /**
25329          * @event mouseout
25330          * Fires when the mouse exits this menu
25331          * @param {Roo.bootstrap.menu.Item} this
25332          * @param {Roo.EventObject} e
25333          */
25334         mouseout : true,
25335         // raw events
25336         /**
25337          * @event click
25338          * The raw click event for the entire grid.
25339          * @param {Roo.EventObject} e
25340          */
25341         click : true
25342     });
25343 };
25344
25345 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25346     
25347     submenu : false,
25348     href : '',
25349     html : '',
25350     preventDefault: true,
25351     disable : false,
25352     icon : false,
25353     pos : 'right',
25354     
25355     getAutoCreate : function()
25356     {
25357         var text = [
25358             {
25359                 tag : 'span',
25360                 cls : 'roo-menu-item-text',
25361                 html : this.html
25362             }
25363         ];
25364         
25365         if(this.icon){
25366             text.unshift({
25367                 tag : 'i',
25368                 cls : 'fa ' + this.icon
25369             })
25370         }
25371         
25372         var cfg = {
25373             tag : 'li',
25374             cn : [
25375                 {
25376                     tag : 'a',
25377                     href : this.href || '#',
25378                     cn : text
25379                 }
25380             ]
25381         };
25382         
25383         if(this.disable){
25384             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25385         }
25386         
25387         if(this.submenu){
25388             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25389             
25390             if(this.pos == 'left'){
25391                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25392             }
25393         }
25394         
25395         return cfg;
25396     },
25397     
25398     initEvents : function() 
25399     {
25400         this.el.on('mouseover', this.onMouseOver, this);
25401         this.el.on('mouseout', this.onMouseOut, this);
25402         
25403         this.el.select('a', true).first().on('click', this.onClick, this);
25404         
25405     },
25406     
25407     onClick : function(e)
25408     {
25409         if(this.preventDefault){
25410             e.preventDefault();
25411         }
25412         
25413         this.fireEvent("click", this, e);
25414     },
25415     
25416     onMouseOver : function(e)
25417     {
25418         if(this.submenu && this.pos == 'left'){
25419             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25420         }
25421         
25422         this.fireEvent("mouseover", this, e);
25423     },
25424     
25425     onMouseOut : function(e)
25426     {
25427         this.fireEvent("mouseout", this, e);
25428     }
25429 });
25430
25431  
25432
25433  /*
25434  * - LGPL
25435  *
25436  * menu separator
25437  * 
25438  */
25439 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25440
25441 /**
25442  * @class Roo.bootstrap.menu.Separator
25443  * @extends Roo.bootstrap.Component
25444  * Bootstrap Separator class
25445  * 
25446  * @constructor
25447  * Create a new Separator
25448  * @param {Object} config The config object
25449  */
25450
25451
25452 Roo.bootstrap.menu.Separator = function(config){
25453     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25454 };
25455
25456 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25457     
25458     getAutoCreate : function(){
25459         var cfg = {
25460             tag : 'li',
25461             cls: 'divider'
25462         };
25463         
25464         return cfg;
25465     }
25466    
25467 });
25468
25469  
25470
25471  /*
25472  * - LGPL
25473  *
25474  * Tooltip
25475  * 
25476  */
25477
25478 /**
25479  * @class Roo.bootstrap.Tooltip
25480  * Bootstrap Tooltip class
25481  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25482  * to determine which dom element triggers the tooltip.
25483  * 
25484  * It needs to add support for additional attributes like tooltip-position
25485  * 
25486  * @constructor
25487  * Create a new Toolti
25488  * @param {Object} config The config object
25489  */
25490
25491 Roo.bootstrap.Tooltip = function(config){
25492     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25493     
25494     this.alignment = Roo.bootstrap.Tooltip.alignment;
25495     
25496     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25497         this.alignment = config.alignment;
25498     }
25499     
25500 };
25501
25502 Roo.apply(Roo.bootstrap.Tooltip, {
25503     /**
25504      * @function init initialize tooltip monitoring.
25505      * @static
25506      */
25507     currentEl : false,
25508     currentTip : false,
25509     currentRegion : false,
25510     
25511     //  init : delay?
25512     
25513     init : function()
25514     {
25515         Roo.get(document).on('mouseover', this.enter ,this);
25516         Roo.get(document).on('mouseout', this.leave, this);
25517          
25518         
25519         this.currentTip = new Roo.bootstrap.Tooltip();
25520     },
25521     
25522     enter : function(ev)
25523     {
25524         var dom = ev.getTarget();
25525         
25526         //Roo.log(['enter',dom]);
25527         var el = Roo.fly(dom);
25528         if (this.currentEl) {
25529             //Roo.log(dom);
25530             //Roo.log(this.currentEl);
25531             //Roo.log(this.currentEl.contains(dom));
25532             if (this.currentEl == el) {
25533                 return;
25534             }
25535             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25536                 return;
25537             }
25538
25539         }
25540         
25541         if (this.currentTip.el) {
25542             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25543         }    
25544         //Roo.log(ev);
25545         
25546         if(!el || el.dom == document){
25547             return;
25548         }
25549         
25550         var bindEl = el;
25551         
25552         // you can not look for children, as if el is the body.. then everythign is the child..
25553         if (!el.attr('tooltip')) { //
25554             if (!el.select("[tooltip]").elements.length) {
25555                 return;
25556             }
25557             // is the mouse over this child...?
25558             bindEl = el.select("[tooltip]").first();
25559             var xy = ev.getXY();
25560             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25561                 //Roo.log("not in region.");
25562                 return;
25563             }
25564             //Roo.log("child element over..");
25565             
25566         }
25567         this.currentEl = bindEl;
25568         this.currentTip.bind(bindEl);
25569         this.currentRegion = Roo.lib.Region.getRegion(dom);
25570         this.currentTip.enter();
25571         
25572     },
25573     leave : function(ev)
25574     {
25575         var dom = ev.getTarget();
25576         //Roo.log(['leave',dom]);
25577         if (!this.currentEl) {
25578             return;
25579         }
25580         
25581         
25582         if (dom != this.currentEl.dom) {
25583             return;
25584         }
25585         var xy = ev.getXY();
25586         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25587             return;
25588         }
25589         // only activate leave if mouse cursor is outside... bounding box..
25590         
25591         
25592         
25593         
25594         if (this.currentTip) {
25595             this.currentTip.leave();
25596         }
25597         //Roo.log('clear currentEl');
25598         this.currentEl = false;
25599         
25600         
25601     },
25602     alignment : {
25603         'left' : ['r-l', [-2,0], 'right'],
25604         'right' : ['l-r', [2,0], 'left'],
25605         'bottom' : ['t-b', [0,2], 'top'],
25606         'top' : [ 'b-t', [0,-2], 'bottom']
25607     }
25608     
25609 });
25610
25611
25612 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25613     
25614     
25615     bindEl : false,
25616     
25617     delay : null, // can be { show : 300 , hide: 500}
25618     
25619     timeout : null,
25620     
25621     hoverState : null, //???
25622     
25623     placement : 'bottom', 
25624     
25625     alignment : false,
25626     
25627     getAutoCreate : function(){
25628     
25629         var cfg = {
25630            cls : 'tooltip',
25631            role : 'tooltip',
25632            cn : [
25633                 {
25634                     cls : 'tooltip-arrow'
25635                 },
25636                 {
25637                     cls : 'tooltip-inner'
25638                 }
25639            ]
25640         };
25641         
25642         return cfg;
25643     },
25644     bind : function(el)
25645     {
25646         this.bindEl = el;
25647     },
25648       
25649     
25650     enter : function () {
25651        
25652         if (this.timeout != null) {
25653             clearTimeout(this.timeout);
25654         }
25655         
25656         this.hoverState = 'in';
25657          //Roo.log("enter - show");
25658         if (!this.delay || !this.delay.show) {
25659             this.show();
25660             return;
25661         }
25662         var _t = this;
25663         this.timeout = setTimeout(function () {
25664             if (_t.hoverState == 'in') {
25665                 _t.show();
25666             }
25667         }, this.delay.show);
25668     },
25669     leave : function()
25670     {
25671         clearTimeout(this.timeout);
25672     
25673         this.hoverState = 'out';
25674          if (!this.delay || !this.delay.hide) {
25675             this.hide();
25676             return;
25677         }
25678        
25679         var _t = this;
25680         this.timeout = setTimeout(function () {
25681             //Roo.log("leave - timeout");
25682             
25683             if (_t.hoverState == 'out') {
25684                 _t.hide();
25685                 Roo.bootstrap.Tooltip.currentEl = false;
25686             }
25687         }, delay);
25688     },
25689     
25690     show : function (msg)
25691     {
25692         if (!this.el) {
25693             this.render(document.body);
25694         }
25695         // set content.
25696         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25697         
25698         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25699         
25700         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25701         
25702         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25703         
25704         var placement = typeof this.placement == 'function' ?
25705             this.placement.call(this, this.el, on_el) :
25706             this.placement;
25707             
25708         var autoToken = /\s?auto?\s?/i;
25709         var autoPlace = autoToken.test(placement);
25710         if (autoPlace) {
25711             placement = placement.replace(autoToken, '') || 'top';
25712         }
25713         
25714         //this.el.detach()
25715         //this.el.setXY([0,0]);
25716         this.el.show();
25717         //this.el.dom.style.display='block';
25718         
25719         //this.el.appendTo(on_el);
25720         
25721         var p = this.getPosition();
25722         var box = this.el.getBox();
25723         
25724         if (autoPlace) {
25725             // fixme..
25726         }
25727         
25728         var align = this.alignment[placement];
25729         
25730         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25731         
25732         if(placement == 'top' || placement == 'bottom'){
25733             if(xy[0] < 0){
25734                 placement = 'right';
25735             }
25736             
25737             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25738                 placement = 'left';
25739             }
25740             
25741             var scroll = Roo.select('body', true).first().getScroll();
25742             
25743             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25744                 placement = 'top';
25745             }
25746             
25747         }
25748         
25749         this.el.alignTo(this.bindEl, align[0],align[1]);
25750         //var arrow = this.el.select('.arrow',true).first();
25751         //arrow.set(align[2], 
25752         
25753         this.el.addClass(placement);
25754         
25755         this.el.addClass('in fade');
25756         
25757         this.hoverState = null;
25758         
25759         if (this.el.hasClass('fade')) {
25760             // fade it?
25761         }
25762         
25763     },
25764     hide : function()
25765     {
25766          
25767         if (!this.el) {
25768             return;
25769         }
25770         //this.el.setXY([0,0]);
25771         this.el.removeClass('in');
25772         //this.el.hide();
25773         
25774     }
25775     
25776 });
25777  
25778
25779  /*
25780  * - LGPL
25781  *
25782  * Location Picker
25783  * 
25784  */
25785
25786 /**
25787  * @class Roo.bootstrap.LocationPicker
25788  * @extends Roo.bootstrap.Component
25789  * Bootstrap LocationPicker class
25790  * @cfg {Number} latitude Position when init default 0
25791  * @cfg {Number} longitude Position when init default 0
25792  * @cfg {Number} zoom default 15
25793  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25794  * @cfg {Boolean} mapTypeControl default false
25795  * @cfg {Boolean} disableDoubleClickZoom default false
25796  * @cfg {Boolean} scrollwheel default true
25797  * @cfg {Boolean} streetViewControl default false
25798  * @cfg {Number} radius default 0
25799  * @cfg {String} locationName
25800  * @cfg {Boolean} draggable default true
25801  * @cfg {Boolean} enableAutocomplete default false
25802  * @cfg {Boolean} enableReverseGeocode default true
25803  * @cfg {String} markerTitle
25804  * 
25805  * @constructor
25806  * Create a new LocationPicker
25807  * @param {Object} config The config object
25808  */
25809
25810
25811 Roo.bootstrap.LocationPicker = function(config){
25812     
25813     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25814     
25815     this.addEvents({
25816         /**
25817          * @event initial
25818          * Fires when the picker initialized.
25819          * @param {Roo.bootstrap.LocationPicker} this
25820          * @param {Google Location} location
25821          */
25822         initial : true,
25823         /**
25824          * @event positionchanged
25825          * Fires when the picker position changed.
25826          * @param {Roo.bootstrap.LocationPicker} this
25827          * @param {Google Location} location
25828          */
25829         positionchanged : true,
25830         /**
25831          * @event resize
25832          * Fires when the map resize.
25833          * @param {Roo.bootstrap.LocationPicker} this
25834          */
25835         resize : true,
25836         /**
25837          * @event show
25838          * Fires when the map show.
25839          * @param {Roo.bootstrap.LocationPicker} this
25840          */
25841         show : true,
25842         /**
25843          * @event hide
25844          * Fires when the map hide.
25845          * @param {Roo.bootstrap.LocationPicker} this
25846          */
25847         hide : true,
25848         /**
25849          * @event mapClick
25850          * Fires when click the map.
25851          * @param {Roo.bootstrap.LocationPicker} this
25852          * @param {Map event} e
25853          */
25854         mapClick : true,
25855         /**
25856          * @event mapRightClick
25857          * Fires when right click the map.
25858          * @param {Roo.bootstrap.LocationPicker} this
25859          * @param {Map event} e
25860          */
25861         mapRightClick : true,
25862         /**
25863          * @event markerClick
25864          * Fires when click the marker.
25865          * @param {Roo.bootstrap.LocationPicker} this
25866          * @param {Map event} e
25867          */
25868         markerClick : true,
25869         /**
25870          * @event markerRightClick
25871          * Fires when right click the marker.
25872          * @param {Roo.bootstrap.LocationPicker} this
25873          * @param {Map event} e
25874          */
25875         markerRightClick : true,
25876         /**
25877          * @event OverlayViewDraw
25878          * Fires when OverlayView Draw
25879          * @param {Roo.bootstrap.LocationPicker} this
25880          */
25881         OverlayViewDraw : true,
25882         /**
25883          * @event OverlayViewOnAdd
25884          * Fires when OverlayView Draw
25885          * @param {Roo.bootstrap.LocationPicker} this
25886          */
25887         OverlayViewOnAdd : true,
25888         /**
25889          * @event OverlayViewOnRemove
25890          * Fires when OverlayView Draw
25891          * @param {Roo.bootstrap.LocationPicker} this
25892          */
25893         OverlayViewOnRemove : true,
25894         /**
25895          * @event OverlayViewShow
25896          * Fires when OverlayView Draw
25897          * @param {Roo.bootstrap.LocationPicker} this
25898          * @param {Pixel} cpx
25899          */
25900         OverlayViewShow : true,
25901         /**
25902          * @event OverlayViewHide
25903          * Fires when OverlayView Draw
25904          * @param {Roo.bootstrap.LocationPicker} this
25905          */
25906         OverlayViewHide : true,
25907         /**
25908          * @event loadexception
25909          * Fires when load google lib failed.
25910          * @param {Roo.bootstrap.LocationPicker} this
25911          */
25912         loadexception : true
25913     });
25914         
25915 };
25916
25917 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25918     
25919     gMapContext: false,
25920     
25921     latitude: 0,
25922     longitude: 0,
25923     zoom: 15,
25924     mapTypeId: false,
25925     mapTypeControl: false,
25926     disableDoubleClickZoom: false,
25927     scrollwheel: true,
25928     streetViewControl: false,
25929     radius: 0,
25930     locationName: '',
25931     draggable: true,
25932     enableAutocomplete: false,
25933     enableReverseGeocode: true,
25934     markerTitle: '',
25935     
25936     getAutoCreate: function()
25937     {
25938
25939         var cfg = {
25940             tag: 'div',
25941             cls: 'roo-location-picker'
25942         };
25943         
25944         return cfg
25945     },
25946     
25947     initEvents: function(ct, position)
25948     {       
25949         if(!this.el.getWidth() || this.isApplied()){
25950             return;
25951         }
25952         
25953         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25954         
25955         this.initial();
25956     },
25957     
25958     initial: function()
25959     {
25960         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25961             this.fireEvent('loadexception', this);
25962             return;
25963         }
25964         
25965         if(!this.mapTypeId){
25966             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25967         }
25968         
25969         this.gMapContext = this.GMapContext();
25970         
25971         this.initOverlayView();
25972         
25973         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25974         
25975         var _this = this;
25976                 
25977         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25978             _this.setPosition(_this.gMapContext.marker.position);
25979         });
25980         
25981         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25982             _this.fireEvent('mapClick', this, event);
25983             
25984         });
25985
25986         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25987             _this.fireEvent('mapRightClick', this, event);
25988             
25989         });
25990         
25991         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25992             _this.fireEvent('markerClick', this, event);
25993             
25994         });
25995
25996         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25997             _this.fireEvent('markerRightClick', this, event);
25998             
25999         });
26000         
26001         this.setPosition(this.gMapContext.location);
26002         
26003         this.fireEvent('initial', this, this.gMapContext.location);
26004     },
26005     
26006     initOverlayView: function()
26007     {
26008         var _this = this;
26009         
26010         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26011             
26012             draw: function()
26013             {
26014                 _this.fireEvent('OverlayViewDraw', _this);
26015             },
26016             
26017             onAdd: function()
26018             {
26019                 _this.fireEvent('OverlayViewOnAdd', _this);
26020             },
26021             
26022             onRemove: function()
26023             {
26024                 _this.fireEvent('OverlayViewOnRemove', _this);
26025             },
26026             
26027             show: function(cpx)
26028             {
26029                 _this.fireEvent('OverlayViewShow', _this, cpx);
26030             },
26031             
26032             hide: function()
26033             {
26034                 _this.fireEvent('OverlayViewHide', _this);
26035             }
26036             
26037         });
26038     },
26039     
26040     fromLatLngToContainerPixel: function(event)
26041     {
26042         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26043     },
26044     
26045     isApplied: function() 
26046     {
26047         return this.getGmapContext() == false ? false : true;
26048     },
26049     
26050     getGmapContext: function() 
26051     {
26052         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26053     },
26054     
26055     GMapContext: function() 
26056     {
26057         var position = new google.maps.LatLng(this.latitude, this.longitude);
26058         
26059         var _map = new google.maps.Map(this.el.dom, {
26060             center: position,
26061             zoom: this.zoom,
26062             mapTypeId: this.mapTypeId,
26063             mapTypeControl: this.mapTypeControl,
26064             disableDoubleClickZoom: this.disableDoubleClickZoom,
26065             scrollwheel: this.scrollwheel,
26066             streetViewControl: this.streetViewControl,
26067             locationName: this.locationName,
26068             draggable: this.draggable,
26069             enableAutocomplete: this.enableAutocomplete,
26070             enableReverseGeocode: this.enableReverseGeocode
26071         });
26072         
26073         var _marker = new google.maps.Marker({
26074             position: position,
26075             map: _map,
26076             title: this.markerTitle,
26077             draggable: this.draggable
26078         });
26079         
26080         return {
26081             map: _map,
26082             marker: _marker,
26083             circle: null,
26084             location: position,
26085             radius: this.radius,
26086             locationName: this.locationName,
26087             addressComponents: {
26088                 formatted_address: null,
26089                 addressLine1: null,
26090                 addressLine2: null,
26091                 streetName: null,
26092                 streetNumber: null,
26093                 city: null,
26094                 district: null,
26095                 state: null,
26096                 stateOrProvince: null
26097             },
26098             settings: this,
26099             domContainer: this.el.dom,
26100             geodecoder: new google.maps.Geocoder()
26101         };
26102     },
26103     
26104     drawCircle: function(center, radius, options) 
26105     {
26106         if (this.gMapContext.circle != null) {
26107             this.gMapContext.circle.setMap(null);
26108         }
26109         if (radius > 0) {
26110             radius *= 1;
26111             options = Roo.apply({}, options, {
26112                 strokeColor: "#0000FF",
26113                 strokeOpacity: .35,
26114                 strokeWeight: 2,
26115                 fillColor: "#0000FF",
26116                 fillOpacity: .2
26117             });
26118             
26119             options.map = this.gMapContext.map;
26120             options.radius = radius;
26121             options.center = center;
26122             this.gMapContext.circle = new google.maps.Circle(options);
26123             return this.gMapContext.circle;
26124         }
26125         
26126         return null;
26127     },
26128     
26129     setPosition: function(location) 
26130     {
26131         this.gMapContext.location = location;
26132         this.gMapContext.marker.setPosition(location);
26133         this.gMapContext.map.panTo(location);
26134         this.drawCircle(location, this.gMapContext.radius, {});
26135         
26136         var _this = this;
26137         
26138         if (this.gMapContext.settings.enableReverseGeocode) {
26139             this.gMapContext.geodecoder.geocode({
26140                 latLng: this.gMapContext.location
26141             }, function(results, status) {
26142                 
26143                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26144                     _this.gMapContext.locationName = results[0].formatted_address;
26145                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26146                     
26147                     _this.fireEvent('positionchanged', this, location);
26148                 }
26149             });
26150             
26151             return;
26152         }
26153         
26154         this.fireEvent('positionchanged', this, location);
26155     },
26156     
26157     resize: function()
26158     {
26159         google.maps.event.trigger(this.gMapContext.map, "resize");
26160         
26161         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26162         
26163         this.fireEvent('resize', this);
26164     },
26165     
26166     setPositionByLatLng: function(latitude, longitude)
26167     {
26168         this.setPosition(new google.maps.LatLng(latitude, longitude));
26169     },
26170     
26171     getCurrentPosition: function() 
26172     {
26173         return {
26174             latitude: this.gMapContext.location.lat(),
26175             longitude: this.gMapContext.location.lng()
26176         };
26177     },
26178     
26179     getAddressName: function() 
26180     {
26181         return this.gMapContext.locationName;
26182     },
26183     
26184     getAddressComponents: function() 
26185     {
26186         return this.gMapContext.addressComponents;
26187     },
26188     
26189     address_component_from_google_geocode: function(address_components) 
26190     {
26191         var result = {};
26192         
26193         for (var i = 0; i < address_components.length; i++) {
26194             var component = address_components[i];
26195             if (component.types.indexOf("postal_code") >= 0) {
26196                 result.postalCode = component.short_name;
26197             } else if (component.types.indexOf("street_number") >= 0) {
26198                 result.streetNumber = component.short_name;
26199             } else if (component.types.indexOf("route") >= 0) {
26200                 result.streetName = component.short_name;
26201             } else if (component.types.indexOf("neighborhood") >= 0) {
26202                 result.city = component.short_name;
26203             } else if (component.types.indexOf("locality") >= 0) {
26204                 result.city = component.short_name;
26205             } else if (component.types.indexOf("sublocality") >= 0) {
26206                 result.district = component.short_name;
26207             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26208                 result.stateOrProvince = component.short_name;
26209             } else if (component.types.indexOf("country") >= 0) {
26210                 result.country = component.short_name;
26211             }
26212         }
26213         
26214         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26215         result.addressLine2 = "";
26216         return result;
26217     },
26218     
26219     setZoomLevel: function(zoom)
26220     {
26221         this.gMapContext.map.setZoom(zoom);
26222     },
26223     
26224     show: function()
26225     {
26226         if(!this.el){
26227             return;
26228         }
26229         
26230         this.el.show();
26231         
26232         this.resize();
26233         
26234         this.fireEvent('show', this);
26235     },
26236     
26237     hide: function()
26238     {
26239         if(!this.el){
26240             return;
26241         }
26242         
26243         this.el.hide();
26244         
26245         this.fireEvent('hide', this);
26246     }
26247     
26248 });
26249
26250 Roo.apply(Roo.bootstrap.LocationPicker, {
26251     
26252     OverlayView : function(map, options)
26253     {
26254         options = options || {};
26255         
26256         this.setMap(map);
26257     }
26258     
26259     
26260 });/*
26261  * - LGPL
26262  *
26263  * Alert
26264  * 
26265  */
26266
26267 /**
26268  * @class Roo.bootstrap.Alert
26269  * @extends Roo.bootstrap.Component
26270  * Bootstrap Alert class
26271  * @cfg {String} title The title of alert
26272  * @cfg {String} html The content of alert
26273  * @cfg {String} weight (  success | info | warning | danger )
26274  * @cfg {String} faicon font-awesomeicon
26275  * 
26276  * @constructor
26277  * Create a new alert
26278  * @param {Object} config The config object
26279  */
26280
26281
26282 Roo.bootstrap.Alert = function(config){
26283     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26284     
26285 };
26286
26287 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26288     
26289     title: '',
26290     html: '',
26291     weight: false,
26292     faicon: false,
26293     
26294     getAutoCreate : function()
26295     {
26296         
26297         var cfg = {
26298             tag : 'div',
26299             cls : 'alert',
26300             cn : [
26301                 {
26302                     tag : 'i',
26303                     cls : 'roo-alert-icon'
26304                     
26305                 },
26306                 {
26307                     tag : 'b',
26308                     cls : 'roo-alert-title',
26309                     html : this.title
26310                 },
26311                 {
26312                     tag : 'span',
26313                     cls : 'roo-alert-text',
26314                     html : this.html
26315                 }
26316             ]
26317         };
26318         
26319         if(this.faicon){
26320             cfg.cn[0].cls += ' fa ' + this.faicon;
26321         }
26322         
26323         if(this.weight){
26324             cfg.cls += ' alert-' + this.weight;
26325         }
26326         
26327         return cfg;
26328     },
26329     
26330     initEvents: function() 
26331     {
26332         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26333     },
26334     
26335     setTitle : function(str)
26336     {
26337         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26338     },
26339     
26340     setText : function(str)
26341     {
26342         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26343     },
26344     
26345     setWeight : function(weight)
26346     {
26347         if(this.weight){
26348             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26349         }
26350         
26351         this.weight = weight;
26352         
26353         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26354     },
26355     
26356     setIcon : function(icon)
26357     {
26358         if(this.faicon){
26359             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26360         }
26361         
26362         this.faicon = icon;
26363         
26364         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26365     },
26366     
26367     hide: function() 
26368     {
26369         this.el.hide();   
26370     },
26371     
26372     show: function() 
26373     {  
26374         this.el.show();   
26375     }
26376     
26377 });
26378
26379  
26380 /*
26381 * Licence: LGPL
26382 */
26383
26384 /**
26385  * @class Roo.bootstrap.UploadCropbox
26386  * @extends Roo.bootstrap.Component
26387  * Bootstrap UploadCropbox class
26388  * @cfg {String} emptyText show when image has been loaded
26389  * @cfg {String} rotateNotify show when image too small to rotate
26390  * @cfg {Number} errorTimeout default 3000
26391  * @cfg {Number} minWidth default 300
26392  * @cfg {Number} minHeight default 300
26393  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26394  * @cfg {Boolean} isDocument (true|false) default false
26395  * @cfg {String} url action url
26396  * @cfg {String} paramName default 'imageUpload'
26397  * @cfg {String} method default POST
26398  * @cfg {Boolean} loadMask (true|false) default true
26399  * @cfg {Boolean} loadingText default 'Loading...'
26400  * 
26401  * @constructor
26402  * Create a new UploadCropbox
26403  * @param {Object} config The config object
26404  */
26405
26406 Roo.bootstrap.UploadCropbox = function(config){
26407     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26408     
26409     this.addEvents({
26410         /**
26411          * @event beforeselectfile
26412          * Fire before select file
26413          * @param {Roo.bootstrap.UploadCropbox} this
26414          */
26415         "beforeselectfile" : true,
26416         /**
26417          * @event initial
26418          * Fire after initEvent
26419          * @param {Roo.bootstrap.UploadCropbox} this
26420          */
26421         "initial" : true,
26422         /**
26423          * @event crop
26424          * Fire after initEvent
26425          * @param {Roo.bootstrap.UploadCropbox} this
26426          * @param {String} data
26427          */
26428         "crop" : true,
26429         /**
26430          * @event prepare
26431          * Fire when preparing the file data
26432          * @param {Roo.bootstrap.UploadCropbox} this
26433          * @param {Object} file
26434          */
26435         "prepare" : true,
26436         /**
26437          * @event exception
26438          * Fire when get exception
26439          * @param {Roo.bootstrap.UploadCropbox} this
26440          * @param {XMLHttpRequest} xhr
26441          */
26442         "exception" : true,
26443         /**
26444          * @event beforeloadcanvas
26445          * Fire before load the canvas
26446          * @param {Roo.bootstrap.UploadCropbox} this
26447          * @param {String} src
26448          */
26449         "beforeloadcanvas" : true,
26450         /**
26451          * @event trash
26452          * Fire when trash image
26453          * @param {Roo.bootstrap.UploadCropbox} this
26454          */
26455         "trash" : true,
26456         /**
26457          * @event download
26458          * Fire when download the image
26459          * @param {Roo.bootstrap.UploadCropbox} this
26460          */
26461         "download" : true,
26462         /**
26463          * @event footerbuttonclick
26464          * Fire when footerbuttonclick
26465          * @param {Roo.bootstrap.UploadCropbox} this
26466          * @param {String} type
26467          */
26468         "footerbuttonclick" : true,
26469         /**
26470          * @event resize
26471          * Fire when resize
26472          * @param {Roo.bootstrap.UploadCropbox} this
26473          */
26474         "resize" : true,
26475         /**
26476          * @event rotate
26477          * Fire when rotate the image
26478          * @param {Roo.bootstrap.UploadCropbox} this
26479          * @param {String} pos
26480          */
26481         "rotate" : true,
26482         /**
26483          * @event inspect
26484          * Fire when inspect the file
26485          * @param {Roo.bootstrap.UploadCropbox} this
26486          * @param {Object} file
26487          */
26488         "inspect" : true,
26489         /**
26490          * @event upload
26491          * Fire when xhr upload the file
26492          * @param {Roo.bootstrap.UploadCropbox} this
26493          * @param {Object} data
26494          */
26495         "upload" : true,
26496         /**
26497          * @event arrange
26498          * Fire when arrange the file data
26499          * @param {Roo.bootstrap.UploadCropbox} this
26500          * @param {Object} formData
26501          */
26502         "arrange" : true
26503     });
26504     
26505     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26506 };
26507
26508 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26509     
26510     emptyText : 'Click to upload image',
26511     rotateNotify : 'Image is too small to rotate',
26512     errorTimeout : 3000,
26513     scale : 0,
26514     baseScale : 1,
26515     rotate : 0,
26516     dragable : false,
26517     pinching : false,
26518     mouseX : 0,
26519     mouseY : 0,
26520     cropData : false,
26521     minWidth : 300,
26522     minHeight : 300,
26523     file : false,
26524     exif : {},
26525     baseRotate : 1,
26526     cropType : 'image/jpeg',
26527     buttons : false,
26528     canvasLoaded : false,
26529     isDocument : false,
26530     method : 'POST',
26531     paramName : 'imageUpload',
26532     loadMask : true,
26533     loadingText : 'Loading...',
26534     maskEl : false,
26535     
26536     getAutoCreate : function()
26537     {
26538         var cfg = {
26539             tag : 'div',
26540             cls : 'roo-upload-cropbox',
26541             cn : [
26542                 {
26543                     tag : 'input',
26544                     cls : 'roo-upload-cropbox-selector',
26545                     type : 'file'
26546                 },
26547                 {
26548                     tag : 'div',
26549                     cls : 'roo-upload-cropbox-body',
26550                     style : 'cursor:pointer',
26551                     cn : [
26552                         {
26553                             tag : 'div',
26554                             cls : 'roo-upload-cropbox-preview'
26555                         },
26556                         {
26557                             tag : 'div',
26558                             cls : 'roo-upload-cropbox-thumb'
26559                         },
26560                         {
26561                             tag : 'div',
26562                             cls : 'roo-upload-cropbox-empty-notify',
26563                             html : this.emptyText
26564                         },
26565                         {
26566                             tag : 'div',
26567                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26568                             html : this.rotateNotify
26569                         }
26570                     ]
26571                 },
26572                 {
26573                     tag : 'div',
26574                     cls : 'roo-upload-cropbox-footer',
26575                     cn : {
26576                         tag : 'div',
26577                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26578                         cn : []
26579                     }
26580                 }
26581             ]
26582         };
26583         
26584         return cfg;
26585     },
26586     
26587     onRender : function(ct, position)
26588     {
26589         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26590         
26591         if (this.buttons.length) {
26592             
26593             Roo.each(this.buttons, function(bb) {
26594                 
26595                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26596                 
26597                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26598                 
26599             }, this);
26600         }
26601         
26602         if(this.loadMask){
26603             this.maskEl = this.el;
26604         }
26605     },
26606     
26607     initEvents : function()
26608     {
26609         this.urlAPI = (window.createObjectURL && window) || 
26610                                 (window.URL && URL.revokeObjectURL && URL) || 
26611                                 (window.webkitURL && webkitURL);
26612                         
26613         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26614         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26615         
26616         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26617         this.selectorEl.hide();
26618         
26619         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26620         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26621         
26622         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26623         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26624         this.thumbEl.hide();
26625         
26626         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26627         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26628         
26629         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26630         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26631         this.errorEl.hide();
26632         
26633         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26634         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26635         this.footerEl.hide();
26636         
26637         this.setThumbBoxSize();
26638         
26639         this.bind();
26640         
26641         this.resize();
26642         
26643         this.fireEvent('initial', this);
26644     },
26645
26646     bind : function()
26647     {
26648         var _this = this;
26649         
26650         window.addEventListener("resize", function() { _this.resize(); } );
26651         
26652         this.bodyEl.on('click', this.beforeSelectFile, this);
26653         
26654         if(Roo.isTouch){
26655             this.bodyEl.on('touchstart', this.onTouchStart, this);
26656             this.bodyEl.on('touchmove', this.onTouchMove, this);
26657             this.bodyEl.on('touchend', this.onTouchEnd, this);
26658         }
26659         
26660         if(!Roo.isTouch){
26661             this.bodyEl.on('mousedown', this.onMouseDown, this);
26662             this.bodyEl.on('mousemove', this.onMouseMove, this);
26663             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26664             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26665             Roo.get(document).on('mouseup', this.onMouseUp, this);
26666         }
26667         
26668         this.selectorEl.on('change', this.onFileSelected, this);
26669     },
26670     
26671     reset : function()
26672     {    
26673         this.scale = 0;
26674         this.baseScale = 1;
26675         this.rotate = 0;
26676         this.baseRotate = 1;
26677         this.dragable = false;
26678         this.pinching = false;
26679         this.mouseX = 0;
26680         this.mouseY = 0;
26681         this.cropData = false;
26682         this.notifyEl.dom.innerHTML = this.emptyText;
26683         
26684         this.selectorEl.dom.value = '';
26685         
26686     },
26687     
26688     resize : function()
26689     {
26690         if(this.fireEvent('resize', this) != false){
26691             this.setThumbBoxPosition();
26692             this.setCanvasPosition();
26693         }
26694     },
26695     
26696     onFooterButtonClick : function(e, el, o, type)
26697     {
26698         switch (type) {
26699             case 'rotate-left' :
26700                 this.onRotateLeft(e);
26701                 break;
26702             case 'rotate-right' :
26703                 this.onRotateRight(e);
26704                 break;
26705             case 'picture' :
26706                 this.beforeSelectFile(e);
26707                 break;
26708             case 'trash' :
26709                 this.trash(e);
26710                 break;
26711             case 'crop' :
26712                 this.crop(e);
26713                 break;
26714             case 'download' :
26715                 this.download(e);
26716                 break;
26717             default :
26718                 break;
26719         }
26720         
26721         this.fireEvent('footerbuttonclick', this, type);
26722     },
26723     
26724     beforeSelectFile : function(e)
26725     {
26726         e.preventDefault();
26727         
26728         if(this.fireEvent('beforeselectfile', this) != false){
26729             this.selectorEl.dom.click();
26730         }
26731     },
26732     
26733     onFileSelected : function(e)
26734     {
26735         e.preventDefault();
26736         
26737         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26738             return;
26739         }
26740         
26741         var file = this.selectorEl.dom.files[0];
26742         
26743         if(this.fireEvent('inspect', this, file) != false){
26744             this.prepare(file);
26745         }
26746         
26747     },
26748     
26749     trash : function(e)
26750     {
26751         this.fireEvent('trash', this);
26752     },
26753     
26754     download : function(e)
26755     {
26756         this.fireEvent('download', this);
26757     },
26758     
26759     loadCanvas : function(src)
26760     {   
26761         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26762             
26763             this.reset();
26764             
26765             this.imageEl = document.createElement('img');
26766             
26767             var _this = this;
26768             
26769             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26770             
26771             this.imageEl.src = src;
26772         }
26773     },
26774     
26775     onLoadCanvas : function()
26776     {   
26777         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26778         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26779         
26780         this.bodyEl.un('click', this.beforeSelectFile, this);
26781         
26782         this.notifyEl.hide();
26783         this.thumbEl.show();
26784         this.footerEl.show();
26785         
26786         this.baseRotateLevel();
26787         
26788         if(this.isDocument){
26789             this.setThumbBoxSize();
26790         }
26791         
26792         this.setThumbBoxPosition();
26793         
26794         this.baseScaleLevel();
26795         
26796         this.draw();
26797         
26798         this.resize();
26799         
26800         this.canvasLoaded = true;
26801         
26802         if(this.loadMask){
26803             this.maskEl.unmask();
26804         }
26805         
26806     },
26807     
26808     setCanvasPosition : function()
26809     {   
26810         if(!this.canvasEl){
26811             return;
26812         }
26813         
26814         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26815         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26816         
26817         this.previewEl.setLeft(pw);
26818         this.previewEl.setTop(ph);
26819         
26820     },
26821     
26822     onMouseDown : function(e)
26823     {   
26824         e.stopEvent();
26825         
26826         this.dragable = true;
26827         this.pinching = false;
26828         
26829         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26830             this.dragable = false;
26831             return;
26832         }
26833         
26834         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26835         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26836         
26837     },
26838     
26839     onMouseMove : function(e)
26840     {   
26841         e.stopEvent();
26842         
26843         if(!this.canvasLoaded){
26844             return;
26845         }
26846         
26847         if (!this.dragable){
26848             return;
26849         }
26850         
26851         var minX = Math.ceil(this.thumbEl.getLeft(true));
26852         var minY = Math.ceil(this.thumbEl.getTop(true));
26853         
26854         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26855         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26856         
26857         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26858         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26859         
26860         x = x - this.mouseX;
26861         y = y - this.mouseY;
26862         
26863         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26864         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26865         
26866         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26867         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26868         
26869         this.previewEl.setLeft(bgX);
26870         this.previewEl.setTop(bgY);
26871         
26872         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26873         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26874     },
26875     
26876     onMouseUp : function(e)
26877     {   
26878         e.stopEvent();
26879         
26880         this.dragable = false;
26881     },
26882     
26883     onMouseWheel : function(e)
26884     {   
26885         e.stopEvent();
26886         
26887         this.startScale = this.scale;
26888         
26889         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26890         
26891         if(!this.zoomable()){
26892             this.scale = this.startScale;
26893             return;
26894         }
26895         
26896         this.draw();
26897         
26898         return;
26899     },
26900     
26901     zoomable : function()
26902     {
26903         var minScale = this.thumbEl.getWidth() / this.minWidth;
26904         
26905         if(this.minWidth < this.minHeight){
26906             minScale = this.thumbEl.getHeight() / this.minHeight;
26907         }
26908         
26909         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26910         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26911         
26912         if(
26913                 this.isDocument &&
26914                 (this.rotate == 0 || this.rotate == 180) && 
26915                 (
26916                     width > this.imageEl.OriginWidth || 
26917                     height > this.imageEl.OriginHeight ||
26918                     (width < this.minWidth && height < this.minHeight)
26919                 )
26920         ){
26921             return false;
26922         }
26923         
26924         if(
26925                 this.isDocument &&
26926                 (this.rotate == 90 || this.rotate == 270) && 
26927                 (
26928                     width > this.imageEl.OriginWidth || 
26929                     height > this.imageEl.OriginHeight ||
26930                     (width < this.minHeight && height < this.minWidth)
26931                 )
26932         ){
26933             return false;
26934         }
26935         
26936         if(
26937                 !this.isDocument &&
26938                 (this.rotate == 0 || this.rotate == 180) && 
26939                 (
26940                     width < this.minWidth || 
26941                     width > this.imageEl.OriginWidth || 
26942                     height < this.minHeight || 
26943                     height > this.imageEl.OriginHeight
26944                 )
26945         ){
26946             return false;
26947         }
26948         
26949         if(
26950                 !this.isDocument &&
26951                 (this.rotate == 90 || this.rotate == 270) && 
26952                 (
26953                     width < this.minHeight || 
26954                     width > this.imageEl.OriginWidth || 
26955                     height < this.minWidth || 
26956                     height > this.imageEl.OriginHeight
26957                 )
26958         ){
26959             return false;
26960         }
26961         
26962         return true;
26963         
26964     },
26965     
26966     onRotateLeft : function(e)
26967     {   
26968         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26969             
26970             var minScale = this.thumbEl.getWidth() / this.minWidth;
26971             
26972             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26973             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26974             
26975             this.startScale = this.scale;
26976             
26977             while (this.getScaleLevel() < minScale){
26978             
26979                 this.scale = this.scale + 1;
26980                 
26981                 if(!this.zoomable()){
26982                     break;
26983                 }
26984                 
26985                 if(
26986                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26987                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26988                 ){
26989                     continue;
26990                 }
26991                 
26992                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26993
26994                 this.draw();
26995                 
26996                 return;
26997             }
26998             
26999             this.scale = this.startScale;
27000             
27001             this.onRotateFail();
27002             
27003             return false;
27004         }
27005         
27006         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27007
27008         if(this.isDocument){
27009             this.setThumbBoxSize();
27010             this.setThumbBoxPosition();
27011             this.setCanvasPosition();
27012         }
27013         
27014         this.draw();
27015         
27016         this.fireEvent('rotate', this, 'left');
27017         
27018     },
27019     
27020     onRotateRight : function(e)
27021     {
27022         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27023             
27024             var minScale = this.thumbEl.getWidth() / this.minWidth;
27025         
27026             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27027             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27028             
27029             this.startScale = this.scale;
27030             
27031             while (this.getScaleLevel() < minScale){
27032             
27033                 this.scale = this.scale + 1;
27034                 
27035                 if(!this.zoomable()){
27036                     break;
27037                 }
27038                 
27039                 if(
27040                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27041                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27042                 ){
27043                     continue;
27044                 }
27045                 
27046                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27047
27048                 this.draw();
27049                 
27050                 return;
27051             }
27052             
27053             this.scale = this.startScale;
27054             
27055             this.onRotateFail();
27056             
27057             return false;
27058         }
27059         
27060         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27061
27062         if(this.isDocument){
27063             this.setThumbBoxSize();
27064             this.setThumbBoxPosition();
27065             this.setCanvasPosition();
27066         }
27067         
27068         this.draw();
27069         
27070         this.fireEvent('rotate', this, 'right');
27071     },
27072     
27073     onRotateFail : function()
27074     {
27075         this.errorEl.show(true);
27076         
27077         var _this = this;
27078         
27079         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27080     },
27081     
27082     draw : function()
27083     {
27084         this.previewEl.dom.innerHTML = '';
27085         
27086         var canvasEl = document.createElement("canvas");
27087         
27088         var contextEl = canvasEl.getContext("2d");
27089         
27090         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27091         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27092         var center = this.imageEl.OriginWidth / 2;
27093         
27094         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27095             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27096             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27097             center = this.imageEl.OriginHeight / 2;
27098         }
27099         
27100         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27101         
27102         contextEl.translate(center, center);
27103         contextEl.rotate(this.rotate * Math.PI / 180);
27104
27105         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27106         
27107         this.canvasEl = document.createElement("canvas");
27108         
27109         this.contextEl = this.canvasEl.getContext("2d");
27110         
27111         switch (this.rotate) {
27112             case 0 :
27113                 
27114                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27115                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27116                 
27117                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27118                 
27119                 break;
27120             case 90 : 
27121                 
27122                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27123                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27124                 
27125                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27126                     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);
27127                     break;
27128                 }
27129                 
27130                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27131                 
27132                 break;
27133             case 180 :
27134                 
27135                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27136                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27137                 
27138                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27139                     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);
27140                     break;
27141                 }
27142                 
27143                 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);
27144                 
27145                 break;
27146             case 270 :
27147                 
27148                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27149                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27150         
27151                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27152                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27153                     break;
27154                 }
27155                 
27156                 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);
27157                 
27158                 break;
27159             default : 
27160                 break;
27161         }
27162         
27163         this.previewEl.appendChild(this.canvasEl);
27164         
27165         this.setCanvasPosition();
27166     },
27167     
27168     crop : function()
27169     {
27170         if(!this.canvasLoaded){
27171             return;
27172         }
27173         
27174         var imageCanvas = document.createElement("canvas");
27175         
27176         var imageContext = imageCanvas.getContext("2d");
27177         
27178         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27179         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27180         
27181         var center = imageCanvas.width / 2;
27182         
27183         imageContext.translate(center, center);
27184         
27185         imageContext.rotate(this.rotate * Math.PI / 180);
27186         
27187         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27188         
27189         var canvas = document.createElement("canvas");
27190         
27191         var context = canvas.getContext("2d");
27192                 
27193         canvas.width = this.minWidth;
27194         canvas.height = this.minHeight;
27195
27196         switch (this.rotate) {
27197             case 0 :
27198                 
27199                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27200                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27201                 
27202                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27203                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27204                 
27205                 var targetWidth = this.minWidth - 2 * x;
27206                 var targetHeight = this.minHeight - 2 * y;
27207                 
27208                 var scale = 1;
27209                 
27210                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27211                     scale = targetWidth / width;
27212                 }
27213                 
27214                 if(x > 0 && y == 0){
27215                     scale = targetHeight / height;
27216                 }
27217                 
27218                 if(x > 0 && y > 0){
27219                     scale = targetWidth / width;
27220                     
27221                     if(width < height){
27222                         scale = targetHeight / height;
27223                     }
27224                 }
27225                 
27226                 context.scale(scale, scale);
27227                 
27228                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27229                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27230
27231                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27232                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27233
27234                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27235                 
27236                 break;
27237             case 90 : 
27238                 
27239                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27240                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27241                 
27242                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27243                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27244                 
27245                 var targetWidth = this.minWidth - 2 * x;
27246                 var targetHeight = this.minHeight - 2 * y;
27247                 
27248                 var scale = 1;
27249                 
27250                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27251                     scale = targetWidth / width;
27252                 }
27253                 
27254                 if(x > 0 && y == 0){
27255                     scale = targetHeight / height;
27256                 }
27257                 
27258                 if(x > 0 && y > 0){
27259                     scale = targetWidth / width;
27260                     
27261                     if(width < height){
27262                         scale = targetHeight / height;
27263                     }
27264                 }
27265                 
27266                 context.scale(scale, scale);
27267                 
27268                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27269                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27270
27271                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27272                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27273                 
27274                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27275                 
27276                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27277                 
27278                 break;
27279             case 180 :
27280                 
27281                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27282                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27283                 
27284                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27285                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27286                 
27287                 var targetWidth = this.minWidth - 2 * x;
27288                 var targetHeight = this.minHeight - 2 * y;
27289                 
27290                 var scale = 1;
27291                 
27292                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27293                     scale = targetWidth / width;
27294                 }
27295                 
27296                 if(x > 0 && y == 0){
27297                     scale = targetHeight / height;
27298                 }
27299                 
27300                 if(x > 0 && y > 0){
27301                     scale = targetWidth / width;
27302                     
27303                     if(width < height){
27304                         scale = targetHeight / height;
27305                     }
27306                 }
27307                 
27308                 context.scale(scale, scale);
27309                 
27310                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27311                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27312
27313                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27314                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27315
27316                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27317                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27318                 
27319                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27320                 
27321                 break;
27322             case 270 :
27323                 
27324                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27325                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27326                 
27327                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27328                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27329                 
27330                 var targetWidth = this.minWidth - 2 * x;
27331                 var targetHeight = this.minHeight - 2 * y;
27332                 
27333                 var scale = 1;
27334                 
27335                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27336                     scale = targetWidth / width;
27337                 }
27338                 
27339                 if(x > 0 && y == 0){
27340                     scale = targetHeight / height;
27341                 }
27342                 
27343                 if(x > 0 && y > 0){
27344                     scale = targetWidth / width;
27345                     
27346                     if(width < height){
27347                         scale = targetHeight / height;
27348                     }
27349                 }
27350                 
27351                 context.scale(scale, scale);
27352                 
27353                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27354                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27355
27356                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27357                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27358                 
27359                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27360                 
27361                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27362                 
27363                 break;
27364             default : 
27365                 break;
27366         }
27367         
27368         this.cropData = canvas.toDataURL(this.cropType);
27369         
27370         if(this.fireEvent('crop', this, this.cropData) !== false){
27371             this.process(this.file, this.cropData);
27372         }
27373         
27374         return;
27375         
27376     },
27377     
27378     setThumbBoxSize : function()
27379     {
27380         var width, height;
27381         
27382         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27383             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27384             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27385             
27386             this.minWidth = width;
27387             this.minHeight = height;
27388             
27389             if(this.rotate == 90 || this.rotate == 270){
27390                 this.minWidth = height;
27391                 this.minHeight = width;
27392             }
27393         }
27394         
27395         height = 300;
27396         width = Math.ceil(this.minWidth * height / this.minHeight);
27397         
27398         if(this.minWidth > this.minHeight){
27399             width = 300;
27400             height = Math.ceil(this.minHeight * width / this.minWidth);
27401         }
27402         
27403         this.thumbEl.setStyle({
27404             width : width + 'px',
27405             height : height + 'px'
27406         });
27407
27408         return;
27409             
27410     },
27411     
27412     setThumbBoxPosition : function()
27413     {
27414         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27415         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27416         
27417         this.thumbEl.setLeft(x);
27418         this.thumbEl.setTop(y);
27419         
27420     },
27421     
27422     baseRotateLevel : function()
27423     {
27424         this.baseRotate = 1;
27425         
27426         if(
27427                 typeof(this.exif) != 'undefined' &&
27428                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27429                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27430         ){
27431             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27432         }
27433         
27434         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27435         
27436     },
27437     
27438     baseScaleLevel : function()
27439     {
27440         var width, height;
27441         
27442         if(this.isDocument){
27443             
27444             if(this.baseRotate == 6 || this.baseRotate == 8){
27445             
27446                 height = this.thumbEl.getHeight();
27447                 this.baseScale = height / this.imageEl.OriginWidth;
27448
27449                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27450                     width = this.thumbEl.getWidth();
27451                     this.baseScale = width / this.imageEl.OriginHeight;
27452                 }
27453
27454                 return;
27455             }
27456
27457             height = this.thumbEl.getHeight();
27458             this.baseScale = height / this.imageEl.OriginHeight;
27459
27460             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27461                 width = this.thumbEl.getWidth();
27462                 this.baseScale = width / this.imageEl.OriginWidth;
27463             }
27464
27465             return;
27466         }
27467         
27468         if(this.baseRotate == 6 || this.baseRotate == 8){
27469             
27470             width = this.thumbEl.getHeight();
27471             this.baseScale = width / this.imageEl.OriginHeight;
27472             
27473             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27474                 height = this.thumbEl.getWidth();
27475                 this.baseScale = height / this.imageEl.OriginHeight;
27476             }
27477             
27478             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27479                 height = this.thumbEl.getWidth();
27480                 this.baseScale = height / this.imageEl.OriginHeight;
27481                 
27482                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27483                     width = this.thumbEl.getHeight();
27484                     this.baseScale = width / this.imageEl.OriginWidth;
27485                 }
27486             }
27487             
27488             return;
27489         }
27490         
27491         width = this.thumbEl.getWidth();
27492         this.baseScale = width / this.imageEl.OriginWidth;
27493         
27494         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27495             height = this.thumbEl.getHeight();
27496             this.baseScale = height / this.imageEl.OriginHeight;
27497         }
27498         
27499         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27500             
27501             height = this.thumbEl.getHeight();
27502             this.baseScale = height / this.imageEl.OriginHeight;
27503             
27504             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27505                 width = this.thumbEl.getWidth();
27506                 this.baseScale = width / this.imageEl.OriginWidth;
27507             }
27508             
27509         }
27510         
27511         return;
27512     },
27513     
27514     getScaleLevel : function()
27515     {
27516         return this.baseScale * Math.pow(1.1, this.scale);
27517     },
27518     
27519     onTouchStart : function(e)
27520     {
27521         if(!this.canvasLoaded){
27522             this.beforeSelectFile(e);
27523             return;
27524         }
27525         
27526         var touches = e.browserEvent.touches;
27527         
27528         if(!touches){
27529             return;
27530         }
27531         
27532         if(touches.length == 1){
27533             this.onMouseDown(e);
27534             return;
27535         }
27536         
27537         if(touches.length != 2){
27538             return;
27539         }
27540         
27541         var coords = [];
27542         
27543         for(var i = 0, finger; finger = touches[i]; i++){
27544             coords.push(finger.pageX, finger.pageY);
27545         }
27546         
27547         var x = Math.pow(coords[0] - coords[2], 2);
27548         var y = Math.pow(coords[1] - coords[3], 2);
27549         
27550         this.startDistance = Math.sqrt(x + y);
27551         
27552         this.startScale = this.scale;
27553         
27554         this.pinching = true;
27555         this.dragable = false;
27556         
27557     },
27558     
27559     onTouchMove : function(e)
27560     {
27561         if(!this.pinching && !this.dragable){
27562             return;
27563         }
27564         
27565         var touches = e.browserEvent.touches;
27566         
27567         if(!touches){
27568             return;
27569         }
27570         
27571         if(this.dragable){
27572             this.onMouseMove(e);
27573             return;
27574         }
27575         
27576         var coords = [];
27577         
27578         for(var i = 0, finger; finger = touches[i]; i++){
27579             coords.push(finger.pageX, finger.pageY);
27580         }
27581         
27582         var x = Math.pow(coords[0] - coords[2], 2);
27583         var y = Math.pow(coords[1] - coords[3], 2);
27584         
27585         this.endDistance = Math.sqrt(x + y);
27586         
27587         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27588         
27589         if(!this.zoomable()){
27590             this.scale = this.startScale;
27591             return;
27592         }
27593         
27594         this.draw();
27595         
27596     },
27597     
27598     onTouchEnd : function(e)
27599     {
27600         this.pinching = false;
27601         this.dragable = false;
27602         
27603     },
27604     
27605     process : function(file, crop)
27606     {
27607         if(this.loadMask){
27608             this.maskEl.mask(this.loadingText);
27609         }
27610         
27611         this.xhr = new XMLHttpRequest();
27612         
27613         file.xhr = this.xhr;
27614
27615         this.xhr.open(this.method, this.url, true);
27616         
27617         var headers = {
27618             "Accept": "application/json",
27619             "Cache-Control": "no-cache",
27620             "X-Requested-With": "XMLHttpRequest"
27621         };
27622         
27623         for (var headerName in headers) {
27624             var headerValue = headers[headerName];
27625             if (headerValue) {
27626                 this.xhr.setRequestHeader(headerName, headerValue);
27627             }
27628         }
27629         
27630         var _this = this;
27631         
27632         this.xhr.onload = function()
27633         {
27634             _this.xhrOnLoad(_this.xhr);
27635         }
27636         
27637         this.xhr.onerror = function()
27638         {
27639             _this.xhrOnError(_this.xhr);
27640         }
27641         
27642         var formData = new FormData();
27643
27644         formData.append('returnHTML', 'NO');
27645         
27646         if(crop){
27647             formData.append('crop', crop);
27648         }
27649         
27650         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27651             formData.append(this.paramName, file, file.name);
27652         }
27653         
27654         if(typeof(file.filename) != 'undefined'){
27655             formData.append('filename', file.filename);
27656         }
27657         
27658         if(typeof(file.mimetype) != 'undefined'){
27659             formData.append('mimetype', file.mimetype);
27660         }
27661         
27662         if(this.fireEvent('arrange', this, formData) != false){
27663             this.xhr.send(formData);
27664         };
27665     },
27666     
27667     xhrOnLoad : function(xhr)
27668     {
27669         if(this.loadMask){
27670             this.maskEl.unmask();
27671         }
27672         
27673         if (xhr.readyState !== 4) {
27674             this.fireEvent('exception', this, xhr);
27675             return;
27676         }
27677
27678         var response = Roo.decode(xhr.responseText);
27679         
27680         if(!response.success){
27681             this.fireEvent('exception', this, xhr);
27682             return;
27683         }
27684         
27685         var response = Roo.decode(xhr.responseText);
27686         
27687         this.fireEvent('upload', this, response);
27688         
27689     },
27690     
27691     xhrOnError : function()
27692     {
27693         if(this.loadMask){
27694             this.maskEl.unmask();
27695         }
27696         
27697         Roo.log('xhr on error');
27698         
27699         var response = Roo.decode(xhr.responseText);
27700           
27701         Roo.log(response);
27702         
27703     },
27704     
27705     prepare : function(file)
27706     {   
27707         if(this.loadMask){
27708             this.maskEl.mask(this.loadingText);
27709         }
27710         
27711         this.file = false;
27712         this.exif = {};
27713         
27714         if(typeof(file) === 'string'){
27715             this.loadCanvas(file);
27716             return;
27717         }
27718         
27719         if(!file || !this.urlAPI){
27720             return;
27721         }
27722         
27723         this.file = file;
27724         this.cropType = file.type;
27725         
27726         var _this = this;
27727         
27728         if(this.fireEvent('prepare', this, this.file) != false){
27729             
27730             var reader = new FileReader();
27731             
27732             reader.onload = function (e) {
27733                 if (e.target.error) {
27734                     Roo.log(e.target.error);
27735                     return;
27736                 }
27737                 
27738                 var buffer = e.target.result,
27739                     dataView = new DataView(buffer),
27740                     offset = 2,
27741                     maxOffset = dataView.byteLength - 4,
27742                     markerBytes,
27743                     markerLength;
27744                 
27745                 if (dataView.getUint16(0) === 0xffd8) {
27746                     while (offset < maxOffset) {
27747                         markerBytes = dataView.getUint16(offset);
27748                         
27749                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27750                             markerLength = dataView.getUint16(offset + 2) + 2;
27751                             if (offset + markerLength > dataView.byteLength) {
27752                                 Roo.log('Invalid meta data: Invalid segment size.');
27753                                 break;
27754                             }
27755                             
27756                             if(markerBytes == 0xffe1){
27757                                 _this.parseExifData(
27758                                     dataView,
27759                                     offset,
27760                                     markerLength
27761                                 );
27762                             }
27763                             
27764                             offset += markerLength;
27765                             
27766                             continue;
27767                         }
27768                         
27769                         break;
27770                     }
27771                     
27772                 }
27773                 
27774                 var url = _this.urlAPI.createObjectURL(_this.file);
27775                 
27776                 _this.loadCanvas(url);
27777                 
27778                 return;
27779             }
27780             
27781             reader.readAsArrayBuffer(this.file);
27782             
27783         }
27784         
27785     },
27786     
27787     parseExifData : function(dataView, offset, length)
27788     {
27789         var tiffOffset = offset + 10,
27790             littleEndian,
27791             dirOffset;
27792     
27793         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27794             // No Exif data, might be XMP data instead
27795             return;
27796         }
27797         
27798         // Check for the ASCII code for "Exif" (0x45786966):
27799         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27800             // No Exif data, might be XMP data instead
27801             return;
27802         }
27803         if (tiffOffset + 8 > dataView.byteLength) {
27804             Roo.log('Invalid Exif data: Invalid segment size.');
27805             return;
27806         }
27807         // Check for the two null bytes:
27808         if (dataView.getUint16(offset + 8) !== 0x0000) {
27809             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27810             return;
27811         }
27812         // Check the byte alignment:
27813         switch (dataView.getUint16(tiffOffset)) {
27814         case 0x4949:
27815             littleEndian = true;
27816             break;
27817         case 0x4D4D:
27818             littleEndian = false;
27819             break;
27820         default:
27821             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27822             return;
27823         }
27824         // Check for the TIFF tag marker (0x002A):
27825         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27826             Roo.log('Invalid Exif data: Missing TIFF marker.');
27827             return;
27828         }
27829         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27830         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27831         
27832         this.parseExifTags(
27833             dataView,
27834             tiffOffset,
27835             tiffOffset + dirOffset,
27836             littleEndian
27837         );
27838     },
27839     
27840     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27841     {
27842         var tagsNumber,
27843             dirEndOffset,
27844             i;
27845         if (dirOffset + 6 > dataView.byteLength) {
27846             Roo.log('Invalid Exif data: Invalid directory offset.');
27847             return;
27848         }
27849         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27850         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27851         if (dirEndOffset + 4 > dataView.byteLength) {
27852             Roo.log('Invalid Exif data: Invalid directory size.');
27853             return;
27854         }
27855         for (i = 0; i < tagsNumber; i += 1) {
27856             this.parseExifTag(
27857                 dataView,
27858                 tiffOffset,
27859                 dirOffset + 2 + 12 * i, // tag offset
27860                 littleEndian
27861             );
27862         }
27863         // Return the offset to the next directory:
27864         return dataView.getUint32(dirEndOffset, littleEndian);
27865     },
27866     
27867     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27868     {
27869         var tag = dataView.getUint16(offset, littleEndian);
27870         
27871         this.exif[tag] = this.getExifValue(
27872             dataView,
27873             tiffOffset,
27874             offset,
27875             dataView.getUint16(offset + 2, littleEndian), // tag type
27876             dataView.getUint32(offset + 4, littleEndian), // tag length
27877             littleEndian
27878         );
27879     },
27880     
27881     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27882     {
27883         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27884             tagSize,
27885             dataOffset,
27886             values,
27887             i,
27888             str,
27889             c;
27890     
27891         if (!tagType) {
27892             Roo.log('Invalid Exif data: Invalid tag type.');
27893             return;
27894         }
27895         
27896         tagSize = tagType.size * length;
27897         // Determine if the value is contained in the dataOffset bytes,
27898         // or if the value at the dataOffset is a pointer to the actual data:
27899         dataOffset = tagSize > 4 ?
27900                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27901         if (dataOffset + tagSize > dataView.byteLength) {
27902             Roo.log('Invalid Exif data: Invalid data offset.');
27903             return;
27904         }
27905         if (length === 1) {
27906             return tagType.getValue(dataView, dataOffset, littleEndian);
27907         }
27908         values = [];
27909         for (i = 0; i < length; i += 1) {
27910             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27911         }
27912         
27913         if (tagType.ascii) {
27914             str = '';
27915             // Concatenate the chars:
27916             for (i = 0; i < values.length; i += 1) {
27917                 c = values[i];
27918                 // Ignore the terminating NULL byte(s):
27919                 if (c === '\u0000') {
27920                     break;
27921                 }
27922                 str += c;
27923             }
27924             return str;
27925         }
27926         return values;
27927     }
27928     
27929 });
27930
27931 Roo.apply(Roo.bootstrap.UploadCropbox, {
27932     tags : {
27933         'Orientation': 0x0112
27934     },
27935     
27936     Orientation: {
27937             1: 0, //'top-left',
27938 //            2: 'top-right',
27939             3: 180, //'bottom-right',
27940 //            4: 'bottom-left',
27941 //            5: 'left-top',
27942             6: 90, //'right-top',
27943 //            7: 'right-bottom',
27944             8: 270 //'left-bottom'
27945     },
27946     
27947     exifTagTypes : {
27948         // byte, 8-bit unsigned int:
27949         1: {
27950             getValue: function (dataView, dataOffset) {
27951                 return dataView.getUint8(dataOffset);
27952             },
27953             size: 1
27954         },
27955         // ascii, 8-bit byte:
27956         2: {
27957             getValue: function (dataView, dataOffset) {
27958                 return String.fromCharCode(dataView.getUint8(dataOffset));
27959             },
27960             size: 1,
27961             ascii: true
27962         },
27963         // short, 16 bit int:
27964         3: {
27965             getValue: function (dataView, dataOffset, littleEndian) {
27966                 return dataView.getUint16(dataOffset, littleEndian);
27967             },
27968             size: 2
27969         },
27970         // long, 32 bit int:
27971         4: {
27972             getValue: function (dataView, dataOffset, littleEndian) {
27973                 return dataView.getUint32(dataOffset, littleEndian);
27974             },
27975             size: 4
27976         },
27977         // rational = two long values, first is numerator, second is denominator:
27978         5: {
27979             getValue: function (dataView, dataOffset, littleEndian) {
27980                 return dataView.getUint32(dataOffset, littleEndian) /
27981                     dataView.getUint32(dataOffset + 4, littleEndian);
27982             },
27983             size: 8
27984         },
27985         // slong, 32 bit signed int:
27986         9: {
27987             getValue: function (dataView, dataOffset, littleEndian) {
27988                 return dataView.getInt32(dataOffset, littleEndian);
27989             },
27990             size: 4
27991         },
27992         // srational, two slongs, first is numerator, second is denominator:
27993         10: {
27994             getValue: function (dataView, dataOffset, littleEndian) {
27995                 return dataView.getInt32(dataOffset, littleEndian) /
27996                     dataView.getInt32(dataOffset + 4, littleEndian);
27997             },
27998             size: 8
27999         }
28000     },
28001     
28002     footer : {
28003         STANDARD : [
28004             {
28005                 tag : 'div',
28006                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28007                 action : 'rotate-left',
28008                 cn : [
28009                     {
28010                         tag : 'button',
28011                         cls : 'btn btn-default',
28012                         html : '<i class="fa fa-undo"></i>'
28013                     }
28014                 ]
28015             },
28016             {
28017                 tag : 'div',
28018                 cls : 'btn-group roo-upload-cropbox-picture',
28019                 action : 'picture',
28020                 cn : [
28021                     {
28022                         tag : 'button',
28023                         cls : 'btn btn-default',
28024                         html : '<i class="fa fa-picture-o"></i>'
28025                     }
28026                 ]
28027             },
28028             {
28029                 tag : 'div',
28030                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28031                 action : 'rotate-right',
28032                 cn : [
28033                     {
28034                         tag : 'button',
28035                         cls : 'btn btn-default',
28036                         html : '<i class="fa fa-repeat"></i>'
28037                     }
28038                 ]
28039             }
28040         ],
28041         DOCUMENT : [
28042             {
28043                 tag : 'div',
28044                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28045                 action : 'rotate-left',
28046                 cn : [
28047                     {
28048                         tag : 'button',
28049                         cls : 'btn btn-default',
28050                         html : '<i class="fa fa-undo"></i>'
28051                     }
28052                 ]
28053             },
28054             {
28055                 tag : 'div',
28056                 cls : 'btn-group roo-upload-cropbox-download',
28057                 action : 'download',
28058                 cn : [
28059                     {
28060                         tag : 'button',
28061                         cls : 'btn btn-default',
28062                         html : '<i class="fa fa-download"></i>'
28063                     }
28064                 ]
28065             },
28066             {
28067                 tag : 'div',
28068                 cls : 'btn-group roo-upload-cropbox-crop',
28069                 action : 'crop',
28070                 cn : [
28071                     {
28072                         tag : 'button',
28073                         cls : 'btn btn-default',
28074                         html : '<i class="fa fa-crop"></i>'
28075                     }
28076                 ]
28077             },
28078             {
28079                 tag : 'div',
28080                 cls : 'btn-group roo-upload-cropbox-trash',
28081                 action : 'trash',
28082                 cn : [
28083                     {
28084                         tag : 'button',
28085                         cls : 'btn btn-default',
28086                         html : '<i class="fa fa-trash"></i>'
28087                     }
28088                 ]
28089             },
28090             {
28091                 tag : 'div',
28092                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28093                 action : 'rotate-right',
28094                 cn : [
28095                     {
28096                         tag : 'button',
28097                         cls : 'btn btn-default',
28098                         html : '<i class="fa fa-repeat"></i>'
28099                     }
28100                 ]
28101             }
28102         ],
28103         ROTATOR : [
28104             {
28105                 tag : 'div',
28106                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28107                 action : 'rotate-left',
28108                 cn : [
28109                     {
28110                         tag : 'button',
28111                         cls : 'btn btn-default',
28112                         html : '<i class="fa fa-undo"></i>'
28113                     }
28114                 ]
28115             },
28116             {
28117                 tag : 'div',
28118                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28119                 action : 'rotate-right',
28120                 cn : [
28121                     {
28122                         tag : 'button',
28123                         cls : 'btn btn-default',
28124                         html : '<i class="fa fa-repeat"></i>'
28125                     }
28126                 ]
28127             }
28128         ]
28129     }
28130 });
28131
28132 /*
28133 * Licence: LGPL
28134 */
28135
28136 /**
28137  * @class Roo.bootstrap.DocumentManager
28138  * @extends Roo.bootstrap.Component
28139  * Bootstrap DocumentManager class
28140  * @cfg {String} paramName default 'imageUpload'
28141  * @cfg {String} toolTipName default 'filename'
28142  * @cfg {String} method default POST
28143  * @cfg {String} url action url
28144  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28145  * @cfg {Boolean} multiple multiple upload default true
28146  * @cfg {Number} thumbSize default 300
28147  * @cfg {String} fieldLabel
28148  * @cfg {Number} labelWidth default 4
28149  * @cfg {String} labelAlign (left|top) default left
28150  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28151 * @cfg {Number} labellg set the width of label (1-12)
28152  * @cfg {Number} labelmd set the width of label (1-12)
28153  * @cfg {Number} labelsm set the width of label (1-12)
28154  * @cfg {Number} labelxs set the width of label (1-12)
28155  * 
28156  * @constructor
28157  * Create a new DocumentManager
28158  * @param {Object} config The config object
28159  */
28160
28161 Roo.bootstrap.DocumentManager = function(config){
28162     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28163     
28164     this.files = [];
28165     this.delegates = [];
28166     
28167     this.addEvents({
28168         /**
28169          * @event initial
28170          * Fire when initial the DocumentManager
28171          * @param {Roo.bootstrap.DocumentManager} this
28172          */
28173         "initial" : true,
28174         /**
28175          * @event inspect
28176          * inspect selected file
28177          * @param {Roo.bootstrap.DocumentManager} this
28178          * @param {File} file
28179          */
28180         "inspect" : true,
28181         /**
28182          * @event exception
28183          * Fire when xhr load exception
28184          * @param {Roo.bootstrap.DocumentManager} this
28185          * @param {XMLHttpRequest} xhr
28186          */
28187         "exception" : true,
28188         /**
28189          * @event afterupload
28190          * Fire when xhr load exception
28191          * @param {Roo.bootstrap.DocumentManager} this
28192          * @param {XMLHttpRequest} xhr
28193          */
28194         "afterupload" : true,
28195         /**
28196          * @event prepare
28197          * prepare the form data
28198          * @param {Roo.bootstrap.DocumentManager} this
28199          * @param {Object} formData
28200          */
28201         "prepare" : true,
28202         /**
28203          * @event remove
28204          * Fire when remove the file
28205          * @param {Roo.bootstrap.DocumentManager} this
28206          * @param {Object} file
28207          */
28208         "remove" : true,
28209         /**
28210          * @event refresh
28211          * Fire after refresh the file
28212          * @param {Roo.bootstrap.DocumentManager} this
28213          */
28214         "refresh" : true,
28215         /**
28216          * @event click
28217          * Fire after click the image
28218          * @param {Roo.bootstrap.DocumentManager} this
28219          * @param {Object} file
28220          */
28221         "click" : true,
28222         /**
28223          * @event edit
28224          * Fire when upload a image and editable set to true
28225          * @param {Roo.bootstrap.DocumentManager} this
28226          * @param {Object} file
28227          */
28228         "edit" : true,
28229         /**
28230          * @event beforeselectfile
28231          * Fire before select file
28232          * @param {Roo.bootstrap.DocumentManager} this
28233          */
28234         "beforeselectfile" : true,
28235         /**
28236          * @event process
28237          * Fire before process file
28238          * @param {Roo.bootstrap.DocumentManager} this
28239          * @param {Object} file
28240          */
28241         "process" : true
28242         
28243     });
28244 };
28245
28246 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28247     
28248     boxes : 0,
28249     inputName : '',
28250     thumbSize : 300,
28251     multiple : true,
28252     files : false,
28253     method : 'POST',
28254     url : '',
28255     paramName : 'imageUpload',
28256     toolTipName : 'filename',
28257     fieldLabel : '',
28258     labelWidth : 4,
28259     labelAlign : 'left',
28260     editable : true,
28261     delegates : false,
28262     xhr : false, 
28263     
28264     labellg : 0,
28265     labelmd : 0,
28266     labelsm : 0,
28267     labelxs : 0,
28268     
28269     getAutoCreate : function()
28270     {   
28271         var managerWidget = {
28272             tag : 'div',
28273             cls : 'roo-document-manager',
28274             cn : [
28275                 {
28276                     tag : 'input',
28277                     cls : 'roo-document-manager-selector',
28278                     type : 'file'
28279                 },
28280                 {
28281                     tag : 'div',
28282                     cls : 'roo-document-manager-uploader',
28283                     cn : [
28284                         {
28285                             tag : 'div',
28286                             cls : 'roo-document-manager-upload-btn',
28287                             html : '<i class="fa fa-plus"></i>'
28288                         }
28289                     ]
28290                     
28291                 }
28292             ]
28293         };
28294         
28295         var content = [
28296             {
28297                 tag : 'div',
28298                 cls : 'column col-md-12',
28299                 cn : managerWidget
28300             }
28301         ];
28302         
28303         if(this.fieldLabel.length){
28304             
28305             content = [
28306                 {
28307                     tag : 'div',
28308                     cls : 'column col-md-12',
28309                     html : this.fieldLabel
28310                 },
28311                 {
28312                     tag : 'div',
28313                     cls : 'column col-md-12',
28314                     cn : managerWidget
28315                 }
28316             ];
28317
28318             if(this.labelAlign == 'left'){
28319                 content = [
28320                     {
28321                         tag : 'div',
28322                         cls : 'column',
28323                         html : this.fieldLabel
28324                     },
28325                     {
28326                         tag : 'div',
28327                         cls : 'column',
28328                         cn : managerWidget
28329                     }
28330                 ];
28331                 
28332                 if(this.labelWidth > 12){
28333                     content[0].style = "width: " + this.labelWidth + 'px';
28334                 }
28335
28336                 if(this.labelWidth < 13 && this.labelmd == 0){
28337                     this.labelmd = this.labelWidth;
28338                 }
28339
28340                 if(this.labellg > 0){
28341                     content[0].cls += ' col-lg-' + this.labellg;
28342                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28343                 }
28344
28345                 if(this.labelmd > 0){
28346                     content[0].cls += ' col-md-' + this.labelmd;
28347                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28348                 }
28349
28350                 if(this.labelsm > 0){
28351                     content[0].cls += ' col-sm-' + this.labelsm;
28352                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28353                 }
28354
28355                 if(this.labelxs > 0){
28356                     content[0].cls += ' col-xs-' + this.labelxs;
28357                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28358                 }
28359                 
28360             }
28361         }
28362         
28363         var cfg = {
28364             tag : 'div',
28365             cls : 'row clearfix',
28366             cn : content
28367         };
28368         
28369         return cfg;
28370         
28371     },
28372     
28373     initEvents : function()
28374     {
28375         this.managerEl = this.el.select('.roo-document-manager', true).first();
28376         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28377         
28378         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28379         this.selectorEl.hide();
28380         
28381         if(this.multiple){
28382             this.selectorEl.attr('multiple', 'multiple');
28383         }
28384         
28385         this.selectorEl.on('change', this.onFileSelected, this);
28386         
28387         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28388         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28389         
28390         this.uploader.on('click', this.onUploaderClick, this);
28391         
28392         this.renderProgressDialog();
28393         
28394         var _this = this;
28395         
28396         window.addEventListener("resize", function() { _this.refresh(); } );
28397         
28398         this.fireEvent('initial', this);
28399     },
28400     
28401     renderProgressDialog : function()
28402     {
28403         var _this = this;
28404         
28405         this.progressDialog = new Roo.bootstrap.Modal({
28406             cls : 'roo-document-manager-progress-dialog',
28407             allow_close : false,
28408             title : '',
28409             buttons : [
28410                 {
28411                     name  :'cancel',
28412                     weight : 'danger',
28413                     html : 'Cancel'
28414                 }
28415             ], 
28416             listeners : { 
28417                 btnclick : function() {
28418                     _this.uploadCancel();
28419                     this.hide();
28420                 }
28421             }
28422         });
28423          
28424         this.progressDialog.render(Roo.get(document.body));
28425          
28426         this.progress = new Roo.bootstrap.Progress({
28427             cls : 'roo-document-manager-progress',
28428             active : true,
28429             striped : true
28430         });
28431         
28432         this.progress.render(this.progressDialog.getChildContainer());
28433         
28434         this.progressBar = new Roo.bootstrap.ProgressBar({
28435             cls : 'roo-document-manager-progress-bar',
28436             aria_valuenow : 0,
28437             aria_valuemin : 0,
28438             aria_valuemax : 12,
28439             panel : 'success'
28440         });
28441         
28442         this.progressBar.render(this.progress.getChildContainer());
28443     },
28444     
28445     onUploaderClick : function(e)
28446     {
28447         e.preventDefault();
28448      
28449         if(this.fireEvent('beforeselectfile', this) != false){
28450             this.selectorEl.dom.click();
28451         }
28452         
28453     },
28454     
28455     onFileSelected : function(e)
28456     {
28457         e.preventDefault();
28458         
28459         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28460             return;
28461         }
28462         
28463         Roo.each(this.selectorEl.dom.files, function(file){
28464             if(this.fireEvent('inspect', this, file) != false){
28465                 this.files.push(file);
28466             }
28467         }, this);
28468         
28469         this.queue();
28470         
28471     },
28472     
28473     queue : function()
28474     {
28475         this.selectorEl.dom.value = '';
28476         
28477         if(!this.files.length){
28478             return;
28479         }
28480         
28481         if(this.boxes > 0 && this.files.length > this.boxes){
28482             this.files = this.files.slice(0, this.boxes);
28483         }
28484         
28485         this.uploader.show();
28486         
28487         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28488             this.uploader.hide();
28489         }
28490         
28491         var _this = this;
28492         
28493         var files = [];
28494         
28495         var docs = [];
28496         
28497         Roo.each(this.files, function(file){
28498             
28499             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28500                 var f = this.renderPreview(file);
28501                 files.push(f);
28502                 return;
28503             }
28504             
28505             if(file.type.indexOf('image') != -1){
28506                 this.delegates.push(
28507                     (function(){
28508                         _this.process(file);
28509                     }).createDelegate(this)
28510                 );
28511         
28512                 return;
28513             }
28514             
28515             docs.push(
28516                 (function(){
28517                     _this.process(file);
28518                 }).createDelegate(this)
28519             );
28520             
28521         }, this);
28522         
28523         this.files = files;
28524         
28525         this.delegates = this.delegates.concat(docs);
28526         
28527         if(!this.delegates.length){
28528             this.refresh();
28529             return;
28530         }
28531         
28532         this.progressBar.aria_valuemax = this.delegates.length;
28533         
28534         this.arrange();
28535         
28536         return;
28537     },
28538     
28539     arrange : function()
28540     {
28541         if(!this.delegates.length){
28542             this.progressDialog.hide();
28543             this.refresh();
28544             return;
28545         }
28546         
28547         var delegate = this.delegates.shift();
28548         
28549         this.progressDialog.show();
28550         
28551         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28552         
28553         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28554         
28555         delegate();
28556     },
28557     
28558     refresh : function()
28559     {
28560         this.uploader.show();
28561         
28562         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28563             this.uploader.hide();
28564         }
28565         
28566         Roo.isTouch ? this.closable(false) : this.closable(true);
28567         
28568         this.fireEvent('refresh', this);
28569     },
28570     
28571     onRemove : function(e, el, o)
28572     {
28573         e.preventDefault();
28574         
28575         this.fireEvent('remove', this, o);
28576         
28577     },
28578     
28579     remove : function(o)
28580     {
28581         var files = [];
28582         
28583         Roo.each(this.files, function(file){
28584             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28585                 files.push(file);
28586                 return;
28587             }
28588
28589             o.target.remove();
28590
28591         }, this);
28592         
28593         this.files = files;
28594         
28595         this.refresh();
28596     },
28597     
28598     clear : function()
28599     {
28600         Roo.each(this.files, function(file){
28601             if(!file.target){
28602                 return;
28603             }
28604             
28605             file.target.remove();
28606
28607         }, this);
28608         
28609         this.files = [];
28610         
28611         this.refresh();
28612     },
28613     
28614     onClick : function(e, el, o)
28615     {
28616         e.preventDefault();
28617         
28618         this.fireEvent('click', this, o);
28619         
28620     },
28621     
28622     closable : function(closable)
28623     {
28624         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28625             
28626             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28627             
28628             if(closable){
28629                 el.show();
28630                 return;
28631             }
28632             
28633             el.hide();
28634             
28635         }, this);
28636     },
28637     
28638     xhrOnLoad : function(xhr)
28639     {
28640         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28641             el.remove();
28642         }, this);
28643         
28644         if (xhr.readyState !== 4) {
28645             this.arrange();
28646             this.fireEvent('exception', this, xhr);
28647             return;
28648         }
28649
28650         var response = Roo.decode(xhr.responseText);
28651         
28652         if(!response.success){
28653             this.arrange();
28654             this.fireEvent('exception', this, xhr);
28655             return;
28656         }
28657         
28658         var file = this.renderPreview(response.data);
28659         
28660         this.files.push(file);
28661         
28662         this.arrange();
28663         
28664         this.fireEvent('afterupload', this, xhr);
28665         
28666     },
28667     
28668     xhrOnError : function(xhr)
28669     {
28670         Roo.log('xhr on error');
28671         
28672         var response = Roo.decode(xhr.responseText);
28673           
28674         Roo.log(response);
28675         
28676         this.arrange();
28677     },
28678     
28679     process : function(file)
28680     {
28681         if(this.fireEvent('process', this, file) !== false){
28682             if(this.editable && file.type.indexOf('image') != -1){
28683                 this.fireEvent('edit', this, file);
28684                 return;
28685             }
28686
28687             this.uploadStart(file, false);
28688
28689             return;
28690         }
28691         
28692     },
28693     
28694     uploadStart : function(file, crop)
28695     {
28696         this.xhr = new XMLHttpRequest();
28697         
28698         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28699             this.arrange();
28700             return;
28701         }
28702         
28703         file.xhr = this.xhr;
28704             
28705         this.managerEl.createChild({
28706             tag : 'div',
28707             cls : 'roo-document-manager-loading',
28708             cn : [
28709                 {
28710                     tag : 'div',
28711                     tooltip : file.name,
28712                     cls : 'roo-document-manager-thumb',
28713                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28714                 }
28715             ]
28716
28717         });
28718
28719         this.xhr.open(this.method, this.url, true);
28720         
28721         var headers = {
28722             "Accept": "application/json",
28723             "Cache-Control": "no-cache",
28724             "X-Requested-With": "XMLHttpRequest"
28725         };
28726         
28727         for (var headerName in headers) {
28728             var headerValue = headers[headerName];
28729             if (headerValue) {
28730                 this.xhr.setRequestHeader(headerName, headerValue);
28731             }
28732         }
28733         
28734         var _this = this;
28735         
28736         this.xhr.onload = function()
28737         {
28738             _this.xhrOnLoad(_this.xhr);
28739         }
28740         
28741         this.xhr.onerror = function()
28742         {
28743             _this.xhrOnError(_this.xhr);
28744         }
28745         
28746         var formData = new FormData();
28747
28748         formData.append('returnHTML', 'NO');
28749         
28750         if(crop){
28751             formData.append('crop', crop);
28752         }
28753         
28754         formData.append(this.paramName, file, file.name);
28755         
28756         var options = {
28757             file : file, 
28758             manually : false
28759         };
28760         
28761         if(this.fireEvent('prepare', this, formData, options) != false){
28762             
28763             if(options.manually){
28764                 return;
28765             }
28766             
28767             this.xhr.send(formData);
28768             return;
28769         };
28770         
28771         this.uploadCancel();
28772     },
28773     
28774     uploadCancel : function()
28775     {
28776         if (this.xhr) {
28777             this.xhr.abort();
28778         }
28779         
28780         this.delegates = [];
28781         
28782         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28783             el.remove();
28784         }, this);
28785         
28786         this.arrange();
28787     },
28788     
28789     renderPreview : function(file)
28790     {
28791         if(typeof(file.target) != 'undefined' && file.target){
28792             return file;
28793         }
28794         
28795         var previewEl = this.managerEl.createChild({
28796             tag : 'div',
28797             cls : 'roo-document-manager-preview',
28798             cn : [
28799                 {
28800                     tag : 'div',
28801                     tooltip : file[this.toolTipName],
28802                     cls : 'roo-document-manager-thumb',
28803                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28804                 },
28805                 {
28806                     tag : 'button',
28807                     cls : 'close',
28808                     html : '<i class="fa fa-times-circle"></i>'
28809                 }
28810             ]
28811         });
28812
28813         var close = previewEl.select('button.close', true).first();
28814
28815         close.on('click', this.onRemove, this, file);
28816
28817         file.target = previewEl;
28818
28819         var image = previewEl.select('img', true).first();
28820         
28821         var _this = this;
28822         
28823         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28824         
28825         image.on('click', this.onClick, this, file);
28826         
28827         return file;
28828         
28829     },
28830     
28831     onPreviewLoad : function(file, image)
28832     {
28833         if(typeof(file.target) == 'undefined' || !file.target){
28834             return;
28835         }
28836         
28837         var width = image.dom.naturalWidth || image.dom.width;
28838         var height = image.dom.naturalHeight || image.dom.height;
28839         
28840         if(width > height){
28841             file.target.addClass('wide');
28842             return;
28843         }
28844         
28845         file.target.addClass('tall');
28846         return;
28847         
28848     },
28849     
28850     uploadFromSource : function(file, crop)
28851     {
28852         this.xhr = new XMLHttpRequest();
28853         
28854         this.managerEl.createChild({
28855             tag : 'div',
28856             cls : 'roo-document-manager-loading',
28857             cn : [
28858                 {
28859                     tag : 'div',
28860                     tooltip : file.name,
28861                     cls : 'roo-document-manager-thumb',
28862                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28863                 }
28864             ]
28865
28866         });
28867
28868         this.xhr.open(this.method, this.url, true);
28869         
28870         var headers = {
28871             "Accept": "application/json",
28872             "Cache-Control": "no-cache",
28873             "X-Requested-With": "XMLHttpRequest"
28874         };
28875         
28876         for (var headerName in headers) {
28877             var headerValue = headers[headerName];
28878             if (headerValue) {
28879                 this.xhr.setRequestHeader(headerName, headerValue);
28880             }
28881         }
28882         
28883         var _this = this;
28884         
28885         this.xhr.onload = function()
28886         {
28887             _this.xhrOnLoad(_this.xhr);
28888         }
28889         
28890         this.xhr.onerror = function()
28891         {
28892             _this.xhrOnError(_this.xhr);
28893         }
28894         
28895         var formData = new FormData();
28896
28897         formData.append('returnHTML', 'NO');
28898         
28899         formData.append('crop', crop);
28900         
28901         if(typeof(file.filename) != 'undefined'){
28902             formData.append('filename', file.filename);
28903         }
28904         
28905         if(typeof(file.mimetype) != 'undefined'){
28906             formData.append('mimetype', file.mimetype);
28907         }
28908         
28909         Roo.log(formData);
28910         
28911         if(this.fireEvent('prepare', this, formData) != false){
28912             this.xhr.send(formData);
28913         };
28914     }
28915 });
28916
28917 /*
28918 * Licence: LGPL
28919 */
28920
28921 /**
28922  * @class Roo.bootstrap.DocumentViewer
28923  * @extends Roo.bootstrap.Component
28924  * Bootstrap DocumentViewer class
28925  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28926  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28927  * 
28928  * @constructor
28929  * Create a new DocumentViewer
28930  * @param {Object} config The config object
28931  */
28932
28933 Roo.bootstrap.DocumentViewer = function(config){
28934     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28935     
28936     this.addEvents({
28937         /**
28938          * @event initial
28939          * Fire after initEvent
28940          * @param {Roo.bootstrap.DocumentViewer} this
28941          */
28942         "initial" : true,
28943         /**
28944          * @event click
28945          * Fire after click
28946          * @param {Roo.bootstrap.DocumentViewer} this
28947          */
28948         "click" : true,
28949         /**
28950          * @event download
28951          * Fire after download button
28952          * @param {Roo.bootstrap.DocumentViewer} this
28953          */
28954         "download" : true,
28955         /**
28956          * @event trash
28957          * Fire after trash button
28958          * @param {Roo.bootstrap.DocumentViewer} this
28959          */
28960         "trash" : true
28961         
28962     });
28963 };
28964
28965 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28966     
28967     showDownload : true,
28968     
28969     showTrash : true,
28970     
28971     getAutoCreate : function()
28972     {
28973         var cfg = {
28974             tag : 'div',
28975             cls : 'roo-document-viewer',
28976             cn : [
28977                 {
28978                     tag : 'div',
28979                     cls : 'roo-document-viewer-body',
28980                     cn : [
28981                         {
28982                             tag : 'div',
28983                             cls : 'roo-document-viewer-thumb',
28984                             cn : [
28985                                 {
28986                                     tag : 'img',
28987                                     cls : 'roo-document-viewer-image'
28988                                 }
28989                             ]
28990                         }
28991                     ]
28992                 },
28993                 {
28994                     tag : 'div',
28995                     cls : 'roo-document-viewer-footer',
28996                     cn : {
28997                         tag : 'div',
28998                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28999                         cn : [
29000                             {
29001                                 tag : 'div',
29002                                 cls : 'btn-group roo-document-viewer-download',
29003                                 cn : [
29004                                     {
29005                                         tag : 'button',
29006                                         cls : 'btn btn-default',
29007                                         html : '<i class="fa fa-download"></i>'
29008                                     }
29009                                 ]
29010                             },
29011                             {
29012                                 tag : 'div',
29013                                 cls : 'btn-group roo-document-viewer-trash',
29014                                 cn : [
29015                                     {
29016                                         tag : 'button',
29017                                         cls : 'btn btn-default',
29018                                         html : '<i class="fa fa-trash"></i>'
29019                                     }
29020                                 ]
29021                             }
29022                         ]
29023                     }
29024                 }
29025             ]
29026         };
29027         
29028         return cfg;
29029     },
29030     
29031     initEvents : function()
29032     {
29033         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29034         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29035         
29036         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29037         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29038         
29039         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29040         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29041         
29042         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29043         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29044         
29045         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29046         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29047         
29048         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29049         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29050         
29051         this.bodyEl.on('click', this.onClick, this);
29052         this.downloadBtn.on('click', this.onDownload, this);
29053         this.trashBtn.on('click', this.onTrash, this);
29054         
29055         this.downloadBtn.hide();
29056         this.trashBtn.hide();
29057         
29058         if(this.showDownload){
29059             this.downloadBtn.show();
29060         }
29061         
29062         if(this.showTrash){
29063             this.trashBtn.show();
29064         }
29065         
29066         if(!this.showDownload && !this.showTrash) {
29067             this.footerEl.hide();
29068         }
29069         
29070     },
29071     
29072     initial : function()
29073     {
29074         this.fireEvent('initial', this);
29075         
29076     },
29077     
29078     onClick : function(e)
29079     {
29080         e.preventDefault();
29081         
29082         this.fireEvent('click', this);
29083     },
29084     
29085     onDownload : function(e)
29086     {
29087         e.preventDefault();
29088         
29089         this.fireEvent('download', this);
29090     },
29091     
29092     onTrash : function(e)
29093     {
29094         e.preventDefault();
29095         
29096         this.fireEvent('trash', this);
29097     }
29098     
29099 });
29100 /*
29101  * - LGPL
29102  *
29103  * nav progress bar
29104  * 
29105  */
29106
29107 /**
29108  * @class Roo.bootstrap.NavProgressBar
29109  * @extends Roo.bootstrap.Component
29110  * Bootstrap NavProgressBar class
29111  * 
29112  * @constructor
29113  * Create a new nav progress bar
29114  * @param {Object} config The config object
29115  */
29116
29117 Roo.bootstrap.NavProgressBar = function(config){
29118     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29119
29120     this.bullets = this.bullets || [];
29121    
29122 //    Roo.bootstrap.NavProgressBar.register(this);
29123      this.addEvents({
29124         /**
29125              * @event changed
29126              * Fires when the active item changes
29127              * @param {Roo.bootstrap.NavProgressBar} this
29128              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29129              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29130          */
29131         'changed': true
29132      });
29133     
29134 };
29135
29136 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29137     
29138     bullets : [],
29139     barItems : [],
29140     
29141     getAutoCreate : function()
29142     {
29143         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29144         
29145         cfg = {
29146             tag : 'div',
29147             cls : 'roo-navigation-bar-group',
29148             cn : [
29149                 {
29150                     tag : 'div',
29151                     cls : 'roo-navigation-top-bar'
29152                 },
29153                 {
29154                     tag : 'div',
29155                     cls : 'roo-navigation-bullets-bar',
29156                     cn : [
29157                         {
29158                             tag : 'ul',
29159                             cls : 'roo-navigation-bar'
29160                         }
29161                     ]
29162                 },
29163                 
29164                 {
29165                     tag : 'div',
29166                     cls : 'roo-navigation-bottom-bar'
29167                 }
29168             ]
29169             
29170         };
29171         
29172         return cfg;
29173         
29174     },
29175     
29176     initEvents: function() 
29177     {
29178         
29179     },
29180     
29181     onRender : function(ct, position) 
29182     {
29183         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29184         
29185         if(this.bullets.length){
29186             Roo.each(this.bullets, function(b){
29187                this.addItem(b);
29188             }, this);
29189         }
29190         
29191         this.format();
29192         
29193     },
29194     
29195     addItem : function(cfg)
29196     {
29197         var item = new Roo.bootstrap.NavProgressItem(cfg);
29198         
29199         item.parentId = this.id;
29200         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29201         
29202         if(cfg.html){
29203             var top = new Roo.bootstrap.Element({
29204                 tag : 'div',
29205                 cls : 'roo-navigation-bar-text'
29206             });
29207             
29208             var bottom = new Roo.bootstrap.Element({
29209                 tag : 'div',
29210                 cls : 'roo-navigation-bar-text'
29211             });
29212             
29213             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29214             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29215             
29216             var topText = new Roo.bootstrap.Element({
29217                 tag : 'span',
29218                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29219             });
29220             
29221             var bottomText = new Roo.bootstrap.Element({
29222                 tag : 'span',
29223                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29224             });
29225             
29226             topText.onRender(top.el, null);
29227             bottomText.onRender(bottom.el, null);
29228             
29229             item.topEl = top;
29230             item.bottomEl = bottom;
29231         }
29232         
29233         this.barItems.push(item);
29234         
29235         return item;
29236     },
29237     
29238     getActive : function()
29239     {
29240         var active = false;
29241         
29242         Roo.each(this.barItems, function(v){
29243             
29244             if (!v.isActive()) {
29245                 return;
29246             }
29247             
29248             active = v;
29249             return false;
29250             
29251         });
29252         
29253         return active;
29254     },
29255     
29256     setActiveItem : function(item)
29257     {
29258         var prev = false;
29259         
29260         Roo.each(this.barItems, function(v){
29261             if (v.rid == item.rid) {
29262                 return ;
29263             }
29264             
29265             if (v.isActive()) {
29266                 v.setActive(false);
29267                 prev = v;
29268             }
29269         });
29270
29271         item.setActive(true);
29272         
29273         this.fireEvent('changed', this, item, prev);
29274     },
29275     
29276     getBarItem: function(rid)
29277     {
29278         var ret = false;
29279         
29280         Roo.each(this.barItems, function(e) {
29281             if (e.rid != rid) {
29282                 return;
29283             }
29284             
29285             ret =  e;
29286             return false;
29287         });
29288         
29289         return ret;
29290     },
29291     
29292     indexOfItem : function(item)
29293     {
29294         var index = false;
29295         
29296         Roo.each(this.barItems, function(v, i){
29297             
29298             if (v.rid != item.rid) {
29299                 return;
29300             }
29301             
29302             index = i;
29303             return false
29304         });
29305         
29306         return index;
29307     },
29308     
29309     setActiveNext : function()
29310     {
29311         var i = this.indexOfItem(this.getActive());
29312         
29313         if (i > this.barItems.length) {
29314             return;
29315         }
29316         
29317         this.setActiveItem(this.barItems[i+1]);
29318     },
29319     
29320     setActivePrev : function()
29321     {
29322         var i = this.indexOfItem(this.getActive());
29323         
29324         if (i  < 1) {
29325             return;
29326         }
29327         
29328         this.setActiveItem(this.barItems[i-1]);
29329     },
29330     
29331     format : function()
29332     {
29333         if(!this.barItems.length){
29334             return;
29335         }
29336      
29337         var width = 100 / this.barItems.length;
29338         
29339         Roo.each(this.barItems, function(i){
29340             i.el.setStyle('width', width + '%');
29341             i.topEl.el.setStyle('width', width + '%');
29342             i.bottomEl.el.setStyle('width', width + '%');
29343         }, this);
29344         
29345     }
29346     
29347 });
29348 /*
29349  * - LGPL
29350  *
29351  * Nav Progress Item
29352  * 
29353  */
29354
29355 /**
29356  * @class Roo.bootstrap.NavProgressItem
29357  * @extends Roo.bootstrap.Component
29358  * Bootstrap NavProgressItem class
29359  * @cfg {String} rid the reference id
29360  * @cfg {Boolean} active (true|false) Is item active default false
29361  * @cfg {Boolean} disabled (true|false) Is item active default false
29362  * @cfg {String} html
29363  * @cfg {String} position (top|bottom) text position default bottom
29364  * @cfg {String} icon show icon instead of number
29365  * 
29366  * @constructor
29367  * Create a new NavProgressItem
29368  * @param {Object} config The config object
29369  */
29370 Roo.bootstrap.NavProgressItem = function(config){
29371     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29372     this.addEvents({
29373         // raw events
29374         /**
29375          * @event click
29376          * The raw click event for the entire grid.
29377          * @param {Roo.bootstrap.NavProgressItem} this
29378          * @param {Roo.EventObject} e
29379          */
29380         "click" : true
29381     });
29382    
29383 };
29384
29385 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29386     
29387     rid : '',
29388     active : false,
29389     disabled : false,
29390     html : '',
29391     position : 'bottom',
29392     icon : false,
29393     
29394     getAutoCreate : function()
29395     {
29396         var iconCls = 'roo-navigation-bar-item-icon';
29397         
29398         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29399         
29400         var cfg = {
29401             tag: 'li',
29402             cls: 'roo-navigation-bar-item',
29403             cn : [
29404                 {
29405                     tag : 'i',
29406                     cls : iconCls
29407                 }
29408             ]
29409         };
29410         
29411         if(this.active){
29412             cfg.cls += ' active';
29413         }
29414         if(this.disabled){
29415             cfg.cls += ' disabled';
29416         }
29417         
29418         return cfg;
29419     },
29420     
29421     disable : function()
29422     {
29423         this.setDisabled(true);
29424     },
29425     
29426     enable : function()
29427     {
29428         this.setDisabled(false);
29429     },
29430     
29431     initEvents: function() 
29432     {
29433         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29434         
29435         this.iconEl.on('click', this.onClick, this);
29436     },
29437     
29438     onClick : function(e)
29439     {
29440         e.preventDefault();
29441         
29442         if(this.disabled){
29443             return;
29444         }
29445         
29446         if(this.fireEvent('click', this, e) === false){
29447             return;
29448         };
29449         
29450         this.parent().setActiveItem(this);
29451     },
29452     
29453     isActive: function () 
29454     {
29455         return this.active;
29456     },
29457     
29458     setActive : function(state)
29459     {
29460         if(this.active == state){
29461             return;
29462         }
29463         
29464         this.active = state;
29465         
29466         if (state) {
29467             this.el.addClass('active');
29468             return;
29469         }
29470         
29471         this.el.removeClass('active');
29472         
29473         return;
29474     },
29475     
29476     setDisabled : function(state)
29477     {
29478         if(this.disabled == state){
29479             return;
29480         }
29481         
29482         this.disabled = state;
29483         
29484         if (state) {
29485             this.el.addClass('disabled');
29486             return;
29487         }
29488         
29489         this.el.removeClass('disabled');
29490     },
29491     
29492     tooltipEl : function()
29493     {
29494         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29495     }
29496 });
29497  
29498
29499  /*
29500  * - LGPL
29501  *
29502  * FieldLabel
29503  * 
29504  */
29505
29506 /**
29507  * @class Roo.bootstrap.FieldLabel
29508  * @extends Roo.bootstrap.Component
29509  * Bootstrap FieldLabel class
29510  * @cfg {String} html contents of the element
29511  * @cfg {String} tag tag of the element default label
29512  * @cfg {String} cls class of the element
29513  * @cfg {String} target label target 
29514  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29515  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29516  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29517  * @cfg {String} iconTooltip default "This field is required"
29518  * 
29519  * @constructor
29520  * Create a new FieldLabel
29521  * @param {Object} config The config object
29522  */
29523
29524 Roo.bootstrap.FieldLabel = function(config){
29525     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29526     
29527     this.addEvents({
29528             /**
29529              * @event invalid
29530              * Fires after the field has been marked as invalid.
29531              * @param {Roo.form.FieldLabel} this
29532              * @param {String} msg The validation message
29533              */
29534             invalid : true,
29535             /**
29536              * @event valid
29537              * Fires after the field has been validated with no errors.
29538              * @param {Roo.form.FieldLabel} this
29539              */
29540             valid : true
29541         });
29542 };
29543
29544 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29545     
29546     tag: 'label',
29547     cls: '',
29548     html: '',
29549     target: '',
29550     allowBlank : true,
29551     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29552     validClass : 'text-success fa fa-lg fa-check',
29553     iconTooltip : 'This field is required',
29554     
29555     getAutoCreate : function(){
29556         
29557         var cfg = {
29558             tag : this.tag,
29559             cls : 'roo-bootstrap-field-label ' + this.cls,
29560             for : this.target,
29561             cn : [
29562                 {
29563                     tag : 'i',
29564                     cls : '',
29565                     tooltip : this.iconTooltip
29566                 },
29567                 {
29568                     tag : 'span',
29569                     html : this.html
29570                 }
29571             ] 
29572         };
29573         
29574         return cfg;
29575     },
29576     
29577     initEvents: function() 
29578     {
29579         Roo.bootstrap.Element.superclass.initEvents.call(this);
29580         
29581         this.iconEl = this.el.select('i', true).first();
29582         
29583         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29584         
29585         Roo.bootstrap.FieldLabel.register(this);
29586     },
29587     
29588     /**
29589      * Mark this field as valid
29590      */
29591     markValid : function()
29592     {
29593         this.iconEl.show();
29594         
29595         this.iconEl.removeClass(this.invalidClass);
29596         
29597         this.iconEl.addClass(this.validClass);
29598         
29599         this.fireEvent('valid', this);
29600     },
29601     
29602     /**
29603      * Mark this field as invalid
29604      * @param {String} msg The validation message
29605      */
29606     markInvalid : function(msg)
29607     {
29608         this.iconEl.show();
29609         
29610         this.iconEl.removeClass(this.validClass);
29611         
29612         this.iconEl.addClass(this.invalidClass);
29613         
29614         this.fireEvent('invalid', this, msg);
29615     }
29616     
29617    
29618 });
29619
29620 Roo.apply(Roo.bootstrap.FieldLabel, {
29621     
29622     groups: {},
29623     
29624      /**
29625     * register a FieldLabel Group
29626     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29627     */
29628     register : function(label)
29629     {
29630         if(this.groups.hasOwnProperty(label.target)){
29631             return;
29632         }
29633      
29634         this.groups[label.target] = label;
29635         
29636     },
29637     /**
29638     * fetch a FieldLabel Group based on the target
29639     * @param {string} target
29640     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29641     */
29642     get: function(target) {
29643         if (typeof(this.groups[target]) == 'undefined') {
29644             return false;
29645         }
29646         
29647         return this.groups[target] ;
29648     }
29649 });
29650
29651  
29652
29653  /*
29654  * - LGPL
29655  *
29656  * page DateSplitField.
29657  * 
29658  */
29659
29660
29661 /**
29662  * @class Roo.bootstrap.DateSplitField
29663  * @extends Roo.bootstrap.Component
29664  * Bootstrap DateSplitField class
29665  * @cfg {string} fieldLabel - the label associated
29666  * @cfg {Number} labelWidth set the width of label (0-12)
29667  * @cfg {String} labelAlign (top|left)
29668  * @cfg {Boolean} dayAllowBlank (true|false) default false
29669  * @cfg {Boolean} monthAllowBlank (true|false) default false
29670  * @cfg {Boolean} yearAllowBlank (true|false) default false
29671  * @cfg {string} dayPlaceholder 
29672  * @cfg {string} monthPlaceholder
29673  * @cfg {string} yearPlaceholder
29674  * @cfg {string} dayFormat default 'd'
29675  * @cfg {string} monthFormat default 'm'
29676  * @cfg {string} yearFormat default 'Y'
29677  * @cfg {Number} labellg set the width of label (1-12)
29678  * @cfg {Number} labelmd set the width of label (1-12)
29679  * @cfg {Number} labelsm set the width of label (1-12)
29680  * @cfg {Number} labelxs set the width of label (1-12)
29681
29682  *     
29683  * @constructor
29684  * Create a new DateSplitField
29685  * @param {Object} config The config object
29686  */
29687
29688 Roo.bootstrap.DateSplitField = function(config){
29689     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29690     
29691     this.addEvents({
29692         // raw events
29693          /**
29694          * @event years
29695          * getting the data of years
29696          * @param {Roo.bootstrap.DateSplitField} this
29697          * @param {Object} years
29698          */
29699         "years" : true,
29700         /**
29701          * @event days
29702          * getting the data of days
29703          * @param {Roo.bootstrap.DateSplitField} this
29704          * @param {Object} days
29705          */
29706         "days" : true,
29707         /**
29708          * @event invalid
29709          * Fires after the field has been marked as invalid.
29710          * @param {Roo.form.Field} this
29711          * @param {String} msg The validation message
29712          */
29713         invalid : true,
29714        /**
29715          * @event valid
29716          * Fires after the field has been validated with no errors.
29717          * @param {Roo.form.Field} this
29718          */
29719         valid : true
29720     });
29721 };
29722
29723 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29724     
29725     fieldLabel : '',
29726     labelAlign : 'top',
29727     labelWidth : 3,
29728     dayAllowBlank : false,
29729     monthAllowBlank : false,
29730     yearAllowBlank : false,
29731     dayPlaceholder : '',
29732     monthPlaceholder : '',
29733     yearPlaceholder : '',
29734     dayFormat : 'd',
29735     monthFormat : 'm',
29736     yearFormat : 'Y',
29737     isFormField : true,
29738     labellg : 0,
29739     labelmd : 0,
29740     labelsm : 0,
29741     labelxs : 0,
29742     
29743     getAutoCreate : function()
29744     {
29745         var cfg = {
29746             tag : 'div',
29747             cls : 'row roo-date-split-field-group',
29748             cn : [
29749                 {
29750                     tag : 'input',
29751                     type : 'hidden',
29752                     cls : 'form-hidden-field roo-date-split-field-group-value',
29753                     name : this.name
29754                 }
29755             ]
29756         };
29757         
29758         var labelCls = 'col-md-12';
29759         var contentCls = 'col-md-4';
29760         
29761         if(this.fieldLabel){
29762             
29763             var label = {
29764                 tag : 'div',
29765                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29766                 cn : [
29767                     {
29768                         tag : 'label',
29769                         html : this.fieldLabel
29770                     }
29771                 ]
29772             };
29773             
29774             if(this.labelAlign == 'left'){
29775             
29776                 if(this.labelWidth > 12){
29777                     label.style = "width: " + this.labelWidth + 'px';
29778                 }
29779
29780                 if(this.labelWidth < 13 && this.labelmd == 0){
29781                     this.labelmd = this.labelWidth;
29782                 }
29783
29784                 if(this.labellg > 0){
29785                     labelCls = ' col-lg-' + this.labellg;
29786                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29787                 }
29788
29789                 if(this.labelmd > 0){
29790                     labelCls = ' col-md-' + this.labelmd;
29791                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29792                 }
29793
29794                 if(this.labelsm > 0){
29795                     labelCls = ' col-sm-' + this.labelsm;
29796                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29797                 }
29798
29799                 if(this.labelxs > 0){
29800                     labelCls = ' col-xs-' + this.labelxs;
29801                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29802                 }
29803             }
29804             
29805             label.cls += ' ' + labelCls;
29806             
29807             cfg.cn.push(label);
29808         }
29809         
29810         Roo.each(['day', 'month', 'year'], function(t){
29811             cfg.cn.push({
29812                 tag : 'div',
29813                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29814             });
29815         }, this);
29816         
29817         return cfg;
29818     },
29819     
29820     inputEl: function ()
29821     {
29822         return this.el.select('.roo-date-split-field-group-value', true).first();
29823     },
29824     
29825     onRender : function(ct, position) 
29826     {
29827         var _this = this;
29828         
29829         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29830         
29831         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29832         
29833         this.dayField = new Roo.bootstrap.ComboBox({
29834             allowBlank : this.dayAllowBlank,
29835             alwaysQuery : true,
29836             displayField : 'value',
29837             editable : false,
29838             fieldLabel : '',
29839             forceSelection : true,
29840             mode : 'local',
29841             placeholder : this.dayPlaceholder,
29842             selectOnFocus : true,
29843             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29844             triggerAction : 'all',
29845             typeAhead : true,
29846             valueField : 'value',
29847             store : new Roo.data.SimpleStore({
29848                 data : (function() {    
29849                     var days = [];
29850                     _this.fireEvent('days', _this, days);
29851                     return days;
29852                 })(),
29853                 fields : [ 'value' ]
29854             }),
29855             listeners : {
29856                 select : function (_self, record, index)
29857                 {
29858                     _this.setValue(_this.getValue());
29859                 }
29860             }
29861         });
29862
29863         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29864         
29865         this.monthField = new Roo.bootstrap.MonthField({
29866             after : '<i class=\"fa fa-calendar\"></i>',
29867             allowBlank : this.monthAllowBlank,
29868             placeholder : this.monthPlaceholder,
29869             readOnly : true,
29870             listeners : {
29871                 render : function (_self)
29872                 {
29873                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29874                         e.preventDefault();
29875                         _self.focus();
29876                     });
29877                 },
29878                 select : function (_self, oldvalue, newvalue)
29879                 {
29880                     _this.setValue(_this.getValue());
29881                 }
29882             }
29883         });
29884         
29885         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29886         
29887         this.yearField = new Roo.bootstrap.ComboBox({
29888             allowBlank : this.yearAllowBlank,
29889             alwaysQuery : true,
29890             displayField : 'value',
29891             editable : false,
29892             fieldLabel : '',
29893             forceSelection : true,
29894             mode : 'local',
29895             placeholder : this.yearPlaceholder,
29896             selectOnFocus : true,
29897             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29898             triggerAction : 'all',
29899             typeAhead : true,
29900             valueField : 'value',
29901             store : new Roo.data.SimpleStore({
29902                 data : (function() {
29903                     var years = [];
29904                     _this.fireEvent('years', _this, years);
29905                     return years;
29906                 })(),
29907                 fields : [ 'value' ]
29908             }),
29909             listeners : {
29910                 select : function (_self, record, index)
29911                 {
29912                     _this.setValue(_this.getValue());
29913                 }
29914             }
29915         });
29916
29917         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29918     },
29919     
29920     setValue : function(v, format)
29921     {
29922         this.inputEl.dom.value = v;
29923         
29924         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29925         
29926         var d = Date.parseDate(v, f);
29927         
29928         if(!d){
29929             this.validate();
29930             return;
29931         }
29932         
29933         this.setDay(d.format(this.dayFormat));
29934         this.setMonth(d.format(this.monthFormat));
29935         this.setYear(d.format(this.yearFormat));
29936         
29937         this.validate();
29938         
29939         return;
29940     },
29941     
29942     setDay : function(v)
29943     {
29944         this.dayField.setValue(v);
29945         this.inputEl.dom.value = this.getValue();
29946         this.validate();
29947         return;
29948     },
29949     
29950     setMonth : function(v)
29951     {
29952         this.monthField.setValue(v, true);
29953         this.inputEl.dom.value = this.getValue();
29954         this.validate();
29955         return;
29956     },
29957     
29958     setYear : function(v)
29959     {
29960         this.yearField.setValue(v);
29961         this.inputEl.dom.value = this.getValue();
29962         this.validate();
29963         return;
29964     },
29965     
29966     getDay : function()
29967     {
29968         return this.dayField.getValue();
29969     },
29970     
29971     getMonth : function()
29972     {
29973         return this.monthField.getValue();
29974     },
29975     
29976     getYear : function()
29977     {
29978         return this.yearField.getValue();
29979     },
29980     
29981     getValue : function()
29982     {
29983         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29984         
29985         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29986         
29987         return date;
29988     },
29989     
29990     reset : function()
29991     {
29992         this.setDay('');
29993         this.setMonth('');
29994         this.setYear('');
29995         this.inputEl.dom.value = '';
29996         this.validate();
29997         return;
29998     },
29999     
30000     validate : function()
30001     {
30002         var d = this.dayField.validate();
30003         var m = this.monthField.validate();
30004         var y = this.yearField.validate();
30005         
30006         var valid = true;
30007         
30008         if(
30009                 (!this.dayAllowBlank && !d) ||
30010                 (!this.monthAllowBlank && !m) ||
30011                 (!this.yearAllowBlank && !y)
30012         ){
30013             valid = false;
30014         }
30015         
30016         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30017             return valid;
30018         }
30019         
30020         if(valid){
30021             this.markValid();
30022             return valid;
30023         }
30024         
30025         this.markInvalid();
30026         
30027         return valid;
30028     },
30029     
30030     markValid : function()
30031     {
30032         
30033         var label = this.el.select('label', true).first();
30034         var icon = this.el.select('i.fa-star', true).first();
30035
30036         if(label && icon){
30037             icon.remove();
30038         }
30039         
30040         this.fireEvent('valid', this);
30041     },
30042     
30043      /**
30044      * Mark this field as invalid
30045      * @param {String} msg The validation message
30046      */
30047     markInvalid : function(msg)
30048     {
30049         
30050         var label = this.el.select('label', true).first();
30051         var icon = this.el.select('i.fa-star', true).first();
30052
30053         if(label && !icon){
30054             this.el.select('.roo-date-split-field-label', true).createChild({
30055                 tag : 'i',
30056                 cls : 'text-danger fa fa-lg fa-star',
30057                 tooltip : 'This field is required',
30058                 style : 'margin-right:5px;'
30059             }, label, true);
30060         }
30061         
30062         this.fireEvent('invalid', this, msg);
30063     },
30064     
30065     clearInvalid : function()
30066     {
30067         var label = this.el.select('label', true).first();
30068         var icon = this.el.select('i.fa-star', true).first();
30069
30070         if(label && icon){
30071             icon.remove();
30072         }
30073         
30074         this.fireEvent('valid', this);
30075     },
30076     
30077     getName: function()
30078     {
30079         return this.name;
30080     }
30081     
30082 });
30083
30084  /**
30085  *
30086  * This is based on 
30087  * http://masonry.desandro.com
30088  *
30089  * The idea is to render all the bricks based on vertical width...
30090  *
30091  * The original code extends 'outlayer' - we might need to use that....
30092  * 
30093  */
30094
30095
30096 /**
30097  * @class Roo.bootstrap.LayoutMasonry
30098  * @extends Roo.bootstrap.Component
30099  * Bootstrap Layout Masonry class
30100  * 
30101  * @constructor
30102  * Create a new Element
30103  * @param {Object} config The config object
30104  */
30105
30106 Roo.bootstrap.LayoutMasonry = function(config){
30107     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30108     
30109     this.bricks = [];
30110     
30111     this.addEvents({
30112         // raw events
30113         /**
30114          * @event layout
30115          * Fire after layout the items
30116          * @param {Roo.bootstrap.LayoutMasonry} this
30117          * @param {Roo.EventObject} e
30118          */
30119         "layout" : true
30120     });
30121     
30122 };
30123
30124 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30125     
30126     /**
30127      * @cfg {Boolean} isLayoutInstant = no animation?
30128      */   
30129     isLayoutInstant : false, // needed?
30130    
30131     /**
30132      * @cfg {Number} boxWidth  width of the columns
30133      */   
30134     boxWidth : 450,
30135     
30136       /**
30137      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30138      */   
30139     boxHeight : 0,
30140     
30141     /**
30142      * @cfg {Number} padWidth padding below box..
30143      */   
30144     padWidth : 10, 
30145     
30146     /**
30147      * @cfg {Number} gutter gutter width..
30148      */   
30149     gutter : 10,
30150     
30151      /**
30152      * @cfg {Number} maxCols maximum number of columns
30153      */   
30154     
30155     maxCols: 0,
30156     
30157     /**
30158      * @cfg {Boolean} isAutoInitial defalut true
30159      */   
30160     isAutoInitial : true, 
30161     
30162     containerWidth: 0,
30163     
30164     /**
30165      * @cfg {Boolean} isHorizontal defalut false
30166      */   
30167     isHorizontal : false, 
30168
30169     currentSize : null,
30170     
30171     tag: 'div',
30172     
30173     cls: '',
30174     
30175     bricks: null, //CompositeElement
30176     
30177     cols : 1,
30178     
30179     _isLayoutInited : false,
30180     
30181 //    isAlternative : false, // only use for vertical layout...
30182     
30183     /**
30184      * @cfg {Number} alternativePadWidth padding below box..
30185      */   
30186     alternativePadWidth : 50, 
30187     
30188     getAutoCreate : function(){
30189         
30190         var cfg = {
30191             tag: this.tag,
30192             cls: 'blog-masonary-wrapper ' + this.cls,
30193             cn : {
30194                 cls : 'mas-boxes masonary'
30195             }
30196         };
30197         
30198         return cfg;
30199     },
30200     
30201     getChildContainer: function( )
30202     {
30203         if (this.boxesEl) {
30204             return this.boxesEl;
30205         }
30206         
30207         this.boxesEl = this.el.select('.mas-boxes').first();
30208         
30209         return this.boxesEl;
30210     },
30211     
30212     
30213     initEvents : function()
30214     {
30215         var _this = this;
30216         
30217         if(this.isAutoInitial){
30218             Roo.log('hook children rendered');
30219             this.on('childrenrendered', function() {
30220                 Roo.log('children rendered');
30221                 _this.initial();
30222             } ,this);
30223         }
30224     },
30225     
30226     initial : function()
30227     {
30228         this.currentSize = this.el.getBox(true);
30229         
30230         Roo.EventManager.onWindowResize(this.resize, this); 
30231
30232         if(!this.isAutoInitial){
30233             this.layout();
30234             return;
30235         }
30236         
30237         this.layout();
30238         
30239         return;
30240         //this.layout.defer(500,this);
30241         
30242     },
30243     
30244     resize : function()
30245     {
30246         var cs = this.el.getBox(true);
30247         
30248         if (
30249                 this.currentSize.width == cs.width && 
30250                 this.currentSize.x == cs.x && 
30251                 this.currentSize.height == cs.height && 
30252                 this.currentSize.y == cs.y 
30253         ) {
30254             Roo.log("no change in with or X or Y");
30255             return;
30256         }
30257         
30258         this.currentSize = cs;
30259         
30260         this.layout();
30261         
30262     },
30263     
30264     layout : function()
30265     {   
30266         this._resetLayout();
30267         
30268         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30269         
30270         this.layoutItems( isInstant );
30271       
30272         this._isLayoutInited = true;
30273         
30274         this.fireEvent('layout', this);
30275         
30276     },
30277     
30278     _resetLayout : function()
30279     {
30280         if(this.isHorizontal){
30281             this.horizontalMeasureColumns();
30282             return;
30283         }
30284         
30285         this.verticalMeasureColumns();
30286         
30287     },
30288     
30289     verticalMeasureColumns : function()
30290     {
30291         this.getContainerWidth();
30292         
30293 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30294 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30295 //            return;
30296 //        }
30297         
30298         var boxWidth = this.boxWidth + this.padWidth;
30299         
30300         if(this.containerWidth < this.boxWidth){
30301             boxWidth = this.containerWidth
30302         }
30303         
30304         var containerWidth = this.containerWidth;
30305         
30306         var cols = Math.floor(containerWidth / boxWidth);
30307         
30308         this.cols = Math.max( cols, 1 );
30309         
30310         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30311         
30312         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30313         
30314         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30315         
30316         this.colWidth = boxWidth + avail - this.padWidth;
30317         
30318         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
30319         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30320     },
30321     
30322     horizontalMeasureColumns : function()
30323     {
30324         this.getContainerWidth();
30325         
30326         var boxWidth = this.boxWidth;
30327         
30328         if(this.containerWidth < boxWidth){
30329             boxWidth = this.containerWidth;
30330         }
30331         
30332         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30333         
30334         this.el.setHeight(boxWidth);
30335         
30336     },
30337     
30338     getContainerWidth : function()
30339     {
30340         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30341     },
30342     
30343     layoutItems : function( isInstant )
30344     {
30345         Roo.log(this.bricks);
30346         
30347         var items = Roo.apply([], this.bricks);
30348         
30349         if(this.isHorizontal){
30350             this._horizontalLayoutItems( items , isInstant );
30351             return;
30352         }
30353         
30354 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30355 //            this._verticalAlternativeLayoutItems( items , isInstant );
30356 //            return;
30357 //        }
30358         
30359         this._verticalLayoutItems( items , isInstant );
30360         
30361     },
30362     
30363     _verticalLayoutItems : function ( items , isInstant)
30364     {
30365         if ( !items || !items.length ) {
30366             return;
30367         }
30368         
30369         var standard = [
30370             ['xs', 'xs', 'xs', 'tall'],
30371             ['xs', 'xs', 'tall'],
30372             ['xs', 'xs', 'sm'],
30373             ['xs', 'xs', 'xs'],
30374             ['xs', 'tall'],
30375             ['xs', 'sm'],
30376             ['xs', 'xs'],
30377             ['xs'],
30378             
30379             ['sm', 'xs', 'xs'],
30380             ['sm', 'xs'],
30381             ['sm'],
30382             
30383             ['tall', 'xs', 'xs', 'xs'],
30384             ['tall', 'xs', 'xs'],
30385             ['tall', 'xs'],
30386             ['tall']
30387             
30388         ];
30389         
30390         var queue = [];
30391         
30392         var boxes = [];
30393         
30394         var box = [];
30395         
30396         Roo.each(items, function(item, k){
30397             
30398             switch (item.size) {
30399                 // these layouts take up a full box,
30400                 case 'md' :
30401                 case 'md-left' :
30402                 case 'md-right' :
30403                 case 'wide' :
30404                     
30405                     if(box.length){
30406                         boxes.push(box);
30407                         box = [];
30408                     }
30409                     
30410                     boxes.push([item]);
30411                     
30412                     break;
30413                     
30414                 case 'xs' :
30415                 case 'sm' :
30416                 case 'tall' :
30417                     
30418                     box.push(item);
30419                     
30420                     break;
30421                 default :
30422                     break;
30423                     
30424             }
30425             
30426         }, this);
30427         
30428         if(box.length){
30429             boxes.push(box);
30430             box = [];
30431         }
30432         
30433         var filterPattern = function(box, length)
30434         {
30435             if(!box.length){
30436                 return;
30437             }
30438             
30439             var match = false;
30440             
30441             var pattern = box.slice(0, length);
30442             
30443             var format = [];
30444             
30445             Roo.each(pattern, function(i){
30446                 format.push(i.size);
30447             }, this);
30448             
30449             Roo.each(standard, function(s){
30450                 
30451                 if(String(s) != String(format)){
30452                     return;
30453                 }
30454                 
30455                 match = true;
30456                 return false;
30457                 
30458             }, this);
30459             
30460             if(!match && length == 1){
30461                 return;
30462             }
30463             
30464             if(!match){
30465                 filterPattern(box, length - 1);
30466                 return;
30467             }
30468                 
30469             queue.push(pattern);
30470
30471             box = box.slice(length, box.length);
30472
30473             filterPattern(box, 4);
30474
30475             return;
30476             
30477         }
30478         
30479         Roo.each(boxes, function(box, k){
30480             
30481             if(!box.length){
30482                 return;
30483             }
30484             
30485             if(box.length == 1){
30486                 queue.push(box);
30487                 return;
30488             }
30489             
30490             filterPattern(box, 4);
30491             
30492         }, this);
30493         
30494         this._processVerticalLayoutQueue( queue, isInstant );
30495         
30496     },
30497     
30498 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30499 //    {
30500 //        if ( !items || !items.length ) {
30501 //            return;
30502 //        }
30503 //
30504 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30505 //        
30506 //    },
30507     
30508     _horizontalLayoutItems : function ( items , isInstant)
30509     {
30510         if ( !items || !items.length || items.length < 3) {
30511             return;
30512         }
30513         
30514         items.reverse();
30515         
30516         var eItems = items.slice(0, 3);
30517         
30518         items = items.slice(3, items.length);
30519         
30520         var standard = [
30521             ['xs', 'xs', 'xs', 'wide'],
30522             ['xs', 'xs', 'wide'],
30523             ['xs', 'xs', 'sm'],
30524             ['xs', 'xs', 'xs'],
30525             ['xs', 'wide'],
30526             ['xs', 'sm'],
30527             ['xs', 'xs'],
30528             ['xs'],
30529             
30530             ['sm', 'xs', 'xs'],
30531             ['sm', 'xs'],
30532             ['sm'],
30533             
30534             ['wide', 'xs', 'xs', 'xs'],
30535             ['wide', 'xs', 'xs'],
30536             ['wide', 'xs'],
30537             ['wide'],
30538             
30539             ['wide-thin']
30540         ];
30541         
30542         var queue = [];
30543         
30544         var boxes = [];
30545         
30546         var box = [];
30547         
30548         Roo.each(items, function(item, k){
30549             
30550             switch (item.size) {
30551                 case 'md' :
30552                 case 'md-left' :
30553                 case 'md-right' :
30554                 case 'tall' :
30555                     
30556                     if(box.length){
30557                         boxes.push(box);
30558                         box = [];
30559                     }
30560                     
30561                     boxes.push([item]);
30562                     
30563                     break;
30564                     
30565                 case 'xs' :
30566                 case 'sm' :
30567                 case 'wide' :
30568                 case 'wide-thin' :
30569                     
30570                     box.push(item);
30571                     
30572                     break;
30573                 default :
30574                     break;
30575                     
30576             }
30577             
30578         }, this);
30579         
30580         if(box.length){
30581             boxes.push(box);
30582             box = [];
30583         }
30584         
30585         var filterPattern = function(box, length)
30586         {
30587             if(!box.length){
30588                 return;
30589             }
30590             
30591             var match = false;
30592             
30593             var pattern = box.slice(0, length);
30594             
30595             var format = [];
30596             
30597             Roo.each(pattern, function(i){
30598                 format.push(i.size);
30599             }, this);
30600             
30601             Roo.each(standard, function(s){
30602                 
30603                 if(String(s) != String(format)){
30604                     return;
30605                 }
30606                 
30607                 match = true;
30608                 return false;
30609                 
30610             }, this);
30611             
30612             if(!match && length == 1){
30613                 return;
30614             }
30615             
30616             if(!match){
30617                 filterPattern(box, length - 1);
30618                 return;
30619             }
30620                 
30621             queue.push(pattern);
30622
30623             box = box.slice(length, box.length);
30624
30625             filterPattern(box, 4);
30626
30627             return;
30628             
30629         }
30630         
30631         Roo.each(boxes, function(box, k){
30632             
30633             if(!box.length){
30634                 return;
30635             }
30636             
30637             if(box.length == 1){
30638                 queue.push(box);
30639                 return;
30640             }
30641             
30642             filterPattern(box, 4);
30643             
30644         }, this);
30645         
30646         
30647         var prune = [];
30648         
30649         var pos = this.el.getBox(true);
30650         
30651         var minX = pos.x;
30652         
30653         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30654         
30655         var hit_end = false;
30656         
30657         Roo.each(queue, function(box){
30658             
30659             if(hit_end){
30660                 
30661                 Roo.each(box, function(b){
30662                 
30663                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30664                     b.el.hide();
30665
30666                 }, this);
30667
30668                 return;
30669             }
30670             
30671             var mx = 0;
30672             
30673             Roo.each(box, function(b){
30674                 
30675                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30676                 b.el.show();
30677
30678                 mx = Math.max(mx, b.x);
30679                 
30680             }, this);
30681             
30682             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30683             
30684             if(maxX < minX){
30685                 
30686                 Roo.each(box, function(b){
30687                 
30688                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30689                     b.el.hide();
30690                     
30691                 }, this);
30692                 
30693                 hit_end = true;
30694                 
30695                 return;
30696             }
30697             
30698             prune.push(box);
30699             
30700         }, this);
30701         
30702         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30703     },
30704     
30705     /** Sets position of item in DOM
30706     * @param {Element} item
30707     * @param {Number} x - horizontal position
30708     * @param {Number} y - vertical position
30709     * @param {Boolean} isInstant - disables transitions
30710     */
30711     _processVerticalLayoutQueue : function( queue, isInstant )
30712     {
30713         var pos = this.el.getBox(true);
30714         var x = pos.x;
30715         var y = pos.y;
30716         var maxY = [];
30717         
30718         for (var i = 0; i < this.cols; i++){
30719             maxY[i] = pos.y;
30720         }
30721         
30722         Roo.each(queue, function(box, k){
30723             
30724             var col = k % this.cols;
30725             
30726             Roo.each(box, function(b,kk){
30727                 
30728                 b.el.position('absolute');
30729                 
30730                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30731                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30732                 
30733                 if(b.size == 'md-left' || b.size == 'md-right'){
30734                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30735                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30736                 }
30737                 
30738                 b.el.setWidth(width);
30739                 b.el.setHeight(height);
30740                 // iframe?
30741                 b.el.select('iframe',true).setSize(width,height);
30742                 
30743             }, this);
30744             
30745             for (var i = 0; i < this.cols; i++){
30746                 
30747                 if(maxY[i] < maxY[col]){
30748                     col = i;
30749                     continue;
30750                 }
30751                 
30752                 col = Math.min(col, i);
30753                 
30754             }
30755             
30756             x = pos.x + col * (this.colWidth + this.padWidth);
30757             
30758             y = maxY[col];
30759             
30760             var positions = [];
30761             
30762             switch (box.length){
30763                 case 1 :
30764                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30765                     break;
30766                 case 2 :
30767                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30768                     break;
30769                 case 3 :
30770                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30771                     break;
30772                 case 4 :
30773                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30774                     break;
30775                 default :
30776                     break;
30777             }
30778             
30779             Roo.each(box, function(b,kk){
30780                 
30781                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30782                 
30783                 var sz = b.el.getSize();
30784                 
30785                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30786                 
30787             }, this);
30788             
30789         }, this);
30790         
30791         var mY = 0;
30792         
30793         for (var i = 0; i < this.cols; i++){
30794             mY = Math.max(mY, maxY[i]);
30795         }
30796         
30797         this.el.setHeight(mY - pos.y);
30798         
30799     },
30800     
30801 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30802 //    {
30803 //        var pos = this.el.getBox(true);
30804 //        var x = pos.x;
30805 //        var y = pos.y;
30806 //        var maxX = pos.right;
30807 //        
30808 //        var maxHeight = 0;
30809 //        
30810 //        Roo.each(items, function(item, k){
30811 //            
30812 //            var c = k % 2;
30813 //            
30814 //            item.el.position('absolute');
30815 //                
30816 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30817 //
30818 //            item.el.setWidth(width);
30819 //
30820 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30821 //
30822 //            item.el.setHeight(height);
30823 //            
30824 //            if(c == 0){
30825 //                item.el.setXY([x, y], isInstant ? false : true);
30826 //            } else {
30827 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30828 //            }
30829 //            
30830 //            y = y + height + this.alternativePadWidth;
30831 //            
30832 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30833 //            
30834 //        }, this);
30835 //        
30836 //        this.el.setHeight(maxHeight);
30837 //        
30838 //    },
30839     
30840     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30841     {
30842         var pos = this.el.getBox(true);
30843         
30844         var minX = pos.x;
30845         var minY = pos.y;
30846         
30847         var maxX = pos.right;
30848         
30849         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30850         
30851         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30852         
30853         Roo.each(queue, function(box, k){
30854             
30855             Roo.each(box, function(b, kk){
30856                 
30857                 b.el.position('absolute');
30858                 
30859                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30860                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30861                 
30862                 if(b.size == 'md-left' || b.size == 'md-right'){
30863                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30864                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30865                 }
30866                 
30867                 b.el.setWidth(width);
30868                 b.el.setHeight(height);
30869                 
30870             }, this);
30871             
30872             if(!box.length){
30873                 return;
30874             }
30875             
30876             var positions = [];
30877             
30878             switch (box.length){
30879                 case 1 :
30880                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30881                     break;
30882                 case 2 :
30883                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30884                     break;
30885                 case 3 :
30886                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30887                     break;
30888                 case 4 :
30889                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30890                     break;
30891                 default :
30892                     break;
30893             }
30894             
30895             Roo.each(box, function(b,kk){
30896                 
30897                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30898                 
30899                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30900                 
30901             }, this);
30902             
30903         }, this);
30904         
30905     },
30906     
30907     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30908     {
30909         Roo.each(eItems, function(b,k){
30910             
30911             b.size = (k == 0) ? 'sm' : 'xs';
30912             b.x = (k == 0) ? 2 : 1;
30913             b.y = (k == 0) ? 2 : 1;
30914             
30915             b.el.position('absolute');
30916             
30917             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30918                 
30919             b.el.setWidth(width);
30920             
30921             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30922             
30923             b.el.setHeight(height);
30924             
30925         }, this);
30926
30927         var positions = [];
30928         
30929         positions.push({
30930             x : maxX - this.unitWidth * 2 - this.gutter,
30931             y : minY
30932         });
30933         
30934         positions.push({
30935             x : maxX - this.unitWidth,
30936             y : minY + (this.unitWidth + this.gutter) * 2
30937         });
30938         
30939         positions.push({
30940             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30941             y : minY
30942         });
30943         
30944         Roo.each(eItems, function(b,k){
30945             
30946             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30947
30948         }, this);
30949         
30950     },
30951     
30952     getVerticalOneBoxColPositions : function(x, y, box)
30953     {
30954         var pos = [];
30955         
30956         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30957         
30958         if(box[0].size == 'md-left'){
30959             rand = 0;
30960         }
30961         
30962         if(box[0].size == 'md-right'){
30963             rand = 1;
30964         }
30965         
30966         pos.push({
30967             x : x + (this.unitWidth + this.gutter) * rand,
30968             y : y
30969         });
30970         
30971         return pos;
30972     },
30973     
30974     getVerticalTwoBoxColPositions : function(x, y, box)
30975     {
30976         var pos = [];
30977         
30978         if(box[0].size == 'xs'){
30979             
30980             pos.push({
30981                 x : x,
30982                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30983             });
30984
30985             pos.push({
30986                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30987                 y : y
30988             });
30989             
30990             return pos;
30991             
30992         }
30993         
30994         pos.push({
30995             x : x,
30996             y : y
30997         });
30998
30999         pos.push({
31000             x : x + (this.unitWidth + this.gutter) * 2,
31001             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31002         });
31003         
31004         return pos;
31005         
31006     },
31007     
31008     getVerticalThreeBoxColPositions : function(x, y, box)
31009     {
31010         var pos = [];
31011         
31012         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31013             
31014             pos.push({
31015                 x : x,
31016                 y : y
31017             });
31018
31019             pos.push({
31020                 x : x + (this.unitWidth + this.gutter) * 1,
31021                 y : y
31022             });
31023             
31024             pos.push({
31025                 x : x + (this.unitWidth + this.gutter) * 2,
31026                 y : y
31027             });
31028             
31029             return pos;
31030             
31031         }
31032         
31033         if(box[0].size == 'xs' && box[1].size == 'xs'){
31034             
31035             pos.push({
31036                 x : x,
31037                 y : y
31038             });
31039
31040             pos.push({
31041                 x : x,
31042                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31043             });
31044             
31045             pos.push({
31046                 x : x + (this.unitWidth + this.gutter) * 1,
31047                 y : y
31048             });
31049             
31050             return pos;
31051             
31052         }
31053         
31054         pos.push({
31055             x : x,
31056             y : y
31057         });
31058
31059         pos.push({
31060             x : x + (this.unitWidth + this.gutter) * 2,
31061             y : y
31062         });
31063
31064         pos.push({
31065             x : x + (this.unitWidth + this.gutter) * 2,
31066             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31067         });
31068             
31069         return pos;
31070         
31071     },
31072     
31073     getVerticalFourBoxColPositions : function(x, y, box)
31074     {
31075         var pos = [];
31076         
31077         if(box[0].size == 'xs'){
31078             
31079             pos.push({
31080                 x : x,
31081                 y : y
31082             });
31083
31084             pos.push({
31085                 x : x,
31086                 y : y + (this.unitHeight + this.gutter) * 1
31087             });
31088             
31089             pos.push({
31090                 x : x,
31091                 y : y + (this.unitHeight + this.gutter) * 2
31092             });
31093             
31094             pos.push({
31095                 x : x + (this.unitWidth + this.gutter) * 1,
31096                 y : y
31097             });
31098             
31099             return pos;
31100             
31101         }
31102         
31103         pos.push({
31104             x : x,
31105             y : y
31106         });
31107
31108         pos.push({
31109             x : x + (this.unitWidth + this.gutter) * 2,
31110             y : y
31111         });
31112
31113         pos.push({
31114             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31115             y : y + (this.unitHeight + this.gutter) * 1
31116         });
31117
31118         pos.push({
31119             x : x + (this.unitWidth + this.gutter) * 2,
31120             y : y + (this.unitWidth + this.gutter) * 2
31121         });
31122
31123         return pos;
31124         
31125     },
31126     
31127     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31128     {
31129         var pos = [];
31130         
31131         if(box[0].size == 'md-left'){
31132             pos.push({
31133                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31134                 y : minY
31135             });
31136             
31137             return pos;
31138         }
31139         
31140         if(box[0].size == 'md-right'){
31141             pos.push({
31142                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31143                 y : minY + (this.unitWidth + this.gutter) * 1
31144             });
31145             
31146             return pos;
31147         }
31148         
31149         var rand = Math.floor(Math.random() * (4 - box[0].y));
31150         
31151         pos.push({
31152             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31153             y : minY + (this.unitWidth + this.gutter) * rand
31154         });
31155         
31156         return pos;
31157         
31158     },
31159     
31160     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31161     {
31162         var pos = [];
31163         
31164         if(box[0].size == 'xs'){
31165             
31166             pos.push({
31167                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31168                 y : minY
31169             });
31170
31171             pos.push({
31172                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31173                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31174             });
31175             
31176             return pos;
31177             
31178         }
31179         
31180         pos.push({
31181             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31182             y : minY
31183         });
31184
31185         pos.push({
31186             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31187             y : minY + (this.unitWidth + this.gutter) * 2
31188         });
31189         
31190         return pos;
31191         
31192     },
31193     
31194     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31195     {
31196         var pos = [];
31197         
31198         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31199             
31200             pos.push({
31201                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31202                 y : minY
31203             });
31204
31205             pos.push({
31206                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31207                 y : minY + (this.unitWidth + this.gutter) * 1
31208             });
31209             
31210             pos.push({
31211                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31212                 y : minY + (this.unitWidth + this.gutter) * 2
31213             });
31214             
31215             return pos;
31216             
31217         }
31218         
31219         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31228                 y : minY
31229             });
31230             
31231             pos.push({
31232                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31233                 y : minY + (this.unitWidth + this.gutter) * 1
31234             });
31235             
31236             return pos;
31237             
31238         }
31239         
31240         pos.push({
31241             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31242             y : minY
31243         });
31244
31245         pos.push({
31246             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31247             y : minY + (this.unitWidth + this.gutter) * 2
31248         });
31249
31250         pos.push({
31251             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31252             y : minY + (this.unitWidth + this.gutter) * 2
31253         });
31254             
31255         return pos;
31256         
31257     },
31258     
31259     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31260     {
31261         var pos = [];
31262         
31263         if(box[0].size == 'xs'){
31264             
31265             pos.push({
31266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31267                 y : minY
31268             });
31269
31270             pos.push({
31271                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31272                 y : minY
31273             });
31274             
31275             pos.push({
31276                 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),
31277                 y : minY
31278             });
31279             
31280             pos.push({
31281                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31282                 y : minY + (this.unitWidth + this.gutter) * 1
31283             });
31284             
31285             return pos;
31286             
31287         }
31288         
31289         pos.push({
31290             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31291             y : minY
31292         });
31293         
31294         pos.push({
31295             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31296             y : minY + (this.unitWidth + this.gutter) * 2
31297         });
31298         
31299         pos.push({
31300             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31301             y : minY + (this.unitWidth + this.gutter) * 2
31302         });
31303         
31304         pos.push({
31305             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),
31306             y : minY + (this.unitWidth + this.gutter) * 2
31307         });
31308
31309         return pos;
31310         
31311     },
31312     
31313     /**
31314     * adds a Masonry Brick
31315     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31316     */
31317     addItem : function(cfg)
31318     {
31319         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31320         //this.register(cn);
31321         cn.parentId = this.id;
31322         cn.onRender(this.el, null);
31323         return cn;
31324     },
31325     
31326     /**
31327     * register a Masonry Brick
31328     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31329     */
31330     register : function(brick)
31331     {
31332         this.bricks.push(brick);
31333         brick.masonryId = this.id;
31334     },
31335     
31336     /**
31337     * clear all the Masonry Brick
31338     */
31339     clearAll : function()
31340     {
31341         this.bricks = [];
31342         //this.getChildContainer().dom.innerHTML = "";
31343         this.el.dom.innerHTML = '';
31344     },
31345     
31346     getSelected : function()
31347     {
31348         for (var i=0; i<this.bricks.length; i++) {
31349             Roo.log(this.bricks)[i];
31350         }
31351     }
31352     
31353 });
31354
31355  
31356
31357  /**
31358  *
31359  * This is based on 
31360  * http://masonry.desandro.com
31361  *
31362  * The idea is to render all the bricks based on vertical width...
31363  *
31364  * The original code extends 'outlayer' - we might need to use that....
31365  * 
31366  */
31367
31368
31369 /**
31370  * @class Roo.bootstrap.LayoutMasonryAuto
31371  * @extends Roo.bootstrap.Component
31372  * Bootstrap Layout Masonry class
31373  * 
31374  * @constructor
31375  * Create a new Element
31376  * @param {Object} config The config object
31377  */
31378
31379 Roo.bootstrap.LayoutMasonryAuto = function(config){
31380     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31381 };
31382
31383 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31384     
31385       /**
31386      * @cfg {Boolean} isFitWidth  - resize the width..
31387      */   
31388     isFitWidth : false,  // options..
31389     /**
31390      * @cfg {Boolean} isOriginLeft = left align?
31391      */   
31392     isOriginLeft : true,
31393     /**
31394      * @cfg {Boolean} isOriginTop = top align?
31395      */   
31396     isOriginTop : false,
31397     /**
31398      * @cfg {Boolean} isLayoutInstant = no animation?
31399      */   
31400     isLayoutInstant : false, // needed?
31401     /**
31402      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31403      */   
31404     isResizingContainer : true,
31405     /**
31406      * @cfg {Number} columnWidth  width of the columns 
31407      */   
31408     
31409     columnWidth : 0,
31410     
31411     /**
31412      * @cfg {Number} maxCols maximum number of columns
31413      */   
31414     
31415     maxCols: 0,
31416     /**
31417      * @cfg {Number} padHeight padding below box..
31418      */   
31419     
31420     padHeight : 10, 
31421     
31422     /**
31423      * @cfg {Boolean} isAutoInitial defalut true
31424      */   
31425     
31426     isAutoInitial : true, 
31427     
31428     // private?
31429     gutter : 0,
31430     
31431     containerWidth: 0,
31432     initialColumnWidth : 0,
31433     currentSize : null,
31434     
31435     colYs : null, // array.
31436     maxY : 0,
31437     padWidth: 10,
31438     
31439     
31440     tag: 'div',
31441     cls: '',
31442     bricks: null, //CompositeElement
31443     cols : 0, // array?
31444     // element : null, // wrapped now this.el
31445     _isLayoutInited : null, 
31446     
31447     
31448     getAutoCreate : function(){
31449         
31450         var cfg = {
31451             tag: this.tag,
31452             cls: 'blog-masonary-wrapper ' + this.cls,
31453             cn : {
31454                 cls : 'mas-boxes masonary'
31455             }
31456         };
31457         
31458         return cfg;
31459     },
31460     
31461     getChildContainer: function( )
31462     {
31463         if (this.boxesEl) {
31464             return this.boxesEl;
31465         }
31466         
31467         this.boxesEl = this.el.select('.mas-boxes').first();
31468         
31469         return this.boxesEl;
31470     },
31471     
31472     
31473     initEvents : function()
31474     {
31475         var _this = this;
31476         
31477         if(this.isAutoInitial){
31478             Roo.log('hook children rendered');
31479             this.on('childrenrendered', function() {
31480                 Roo.log('children rendered');
31481                 _this.initial();
31482             } ,this);
31483         }
31484         
31485     },
31486     
31487     initial : function()
31488     {
31489         this.reloadItems();
31490
31491         this.currentSize = this.el.getBox(true);
31492
31493         /// was window resize... - let's see if this works..
31494         Roo.EventManager.onWindowResize(this.resize, this); 
31495
31496         if(!this.isAutoInitial){
31497             this.layout();
31498             return;
31499         }
31500         
31501         this.layout.defer(500,this);
31502     },
31503     
31504     reloadItems: function()
31505     {
31506         this.bricks = this.el.select('.masonry-brick', true);
31507         
31508         this.bricks.each(function(b) {
31509             //Roo.log(b.getSize());
31510             if (!b.attr('originalwidth')) {
31511                 b.attr('originalwidth',  b.getSize().width);
31512             }
31513             
31514         });
31515         
31516         Roo.log(this.bricks.elements.length);
31517     },
31518     
31519     resize : function()
31520     {
31521         Roo.log('resize');
31522         var cs = this.el.getBox(true);
31523         
31524         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31525             Roo.log("no change in with or X");
31526             return;
31527         }
31528         this.currentSize = cs;
31529         this.layout();
31530     },
31531     
31532     layout : function()
31533     {
31534          Roo.log('layout');
31535         this._resetLayout();
31536         //this._manageStamps();
31537       
31538         // don't animate first layout
31539         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31540         this.layoutItems( isInstant );
31541       
31542         // flag for initalized
31543         this._isLayoutInited = true;
31544     },
31545     
31546     layoutItems : function( isInstant )
31547     {
31548         //var items = this._getItemsForLayout( this.items );
31549         // original code supports filtering layout items.. we just ignore it..
31550         
31551         this._layoutItems( this.bricks , isInstant );
31552       
31553         this._postLayout();
31554     },
31555     _layoutItems : function ( items , isInstant)
31556     {
31557        //this.fireEvent( 'layout', this, items );
31558     
31559
31560         if ( !items || !items.elements.length ) {
31561           // no items, emit event with empty array
31562             return;
31563         }
31564
31565         var queue = [];
31566         items.each(function(item) {
31567             Roo.log("layout item");
31568             Roo.log(item);
31569             // get x/y object from method
31570             var position = this._getItemLayoutPosition( item );
31571             // enqueue
31572             position.item = item;
31573             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31574             queue.push( position );
31575         }, this);
31576       
31577         this._processLayoutQueue( queue );
31578     },
31579     /** Sets position of item in DOM
31580     * @param {Element} item
31581     * @param {Number} x - horizontal position
31582     * @param {Number} y - vertical position
31583     * @param {Boolean} isInstant - disables transitions
31584     */
31585     _processLayoutQueue : function( queue )
31586     {
31587         for ( var i=0, len = queue.length; i < len; i++ ) {
31588             var obj = queue[i];
31589             obj.item.position('absolute');
31590             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31591         }
31592     },
31593       
31594     
31595     /**
31596     * Any logic you want to do after each layout,
31597     * i.e. size the container
31598     */
31599     _postLayout : function()
31600     {
31601         this.resizeContainer();
31602     },
31603     
31604     resizeContainer : function()
31605     {
31606         if ( !this.isResizingContainer ) {
31607             return;
31608         }
31609         var size = this._getContainerSize();
31610         if ( size ) {
31611             this.el.setSize(size.width,size.height);
31612             this.boxesEl.setSize(size.width,size.height);
31613         }
31614     },
31615     
31616     
31617     
31618     _resetLayout : function()
31619     {
31620         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31621         this.colWidth = this.el.getWidth();
31622         //this.gutter = this.el.getWidth(); 
31623         
31624         this.measureColumns();
31625
31626         // reset column Y
31627         var i = this.cols;
31628         this.colYs = [];
31629         while (i--) {
31630             this.colYs.push( 0 );
31631         }
31632     
31633         this.maxY = 0;
31634     },
31635
31636     measureColumns : function()
31637     {
31638         this.getContainerWidth();
31639       // if columnWidth is 0, default to outerWidth of first item
31640         if ( !this.columnWidth ) {
31641             var firstItem = this.bricks.first();
31642             Roo.log(firstItem);
31643             this.columnWidth  = this.containerWidth;
31644             if (firstItem && firstItem.attr('originalwidth') ) {
31645                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31646             }
31647             // columnWidth fall back to item of first element
31648             Roo.log("set column width?");
31649                         this.initialColumnWidth = this.columnWidth  ;
31650
31651             // if first elem has no width, default to size of container
31652             
31653         }
31654         
31655         
31656         if (this.initialColumnWidth) {
31657             this.columnWidth = this.initialColumnWidth;
31658         }
31659         
31660         
31661             
31662         // column width is fixed at the top - however if container width get's smaller we should
31663         // reduce it...
31664         
31665         // this bit calcs how man columns..
31666             
31667         var columnWidth = this.columnWidth += this.gutter;
31668       
31669         // calculate columns
31670         var containerWidth = this.containerWidth + this.gutter;
31671         
31672         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31673         // fix rounding errors, typically with gutters
31674         var excess = columnWidth - containerWidth % columnWidth;
31675         
31676         
31677         // if overshoot is less than a pixel, round up, otherwise floor it
31678         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31679         cols = Math[ mathMethod ]( cols );
31680         this.cols = Math.max( cols, 1 );
31681         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31682         
31683          // padding positioning..
31684         var totalColWidth = this.cols * this.columnWidth;
31685         var padavail = this.containerWidth - totalColWidth;
31686         // so for 2 columns - we need 3 'pads'
31687         
31688         var padNeeded = (1+this.cols) * this.padWidth;
31689         
31690         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31691         
31692         this.columnWidth += padExtra
31693         //this.padWidth = Math.floor(padavail /  ( this.cols));
31694         
31695         // adjust colum width so that padding is fixed??
31696         
31697         // we have 3 columns ... total = width * 3
31698         // we have X left over... that should be used by 
31699         
31700         //if (this.expandC) {
31701             
31702         //}
31703         
31704         
31705         
31706     },
31707     
31708     getContainerWidth : function()
31709     {
31710        /* // container is parent if fit width
31711         var container = this.isFitWidth ? this.element.parentNode : this.element;
31712         // check that this.size and size are there
31713         // IE8 triggers resize on body size change, so they might not be
31714         
31715         var size = getSize( container );  //FIXME
31716         this.containerWidth = size && size.innerWidth; //FIXME
31717         */
31718          
31719         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31720         
31721     },
31722     
31723     _getItemLayoutPosition : function( item )  // what is item?
31724     {
31725         // we resize the item to our columnWidth..
31726       
31727         item.setWidth(this.columnWidth);
31728         item.autoBoxAdjust  = false;
31729         
31730         var sz = item.getSize();
31731  
31732         // how many columns does this brick span
31733         var remainder = this.containerWidth % this.columnWidth;
31734         
31735         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31736         // round if off by 1 pixel, otherwise use ceil
31737         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31738         colSpan = Math.min( colSpan, this.cols );
31739         
31740         // normally this should be '1' as we dont' currently allow multi width columns..
31741         
31742         var colGroup = this._getColGroup( colSpan );
31743         // get the minimum Y value from the columns
31744         var minimumY = Math.min.apply( Math, colGroup );
31745         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31746         
31747         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31748          
31749         // position the brick
31750         var position = {
31751             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31752             y: this.currentSize.y + minimumY + this.padHeight
31753         };
31754         
31755         Roo.log(position);
31756         // apply setHeight to necessary columns
31757         var setHeight = minimumY + sz.height + this.padHeight;
31758         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31759         
31760         var setSpan = this.cols + 1 - colGroup.length;
31761         for ( var i = 0; i < setSpan; i++ ) {
31762           this.colYs[ shortColIndex + i ] = setHeight ;
31763         }
31764       
31765         return position;
31766     },
31767     
31768     /**
31769      * @param {Number} colSpan - number of columns the element spans
31770      * @returns {Array} colGroup
31771      */
31772     _getColGroup : function( colSpan )
31773     {
31774         if ( colSpan < 2 ) {
31775           // if brick spans only one column, use all the column Ys
31776           return this.colYs;
31777         }
31778       
31779         var colGroup = [];
31780         // how many different places could this brick fit horizontally
31781         var groupCount = this.cols + 1 - colSpan;
31782         // for each group potential horizontal position
31783         for ( var i = 0; i < groupCount; i++ ) {
31784           // make an array of colY values for that one group
31785           var groupColYs = this.colYs.slice( i, i + colSpan );
31786           // and get the max value of the array
31787           colGroup[i] = Math.max.apply( Math, groupColYs );
31788         }
31789         return colGroup;
31790     },
31791     /*
31792     _manageStamp : function( stamp )
31793     {
31794         var stampSize =  stamp.getSize();
31795         var offset = stamp.getBox();
31796         // get the columns that this stamp affects
31797         var firstX = this.isOriginLeft ? offset.x : offset.right;
31798         var lastX = firstX + stampSize.width;
31799         var firstCol = Math.floor( firstX / this.columnWidth );
31800         firstCol = Math.max( 0, firstCol );
31801         
31802         var lastCol = Math.floor( lastX / this.columnWidth );
31803         // lastCol should not go over if multiple of columnWidth #425
31804         lastCol -= lastX % this.columnWidth ? 0 : 1;
31805         lastCol = Math.min( this.cols - 1, lastCol );
31806         
31807         // set colYs to bottom of the stamp
31808         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31809             stampSize.height;
31810             
31811         for ( var i = firstCol; i <= lastCol; i++ ) {
31812           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31813         }
31814     },
31815     */
31816     
31817     _getContainerSize : function()
31818     {
31819         this.maxY = Math.max.apply( Math, this.colYs );
31820         var size = {
31821             height: this.maxY
31822         };
31823       
31824         if ( this.isFitWidth ) {
31825             size.width = this._getContainerFitWidth();
31826         }
31827       
31828         return size;
31829     },
31830     
31831     _getContainerFitWidth : function()
31832     {
31833         var unusedCols = 0;
31834         // count unused columns
31835         var i = this.cols;
31836         while ( --i ) {
31837           if ( this.colYs[i] !== 0 ) {
31838             break;
31839           }
31840           unusedCols++;
31841         }
31842         // fit container to columns that have been used
31843         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31844     },
31845     
31846     needsResizeLayout : function()
31847     {
31848         var previousWidth = this.containerWidth;
31849         this.getContainerWidth();
31850         return previousWidth !== this.containerWidth;
31851     }
31852  
31853 });
31854
31855  
31856
31857  /*
31858  * - LGPL
31859  *
31860  * element
31861  * 
31862  */
31863
31864 /**
31865  * @class Roo.bootstrap.MasonryBrick
31866  * @extends Roo.bootstrap.Component
31867  * Bootstrap MasonryBrick class
31868  * 
31869  * @constructor
31870  * Create a new MasonryBrick
31871  * @param {Object} config The config object
31872  */
31873
31874 Roo.bootstrap.MasonryBrick = function(config){
31875     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31876     
31877     this.addEvents({
31878         // raw events
31879         /**
31880          * @event click
31881          * When a MasonryBrick is clcik
31882          * @param {Roo.bootstrap.MasonryBrick} this
31883          * @param {Roo.EventObject} e
31884          */
31885         "click" : true
31886     });
31887 };
31888
31889 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31890     
31891     /**
31892      * @cfg {String} title
31893      */   
31894     title : '',
31895     /**
31896      * @cfg {String} html
31897      */   
31898     html : '',
31899     /**
31900      * @cfg {String} bgimage
31901      */   
31902     bgimage : '',
31903     /**
31904      * @cfg {String} videourl
31905      */   
31906     videourl : '',
31907     /**
31908      * @cfg {String} cls
31909      */   
31910     cls : '',
31911     /**
31912      * @cfg {String} href
31913      */   
31914     href : '',
31915     /**
31916      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31917      */   
31918     size : 'xs',
31919     
31920     /**
31921      * @cfg {String} placetitle (center|bottom)
31922      */   
31923     placetitle : '',
31924     
31925     /**
31926      * @cfg {Boolean} isFitContainer defalut true
31927      */   
31928     isFitContainer : true, 
31929     
31930     /**
31931      * @cfg {Boolean} preventDefault defalut false
31932      */   
31933     preventDefault : false, 
31934     
31935     /**
31936      * @cfg {Boolean} inverse defalut false
31937      */   
31938     maskInverse : false, 
31939     
31940     getAutoCreate : function()
31941     {
31942         if(!this.isFitContainer){
31943             return this.getSplitAutoCreate();
31944         }
31945         
31946         var cls = 'masonry-brick masonry-brick-full';
31947         
31948         if(this.href.length){
31949             cls += ' masonry-brick-link';
31950         }
31951         
31952         if(this.bgimage.length){
31953             cls += ' masonry-brick-image';
31954         }
31955         
31956         if(this.maskInverse){
31957             cls += ' mask-inverse';
31958         }
31959         
31960         if(!this.html.length && !this.maskInverse){
31961             cls += ' enable-mask';
31962         }
31963         
31964         if(this.size){
31965             cls += ' masonry-' + this.size + '-brick';
31966         }
31967         
31968         if(this.placetitle.length){
31969             
31970             switch (this.placetitle) {
31971                 case 'center' :
31972                     cls += ' masonry-center-title';
31973                     break;
31974                 case 'bottom' :
31975                     cls += ' masonry-bottom-title';
31976                     break;
31977                 default:
31978                     break;
31979             }
31980             
31981         } else {
31982             if(!this.html.length && !this.bgimage.length){
31983                 cls += ' masonry-center-title';
31984             }
31985
31986             if(!this.html.length && this.bgimage.length){
31987                 cls += ' masonry-bottom-title';
31988             }
31989         }
31990         
31991         if(this.cls){
31992             cls += ' ' + this.cls;
31993         }
31994         
31995         var cfg = {
31996             tag: (this.href.length) ? 'a' : 'div',
31997             cls: cls,
31998             cn: [
31999                 {
32000                     tag: 'div',
32001                     cls: 'masonry-brick-paragraph',
32002                     cn: []
32003                 }
32004             ]
32005         };
32006         
32007         if(this.href.length){
32008             cfg.href = this.href;
32009         }
32010         
32011         var cn = cfg.cn[0].cn;
32012         
32013         if(this.title.length){
32014             cn.push({
32015                 tag: 'h4',
32016                 cls: 'masonry-brick-title',
32017                 html: this.title
32018             });
32019         }
32020         
32021         if(this.html.length){
32022             cn.push({
32023                 tag: 'p',
32024                 cls: 'masonry-brick-text',
32025                 html: this.html
32026             });
32027         }  
32028         if (!this.title.length && !this.html.length) {
32029             cfg.cn[0].cls += ' hide';
32030         }
32031         
32032         if(this.bgimage.length){
32033             cfg.cn.push({
32034                 tag: 'img',
32035                 cls: 'masonry-brick-image-view',
32036                 src: this.bgimage
32037             });
32038         }
32039         
32040         if(this.videourl.length){
32041             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32042             // youtube support only?
32043             cfg.cn.push({
32044                 tag: 'iframe',
32045                 cls: 'masonry-brick-image-view',
32046                 src: vurl,
32047                 frameborder : 0,
32048                 allowfullscreen : true
32049             });
32050             
32051             
32052         }
32053         
32054         cfg.cn.push({
32055             tag: 'div',
32056             cls: 'masonry-brick-mask'
32057         });
32058         
32059         return cfg;
32060         
32061     },
32062     
32063     getSplitAutoCreate : function()
32064     {
32065         var cls = 'masonry-brick masonry-brick-split';
32066         
32067         if(this.href.length){
32068             cls += ' masonry-brick-link';
32069         }
32070         
32071         if(this.bgimage.length){
32072             cls += ' masonry-brick-image';
32073         }
32074         
32075         if(this.size){
32076             cls += ' masonry-' + this.size + '-brick';
32077         }
32078         
32079         switch (this.placetitle) {
32080             case 'center' :
32081                 cls += ' masonry-center-title';
32082                 break;
32083             case 'bottom' :
32084                 cls += ' masonry-bottom-title';
32085                 break;
32086             default:
32087                 if(!this.bgimage.length){
32088                     cls += ' masonry-center-title';
32089                 }
32090
32091                 if(this.bgimage.length){
32092                     cls += ' masonry-bottom-title';
32093                 }
32094                 break;
32095         }
32096         
32097         if(this.cls){
32098             cls += ' ' + this.cls;
32099         }
32100         
32101         var cfg = {
32102             tag: (this.href.length) ? 'a' : 'div',
32103             cls: cls,
32104             cn: [
32105                 {
32106                     tag: 'div',
32107                     cls: 'masonry-brick-split-head',
32108                     cn: [
32109                         {
32110                             tag: 'div',
32111                             cls: 'masonry-brick-paragraph',
32112                             cn: []
32113                         }
32114                     ]
32115                 },
32116                 {
32117                     tag: 'div',
32118                     cls: 'masonry-brick-split-body',
32119                     cn: []
32120                 }
32121             ]
32122         };
32123         
32124         if(this.href.length){
32125             cfg.href = this.href;
32126         }
32127         
32128         if(this.title.length){
32129             cfg.cn[0].cn[0].cn.push({
32130                 tag: 'h4',
32131                 cls: 'masonry-brick-title',
32132                 html: this.title
32133             });
32134         }
32135         
32136         if(this.html.length){
32137             cfg.cn[1].cn.push({
32138                 tag: 'p',
32139                 cls: 'masonry-brick-text',
32140                 html: this.html
32141             });
32142         }
32143
32144         if(this.bgimage.length){
32145             cfg.cn[0].cn.push({
32146                 tag: 'img',
32147                 cls: 'masonry-brick-image-view',
32148                 src: this.bgimage
32149             });
32150         }
32151         
32152         if(this.videourl.length){
32153             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32154             // youtube support only?
32155             cfg.cn[0].cn.cn.push({
32156                 tag: 'iframe',
32157                 cls: 'masonry-brick-image-view',
32158                 src: vurl,
32159                 frameborder : 0,
32160                 allowfullscreen : true
32161             });
32162         }
32163         
32164         return cfg;
32165     },
32166     
32167     initEvents: function() 
32168     {
32169         switch (this.size) {
32170             case 'xs' :
32171                 this.x = 1;
32172                 this.y = 1;
32173                 break;
32174             case 'sm' :
32175                 this.x = 2;
32176                 this.y = 2;
32177                 break;
32178             case 'md' :
32179             case 'md-left' :
32180             case 'md-right' :
32181                 this.x = 3;
32182                 this.y = 3;
32183                 break;
32184             case 'tall' :
32185                 this.x = 2;
32186                 this.y = 3;
32187                 break;
32188             case 'wide' :
32189                 this.x = 3;
32190                 this.y = 2;
32191                 break;
32192             case 'wide-thin' :
32193                 this.x = 3;
32194                 this.y = 1;
32195                 break;
32196                         
32197             default :
32198                 break;
32199         }
32200         
32201         if(Roo.isTouch){
32202             this.el.on('touchstart', this.onTouchStart, this);
32203             this.el.on('touchmove', this.onTouchMove, this);
32204             this.el.on('touchend', this.onTouchEnd, this);
32205             this.el.on('contextmenu', this.onContextMenu, this);
32206         } else {
32207             this.el.on('mouseenter'  ,this.enter, this);
32208             this.el.on('mouseleave', this.leave, this);
32209             this.el.on('click', this.onClick, this);
32210         }
32211         
32212         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32213             this.parent().bricks.push(this);   
32214         }
32215         
32216     },
32217     
32218     onClick: function(e, el)
32219     {
32220         var time = this.endTimer - this.startTimer;
32221         
32222         if(Roo.isTouch){
32223             if(time > 1000){
32224                 e.preventDefault();
32225                 return;
32226             }
32227         }
32228         
32229         if(!this.preventDefault){
32230             return;
32231         }
32232         
32233         e.preventDefault();
32234         this.fireEvent('click', this);
32235     },
32236     
32237     enter: function(e, el)
32238     {
32239         e.preventDefault();
32240         
32241         if(!this.isFitContainer || this.maskInverse){
32242             return;
32243         }
32244         
32245         if(this.bgimage.length && this.html.length){
32246             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32247         }
32248     },
32249     
32250     leave: function(e, el)
32251     {
32252         e.preventDefault();
32253         
32254         if(!this.isFitContainer || this.maskInverse){
32255             return;
32256         }
32257         
32258         if(this.bgimage.length && this.html.length){
32259             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32260         }
32261     },
32262     
32263     onTouchStart: function(e, el)
32264     {
32265 //        e.preventDefault();
32266         
32267         this.touchmoved = false;
32268         
32269         if(!this.isFitContainer){
32270             return;
32271         }
32272         
32273         if(!this.bgimage.length || !this.html.length){
32274             return;
32275         }
32276         
32277         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32278         
32279         this.timer = new Date().getTime();
32280         
32281     },
32282     
32283     onTouchMove: function(e, el)
32284     {
32285         this.touchmoved = true;
32286     },
32287     
32288     onContextMenu : function(e,el)
32289     {
32290         e.preventDefault();
32291         e.stopPropagation();
32292         return false;
32293     },
32294     
32295     onTouchEnd: function(e, el)
32296     {
32297 //        e.preventDefault();
32298         
32299         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32300         
32301             this.leave(e,el);
32302             
32303             return;
32304         }
32305         
32306         if(!this.bgimage.length || !this.html.length){
32307             
32308             if(this.href.length){
32309                 window.location.href = this.href;
32310             }
32311             
32312             return;
32313         }
32314         
32315         if(!this.isFitContainer){
32316             return;
32317         }
32318         
32319         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32320         
32321         window.location.href = this.href;
32322     }
32323     
32324 });
32325
32326  
32327
32328  /*
32329  * - LGPL
32330  *
32331  * element
32332  * 
32333  */
32334
32335 /**
32336  * @class Roo.bootstrap.Brick
32337  * @extends Roo.bootstrap.Component
32338  * Bootstrap Brick class
32339  * 
32340  * @constructor
32341  * Create a new Brick
32342  * @param {Object} config The config object
32343  */
32344
32345 Roo.bootstrap.Brick = function(config){
32346     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32347     
32348     this.addEvents({
32349         // raw events
32350         /**
32351          * @event click
32352          * When a Brick is click
32353          * @param {Roo.bootstrap.Brick} this
32354          * @param {Roo.EventObject} e
32355          */
32356         "click" : true
32357     });
32358 };
32359
32360 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32361     
32362     /**
32363      * @cfg {String} title
32364      */   
32365     title : '',
32366     /**
32367      * @cfg {String} html
32368      */   
32369     html : '',
32370     /**
32371      * @cfg {String} bgimage
32372      */   
32373     bgimage : '',
32374     /**
32375      * @cfg {String} cls
32376      */   
32377     cls : '',
32378     /**
32379      * @cfg {String} href
32380      */   
32381     href : '',
32382     /**
32383      * @cfg {String} video
32384      */   
32385     video : '',
32386     /**
32387      * @cfg {Boolean} square
32388      */   
32389     square : true,
32390     
32391     getAutoCreate : function()
32392     {
32393         var cls = 'roo-brick';
32394         
32395         if(this.href.length){
32396             cls += ' roo-brick-link';
32397         }
32398         
32399         if(this.bgimage.length){
32400             cls += ' roo-brick-image';
32401         }
32402         
32403         if(!this.html.length && !this.bgimage.length){
32404             cls += ' roo-brick-center-title';
32405         }
32406         
32407         if(!this.html.length && this.bgimage.length){
32408             cls += ' roo-brick-bottom-title';
32409         }
32410         
32411         if(this.cls){
32412             cls += ' ' + this.cls;
32413         }
32414         
32415         var cfg = {
32416             tag: (this.href.length) ? 'a' : 'div',
32417             cls: cls,
32418             cn: [
32419                 {
32420                     tag: 'div',
32421                     cls: 'roo-brick-paragraph',
32422                     cn: []
32423                 }
32424             ]
32425         };
32426         
32427         if(this.href.length){
32428             cfg.href = this.href;
32429         }
32430         
32431         var cn = cfg.cn[0].cn;
32432         
32433         if(this.title.length){
32434             cn.push({
32435                 tag: 'h4',
32436                 cls: 'roo-brick-title',
32437                 html: this.title
32438             });
32439         }
32440         
32441         if(this.html.length){
32442             cn.push({
32443                 tag: 'p',
32444                 cls: 'roo-brick-text',
32445                 html: this.html
32446             });
32447         } else {
32448             cn.cls += ' hide';
32449         }
32450         
32451         if(this.bgimage.length){
32452             cfg.cn.push({
32453                 tag: 'img',
32454                 cls: 'roo-brick-image-view',
32455                 src: this.bgimage
32456             });
32457         }
32458         
32459         return cfg;
32460     },
32461     
32462     initEvents: function() 
32463     {
32464         if(this.title.length || this.html.length){
32465             this.el.on('mouseenter'  ,this.enter, this);
32466             this.el.on('mouseleave', this.leave, this);
32467         }
32468         
32469         
32470         Roo.EventManager.onWindowResize(this.resize, this); 
32471         
32472         this.resize();
32473     },
32474     
32475     resize : function()
32476     {
32477         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32478         
32479         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32480         
32481         if(this.bgimage.length){
32482             var image = this.el.select('.roo-brick-image-view', true).first();
32483             image.setWidth(paragraph.getWidth());
32484             image.setHeight(paragraph.getWidth());
32485             
32486             this.el.setHeight(paragraph.getWidth());
32487             
32488         }
32489         
32490     },
32491     
32492     enter: function(e, el)
32493     {
32494         e.preventDefault();
32495         
32496         if(this.bgimage.length){
32497             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32498             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32499         }
32500     },
32501     
32502     leave: function(e, el)
32503     {
32504         e.preventDefault();
32505         
32506         if(this.bgimage.length){
32507             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32508             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32509         }
32510     }
32511     
32512 });
32513
32514  
32515
32516  /*
32517  * - LGPL
32518  *
32519  * Input
32520  * 
32521  */
32522
32523 /**
32524  * @class Roo.bootstrap.NumberField
32525  * @extends Roo.bootstrap.Input
32526  * Bootstrap NumberField class
32527  * 
32528  * 
32529  * 
32530  * 
32531  * @constructor
32532  * Create a new NumberField
32533  * @param {Object} config The config object
32534  */
32535
32536 Roo.bootstrap.NumberField = function(config){
32537     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32538 };
32539
32540 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32541     
32542     /**
32543      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32544      */
32545     allowDecimals : true,
32546     /**
32547      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32548      */
32549     decimalSeparator : ".",
32550     /**
32551      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32552      */
32553     decimalPrecision : 2,
32554     /**
32555      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32556      */
32557     allowNegative : true,
32558     /**
32559      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32560      */
32561     minValue : Number.NEGATIVE_INFINITY,
32562     /**
32563      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32564      */
32565     maxValue : Number.MAX_VALUE,
32566     /**
32567      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32568      */
32569     minText : "The minimum value for this field is {0}",
32570     /**
32571      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32572      */
32573     maxText : "The maximum value for this field is {0}",
32574     /**
32575      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32576      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32577      */
32578     nanText : "{0} is not a valid number",
32579     /**
32580      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32581      */
32582     castInt : true,
32583
32584     // private
32585     initEvents : function()
32586     {   
32587         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32588         
32589         var allowed = "0123456789";
32590         
32591         if(this.allowDecimals){
32592             allowed += this.decimalSeparator;
32593         }
32594         
32595         if(this.allowNegative){
32596             allowed += "-";
32597         }
32598         
32599         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32600         
32601         var keyPress = function(e){
32602             
32603             var k = e.getKey();
32604             
32605             var c = e.getCharCode();
32606             
32607             if(
32608                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32609                     allowed.indexOf(String.fromCharCode(c)) === -1
32610             ){
32611                 e.stopEvent();
32612                 return;
32613             }
32614             
32615             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32616                 return;
32617             }
32618             
32619             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32620                 e.stopEvent();
32621             }
32622         };
32623         
32624         this.el.on("keypress", keyPress, this);
32625     },
32626     
32627     validateValue : function(value)
32628     {
32629         
32630         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32631             return false;
32632         }
32633         
32634         var num = this.parseValue(value);
32635         
32636         if(isNaN(num)){
32637             this.markInvalid(String.format(this.nanText, value));
32638             return false;
32639         }
32640         
32641         if(num < this.minValue){
32642             this.markInvalid(String.format(this.minText, this.minValue));
32643             return false;
32644         }
32645         
32646         if(num > this.maxValue){
32647             this.markInvalid(String.format(this.maxText, this.maxValue));
32648             return false;
32649         }
32650         
32651         return true;
32652     },
32653
32654     getValue : function()
32655     {
32656         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32657     },
32658
32659     parseValue : function(value)
32660     {
32661         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32662         return isNaN(value) ? '' : value;
32663     },
32664
32665     fixPrecision : function(value)
32666     {
32667         var nan = isNaN(value);
32668         
32669         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32670             return nan ? '' : value;
32671         }
32672         return parseFloat(value).toFixed(this.decimalPrecision);
32673     },
32674
32675     setValue : function(v)
32676     {
32677         v = this.fixPrecision(v);
32678         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32679     },
32680
32681     decimalPrecisionFcn : function(v)
32682     {
32683         return Math.floor(v);
32684     },
32685
32686     beforeBlur : function()
32687     {
32688         if(!this.castInt){
32689             return;
32690         }
32691         
32692         var v = this.parseValue(this.getRawValue());
32693         if(v){
32694             this.setValue(v);
32695         }
32696     }
32697     
32698 });
32699
32700  
32701
32702 /*
32703 * Licence: LGPL
32704 */
32705
32706 /**
32707  * @class Roo.bootstrap.DocumentSlider
32708  * @extends Roo.bootstrap.Component
32709  * Bootstrap DocumentSlider class
32710  * 
32711  * @constructor
32712  * Create a new DocumentViewer
32713  * @param {Object} config The config object
32714  */
32715
32716 Roo.bootstrap.DocumentSlider = function(config){
32717     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32718     
32719     this.files = [];
32720     
32721     this.addEvents({
32722         /**
32723          * @event initial
32724          * Fire after initEvent
32725          * @param {Roo.bootstrap.DocumentSlider} this
32726          */
32727         "initial" : true,
32728         /**
32729          * @event update
32730          * Fire after update
32731          * @param {Roo.bootstrap.DocumentSlider} this
32732          */
32733         "update" : true,
32734         /**
32735          * @event click
32736          * Fire after click
32737          * @param {Roo.bootstrap.DocumentSlider} this
32738          */
32739         "click" : true
32740     });
32741 };
32742
32743 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32744     
32745     files : false,
32746     
32747     indicator : 0,
32748     
32749     getAutoCreate : function()
32750     {
32751         var cfg = {
32752             tag : 'div',
32753             cls : 'roo-document-slider',
32754             cn : [
32755                 {
32756                     tag : 'div',
32757                     cls : 'roo-document-slider-header',
32758                     cn : [
32759                         {
32760                             tag : 'div',
32761                             cls : 'roo-document-slider-header-title'
32762                         }
32763                     ]
32764                 },
32765                 {
32766                     tag : 'div',
32767                     cls : 'roo-document-slider-body',
32768                     cn : [
32769                         {
32770                             tag : 'div',
32771                             cls : 'roo-document-slider-prev',
32772                             cn : [
32773                                 {
32774                                     tag : 'i',
32775                                     cls : 'fa fa-chevron-left'
32776                                 }
32777                             ]
32778                         },
32779                         {
32780                             tag : 'div',
32781                             cls : 'roo-document-slider-thumb',
32782                             cn : [
32783                                 {
32784                                     tag : 'img',
32785                                     cls : 'roo-document-slider-image'
32786                                 }
32787                             ]
32788                         },
32789                         {
32790                             tag : 'div',
32791                             cls : 'roo-document-slider-next',
32792                             cn : [
32793                                 {
32794                                     tag : 'i',
32795                                     cls : 'fa fa-chevron-right'
32796                                 }
32797                             ]
32798                         }
32799                     ]
32800                 }
32801             ]
32802         };
32803         
32804         return cfg;
32805     },
32806     
32807     initEvents : function()
32808     {
32809         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32810         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32811         
32812         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32813         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32814         
32815         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32816         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32817         
32818         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32819         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32820         
32821         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32822         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32823         
32824         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32825         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32826         
32827         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32828         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32829         
32830         this.thumbEl.on('click', this.onClick, this);
32831         
32832         this.prevIndicator.on('click', this.prev, this);
32833         
32834         this.nextIndicator.on('click', this.next, this);
32835         
32836     },
32837     
32838     initial : function()
32839     {
32840         if(this.files.length){
32841             this.indicator = 1;
32842             this.update()
32843         }
32844         
32845         this.fireEvent('initial', this);
32846     },
32847     
32848     update : function()
32849     {
32850         this.imageEl.attr('src', this.files[this.indicator - 1]);
32851         
32852         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32853         
32854         this.prevIndicator.show();
32855         
32856         if(this.indicator == 1){
32857             this.prevIndicator.hide();
32858         }
32859         
32860         this.nextIndicator.show();
32861         
32862         if(this.indicator == this.files.length){
32863             this.nextIndicator.hide();
32864         }
32865         
32866         this.thumbEl.scrollTo('top');
32867         
32868         this.fireEvent('update', this);
32869     },
32870     
32871     onClick : function(e)
32872     {
32873         e.preventDefault();
32874         
32875         this.fireEvent('click', this);
32876     },
32877     
32878     prev : function(e)
32879     {
32880         e.preventDefault();
32881         
32882         this.indicator = Math.max(1, this.indicator - 1);
32883         
32884         this.update();
32885     },
32886     
32887     next : function(e)
32888     {
32889         e.preventDefault();
32890         
32891         this.indicator = Math.min(this.files.length, this.indicator + 1);
32892         
32893         this.update();
32894     }
32895 });
32896 /*
32897  * - LGPL
32898  *
32899  * RadioSet
32900  *
32901  *
32902  */
32903
32904 /**
32905  * @class Roo.bootstrap.RadioSet
32906  * @extends Roo.bootstrap.Input
32907  * Bootstrap RadioSet class
32908  * @cfg {String} indicatorpos (left|right) default left
32909  * @cfg {Boolean} inline (true|false) inline the element (default true)
32910  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32911  * @constructor
32912  * Create a new RadioSet
32913  * @param {Object} config The config object
32914  */
32915
32916 Roo.bootstrap.RadioSet = function(config){
32917     
32918     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32919     
32920     this.radioes = [];
32921     
32922     Roo.bootstrap.RadioSet.register(this);
32923     
32924     this.addEvents({
32925         /**
32926         * @event check
32927         * Fires when the element is checked or unchecked.
32928         * @param {Roo.bootstrap.RadioSet} this This radio
32929         * @param {Roo.bootstrap.Radio} item The checked item
32930         */
32931        check : true
32932     });
32933     
32934 };
32935
32936 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32937
32938     radioes : false,
32939     
32940     inline : true,
32941     
32942     weight : '',
32943     
32944     indicatorpos : 'left',
32945     
32946     getAutoCreate : function()
32947     {
32948         var label = {
32949             tag : 'label',
32950             cls : 'roo-radio-set-label',
32951             cn : [
32952                 {
32953                     tag : 'span',
32954                     html : this.fieldLabel
32955                 }
32956             ]
32957         };
32958         
32959         if(this.indicatorpos == 'left'){
32960             label.cn.unshift({
32961                 tag : 'i',
32962                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32963                 tooltip : 'This field is required'
32964             });
32965         } else {
32966             label.cn.push({
32967                 tag : 'i',
32968                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32969                 tooltip : 'This field is required'
32970             });
32971         }
32972         
32973         var items = {
32974             tag : 'div',
32975             cls : 'roo-radio-set-items'
32976         };
32977         
32978         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32979         
32980         if (align === 'left' && this.fieldLabel.length) {
32981             
32982             items = {
32983                 cls : "roo-radio-set-right", 
32984                 cn: [
32985                     items
32986                 ]
32987             };
32988             
32989             if(this.labelWidth > 12){
32990                 label.style = "width: " + this.labelWidth + 'px';
32991             }
32992             
32993             if(this.labelWidth < 13 && this.labelmd == 0){
32994                 this.labelmd = this.labelWidth;
32995             }
32996             
32997             if(this.labellg > 0){
32998                 label.cls += ' col-lg-' + this.labellg;
32999                 items.cls += ' col-lg-' + (12 - this.labellg);
33000             }
33001             
33002             if(this.labelmd > 0){
33003                 label.cls += ' col-md-' + this.labelmd;
33004                 items.cls += ' col-md-' + (12 - this.labelmd);
33005             }
33006             
33007             if(this.labelsm > 0){
33008                 label.cls += ' col-sm-' + this.labelsm;
33009                 items.cls += ' col-sm-' + (12 - this.labelsm);
33010             }
33011             
33012             if(this.labelxs > 0){
33013                 label.cls += ' col-xs-' + this.labelxs;
33014                 items.cls += ' col-xs-' + (12 - this.labelxs);
33015             }
33016         }
33017         
33018         var cfg = {
33019             tag : 'div',
33020             cls : 'roo-radio-set',
33021             cn : [
33022                 {
33023                     tag : 'input',
33024                     cls : 'roo-radio-set-input',
33025                     type : 'hidden',
33026                     name : this.name,
33027                     value : this.value ? this.value :  ''
33028                 },
33029                 label,
33030                 items
33031             ]
33032         };
33033         
33034         if(this.weight.length){
33035             cfg.cls += ' roo-radio-' + this.weight;
33036         }
33037         
33038         if(this.inline) {
33039             cfg.cls += ' roo-radio-set-inline';
33040         }
33041         
33042         return cfg;
33043         
33044     },
33045
33046     initEvents : function()
33047     {
33048         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33049         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33050         
33051         if(!this.fieldLabel.length){
33052             this.labelEl.hide();
33053         }
33054         
33055         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33056         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33057         
33058         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33059         this.indicatorEl().hide();
33060         
33061         this.originalValue = this.getValue();
33062         
33063     },
33064     
33065     inputEl: function ()
33066     {
33067         return this.el.select('.roo-radio-set-input', true).first();
33068     },
33069     
33070     getChildContainer : function()
33071     {
33072         return this.itemsEl;
33073     },
33074     
33075     register : function(item)
33076     {
33077         this.radioes.push(item);
33078         
33079     },
33080     
33081     validate : function()
33082     {   
33083         var valid = false;
33084         
33085         Roo.each(this.radioes, function(i){
33086             if(!i.checked){
33087                 return;
33088             }
33089             
33090             valid = true;
33091             return false;
33092         });
33093         
33094         if(this.allowBlank) {
33095             return true;
33096         }
33097         
33098         if(this.disabled || valid){
33099             this.markValid();
33100             return true;
33101         }
33102         
33103         this.markInvalid();
33104         return false;
33105         
33106     },
33107     
33108     markValid : function()
33109     {
33110         if(this.labelEl.isVisible(true)){
33111             this.indicatorEl().hide();
33112         }
33113         
33114         this.el.removeClass([this.invalidClass, this.validClass]);
33115         this.el.addClass(this.validClass);
33116         
33117         this.fireEvent('valid', this);
33118     },
33119     
33120     markInvalid : function(msg)
33121     {
33122         if(this.allowBlank || this.disabled){
33123             return;
33124         }
33125         
33126         if(this.labelEl.isVisible(true)){
33127             this.indicatorEl().show();
33128         }
33129         
33130         this.el.removeClass([this.invalidClass, this.validClass]);
33131         this.el.addClass(this.invalidClass);
33132         
33133         this.fireEvent('invalid', this, msg);
33134         
33135     },
33136     
33137     setValue : function(v, suppressEvent)
33138     {   
33139         Roo.each(this.radioes, function(i){
33140             
33141             i.checked = false;
33142             i.el.removeClass('checked');
33143             
33144             if(i.value === v || i.value.toString() === v.toString()){
33145                 i.checked = true;
33146                 i.el.addClass('checked');
33147                 
33148                 if(suppressEvent !== true){
33149                     this.fireEvent('check', this, i);
33150                 }
33151             }
33152             
33153         }, this);
33154         
33155         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
33156         
33157     },
33158     
33159     clearInvalid : function(){
33160         
33161         if(!this.el || this.preventMark){
33162             return;
33163         }
33164         
33165         if(this.labelEl.isVisible(true)){
33166             this.indicatorEl().hide();
33167         }
33168         
33169         this.el.removeClass([this.invalidClass]);
33170         
33171         this.fireEvent('valid', this);
33172     }
33173     
33174 });
33175
33176 Roo.apply(Roo.bootstrap.RadioSet, {
33177     
33178     groups: {},
33179     
33180     register : function(set)
33181     {
33182         this.groups[set.name] = set;
33183     },
33184     
33185     get: function(name) 
33186     {
33187         if (typeof(this.groups[name]) == 'undefined') {
33188             return false;
33189         }
33190         
33191         return this.groups[name] ;
33192     }
33193     
33194 });
33195 /*
33196  * Based on:
33197  * Ext JS Library 1.1.1
33198  * Copyright(c) 2006-2007, Ext JS, LLC.
33199  *
33200  * Originally Released Under LGPL - original licence link has changed is not relivant.
33201  *
33202  * Fork - LGPL
33203  * <script type="text/javascript">
33204  */
33205
33206
33207 /**
33208  * @class Roo.bootstrap.SplitBar
33209  * @extends Roo.util.Observable
33210  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33211  * <br><br>
33212  * Usage:
33213  * <pre><code>
33214 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33215                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33216 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33217 split.minSize = 100;
33218 split.maxSize = 600;
33219 split.animate = true;
33220 split.on('moved', splitterMoved);
33221 </code></pre>
33222  * @constructor
33223  * Create a new SplitBar
33224  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33225  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33226  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33227  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33228                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33229                         position of the SplitBar).
33230  */
33231 Roo.bootstrap.SplitBar = function(cfg){
33232     
33233     /** @private */
33234     
33235     //{
33236     //  dragElement : elm
33237     //  resizingElement: el,
33238         // optional..
33239     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33240     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33241         // existingProxy ???
33242     //}
33243     
33244     this.el = Roo.get(cfg.dragElement, true);
33245     this.el.dom.unselectable = "on";
33246     /** @private */
33247     this.resizingEl = Roo.get(cfg.resizingElement, true);
33248
33249     /**
33250      * @private
33251      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33252      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33253      * @type Number
33254      */
33255     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33256     
33257     /**
33258      * The minimum size of the resizing element. (Defaults to 0)
33259      * @type Number
33260      */
33261     this.minSize = 0;
33262     
33263     /**
33264      * The maximum size of the resizing element. (Defaults to 2000)
33265      * @type Number
33266      */
33267     this.maxSize = 2000;
33268     
33269     /**
33270      * Whether to animate the transition to the new size
33271      * @type Boolean
33272      */
33273     this.animate = false;
33274     
33275     /**
33276      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33277      * @type Boolean
33278      */
33279     this.useShim = false;
33280     
33281     /** @private */
33282     this.shim = null;
33283     
33284     if(!cfg.existingProxy){
33285         /** @private */
33286         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33287     }else{
33288         this.proxy = Roo.get(cfg.existingProxy).dom;
33289     }
33290     /** @private */
33291     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33292     
33293     /** @private */
33294     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33295     
33296     /** @private */
33297     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33298     
33299     /** @private */
33300     this.dragSpecs = {};
33301     
33302     /**
33303      * @private The adapter to use to positon and resize elements
33304      */
33305     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33306     this.adapter.init(this);
33307     
33308     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33309         /** @private */
33310         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33311         this.el.addClass("roo-splitbar-h");
33312     }else{
33313         /** @private */
33314         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33315         this.el.addClass("roo-splitbar-v");
33316     }
33317     
33318     this.addEvents({
33319         /**
33320          * @event resize
33321          * Fires when the splitter is moved (alias for {@link #event-moved})
33322          * @param {Roo.bootstrap.SplitBar} this
33323          * @param {Number} newSize the new width or height
33324          */
33325         "resize" : true,
33326         /**
33327          * @event moved
33328          * Fires when the splitter is moved
33329          * @param {Roo.bootstrap.SplitBar} this
33330          * @param {Number} newSize the new width or height
33331          */
33332         "moved" : true,
33333         /**
33334          * @event beforeresize
33335          * Fires before the splitter is dragged
33336          * @param {Roo.bootstrap.SplitBar} this
33337          */
33338         "beforeresize" : true,
33339
33340         "beforeapply" : true
33341     });
33342
33343     Roo.util.Observable.call(this);
33344 };
33345
33346 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33347     onStartProxyDrag : function(x, y){
33348         this.fireEvent("beforeresize", this);
33349         if(!this.overlay){
33350             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33351             o.unselectable();
33352             o.enableDisplayMode("block");
33353             // all splitbars share the same overlay
33354             Roo.bootstrap.SplitBar.prototype.overlay = o;
33355         }
33356         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33357         this.overlay.show();
33358         Roo.get(this.proxy).setDisplayed("block");
33359         var size = this.adapter.getElementSize(this);
33360         this.activeMinSize = this.getMinimumSize();;
33361         this.activeMaxSize = this.getMaximumSize();;
33362         var c1 = size - this.activeMinSize;
33363         var c2 = Math.max(this.activeMaxSize - size, 0);
33364         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33365             this.dd.resetConstraints();
33366             this.dd.setXConstraint(
33367                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33368                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33369             );
33370             this.dd.setYConstraint(0, 0);
33371         }else{
33372             this.dd.resetConstraints();
33373             this.dd.setXConstraint(0, 0);
33374             this.dd.setYConstraint(
33375                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33376                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33377             );
33378          }
33379         this.dragSpecs.startSize = size;
33380         this.dragSpecs.startPoint = [x, y];
33381         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33382     },
33383     
33384     /** 
33385      * @private Called after the drag operation by the DDProxy
33386      */
33387     onEndProxyDrag : function(e){
33388         Roo.get(this.proxy).setDisplayed(false);
33389         var endPoint = Roo.lib.Event.getXY(e);
33390         if(this.overlay){
33391             this.overlay.hide();
33392         }
33393         var newSize;
33394         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33395             newSize = this.dragSpecs.startSize + 
33396                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33397                     endPoint[0] - this.dragSpecs.startPoint[0] :
33398                     this.dragSpecs.startPoint[0] - endPoint[0]
33399                 );
33400         }else{
33401             newSize = this.dragSpecs.startSize + 
33402                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33403                     endPoint[1] - this.dragSpecs.startPoint[1] :
33404                     this.dragSpecs.startPoint[1] - endPoint[1]
33405                 );
33406         }
33407         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33408         if(newSize != this.dragSpecs.startSize){
33409             if(this.fireEvent('beforeapply', this, newSize) !== false){
33410                 this.adapter.setElementSize(this, newSize);
33411                 this.fireEvent("moved", this, newSize);
33412                 this.fireEvent("resize", this, newSize);
33413             }
33414         }
33415     },
33416     
33417     /**
33418      * Get the adapter this SplitBar uses
33419      * @return The adapter object
33420      */
33421     getAdapter : function(){
33422         return this.adapter;
33423     },
33424     
33425     /**
33426      * Set the adapter this SplitBar uses
33427      * @param {Object} adapter A SplitBar adapter object
33428      */
33429     setAdapter : function(adapter){
33430         this.adapter = adapter;
33431         this.adapter.init(this);
33432     },
33433     
33434     /**
33435      * Gets the minimum size for the resizing element
33436      * @return {Number} The minimum size
33437      */
33438     getMinimumSize : function(){
33439         return this.minSize;
33440     },
33441     
33442     /**
33443      * Sets the minimum size for the resizing element
33444      * @param {Number} minSize The minimum size
33445      */
33446     setMinimumSize : function(minSize){
33447         this.minSize = minSize;
33448     },
33449     
33450     /**
33451      * Gets the maximum size for the resizing element
33452      * @return {Number} The maximum size
33453      */
33454     getMaximumSize : function(){
33455         return this.maxSize;
33456     },
33457     
33458     /**
33459      * Sets the maximum size for the resizing element
33460      * @param {Number} maxSize The maximum size
33461      */
33462     setMaximumSize : function(maxSize){
33463         this.maxSize = maxSize;
33464     },
33465     
33466     /**
33467      * Sets the initialize size for the resizing element
33468      * @param {Number} size The initial size
33469      */
33470     setCurrentSize : function(size){
33471         var oldAnimate = this.animate;
33472         this.animate = false;
33473         this.adapter.setElementSize(this, size);
33474         this.animate = oldAnimate;
33475     },
33476     
33477     /**
33478      * Destroy this splitbar. 
33479      * @param {Boolean} removeEl True to remove the element
33480      */
33481     destroy : function(removeEl){
33482         if(this.shim){
33483             this.shim.remove();
33484         }
33485         this.dd.unreg();
33486         this.proxy.parentNode.removeChild(this.proxy);
33487         if(removeEl){
33488             this.el.remove();
33489         }
33490     }
33491 });
33492
33493 /**
33494  * @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.
33495  */
33496 Roo.bootstrap.SplitBar.createProxy = function(dir){
33497     var proxy = new Roo.Element(document.createElement("div"));
33498     proxy.unselectable();
33499     var cls = 'roo-splitbar-proxy';
33500     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33501     document.body.appendChild(proxy.dom);
33502     return proxy.dom;
33503 };
33504
33505 /** 
33506  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33507  * Default Adapter. It assumes the splitter and resizing element are not positioned
33508  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33509  */
33510 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33511 };
33512
33513 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33514     // do nothing for now
33515     init : function(s){
33516     
33517     },
33518     /**
33519      * Called before drag operations to get the current size of the resizing element. 
33520      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33521      */
33522      getElementSize : function(s){
33523         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33524             return s.resizingEl.getWidth();
33525         }else{
33526             return s.resizingEl.getHeight();
33527         }
33528     },
33529     
33530     /**
33531      * Called after drag operations to set the size of the resizing element.
33532      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33533      * @param {Number} newSize The new size to set
33534      * @param {Function} onComplete A function to be invoked when resizing is complete
33535      */
33536     setElementSize : function(s, newSize, onComplete){
33537         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33538             if(!s.animate){
33539                 s.resizingEl.setWidth(newSize);
33540                 if(onComplete){
33541                     onComplete(s, newSize);
33542                 }
33543             }else{
33544                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33545             }
33546         }else{
33547             
33548             if(!s.animate){
33549                 s.resizingEl.setHeight(newSize);
33550                 if(onComplete){
33551                     onComplete(s, newSize);
33552                 }
33553             }else{
33554                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33555             }
33556         }
33557     }
33558 };
33559
33560 /** 
33561  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33562  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33563  * Adapter that  moves the splitter element to align with the resized sizing element. 
33564  * Used with an absolute positioned SplitBar.
33565  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33566  * document.body, make sure you assign an id to the body element.
33567  */
33568 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33569     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33570     this.container = Roo.get(container);
33571 };
33572
33573 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33574     init : function(s){
33575         this.basic.init(s);
33576     },
33577     
33578     getElementSize : function(s){
33579         return this.basic.getElementSize(s);
33580     },
33581     
33582     setElementSize : function(s, newSize, onComplete){
33583         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33584     },
33585     
33586     moveSplitter : function(s){
33587         var yes = Roo.bootstrap.SplitBar;
33588         switch(s.placement){
33589             case yes.LEFT:
33590                 s.el.setX(s.resizingEl.getRight());
33591                 break;
33592             case yes.RIGHT:
33593                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33594                 break;
33595             case yes.TOP:
33596                 s.el.setY(s.resizingEl.getBottom());
33597                 break;
33598             case yes.BOTTOM:
33599                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33600                 break;
33601         }
33602     }
33603 };
33604
33605 /**
33606  * Orientation constant - Create a vertical SplitBar
33607  * @static
33608  * @type Number
33609  */
33610 Roo.bootstrap.SplitBar.VERTICAL = 1;
33611
33612 /**
33613  * Orientation constant - Create a horizontal SplitBar
33614  * @static
33615  * @type Number
33616  */
33617 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33618
33619 /**
33620  * Placement constant - The resizing element is to the left of the splitter element
33621  * @static
33622  * @type Number
33623  */
33624 Roo.bootstrap.SplitBar.LEFT = 1;
33625
33626 /**
33627  * Placement constant - The resizing element is to the right of the splitter element
33628  * @static
33629  * @type Number
33630  */
33631 Roo.bootstrap.SplitBar.RIGHT = 2;
33632
33633 /**
33634  * Placement constant - The resizing element is positioned above the splitter element
33635  * @static
33636  * @type Number
33637  */
33638 Roo.bootstrap.SplitBar.TOP = 3;
33639
33640 /**
33641  * Placement constant - The resizing element is positioned under splitter element
33642  * @static
33643  * @type Number
33644  */
33645 Roo.bootstrap.SplitBar.BOTTOM = 4;
33646 Roo.namespace("Roo.bootstrap.layout");/*
33647  * Based on:
33648  * Ext JS Library 1.1.1
33649  * Copyright(c) 2006-2007, Ext JS, LLC.
33650  *
33651  * Originally Released Under LGPL - original licence link has changed is not relivant.
33652  *
33653  * Fork - LGPL
33654  * <script type="text/javascript">
33655  */
33656
33657 /**
33658  * @class Roo.bootstrap.layout.Manager
33659  * @extends Roo.bootstrap.Component
33660  * Base class for layout managers.
33661  */
33662 Roo.bootstrap.layout.Manager = function(config)
33663 {
33664     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33665
33666
33667
33668
33669
33670     /** false to disable window resize monitoring @type Boolean */
33671     this.monitorWindowResize = true;
33672     this.regions = {};
33673     this.addEvents({
33674         /**
33675          * @event layout
33676          * Fires when a layout is performed.
33677          * @param {Roo.LayoutManager} this
33678          */
33679         "layout" : true,
33680         /**
33681          * @event regionresized
33682          * Fires when the user resizes a region.
33683          * @param {Roo.LayoutRegion} region The resized region
33684          * @param {Number} newSize The new size (width for east/west, height for north/south)
33685          */
33686         "regionresized" : true,
33687         /**
33688          * @event regioncollapsed
33689          * Fires when a region is collapsed.
33690          * @param {Roo.LayoutRegion} region The collapsed region
33691          */
33692         "regioncollapsed" : true,
33693         /**
33694          * @event regionexpanded
33695          * Fires when a region is expanded.
33696          * @param {Roo.LayoutRegion} region The expanded region
33697          */
33698         "regionexpanded" : true
33699     });
33700     this.updating = false;
33701
33702     if (config.el) {
33703         this.el = Roo.get(config.el);
33704         this.initEvents();
33705     }
33706
33707 };
33708
33709 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33710
33711
33712     regions : null,
33713
33714     monitorWindowResize : true,
33715
33716
33717     updating : false,
33718
33719
33720     onRender : function(ct, position)
33721     {
33722         if(!this.el){
33723             this.el = Roo.get(ct);
33724             this.initEvents();
33725         }
33726         //this.fireEvent('render',this);
33727     },
33728
33729
33730     initEvents: function()
33731     {
33732
33733
33734         // ie scrollbar fix
33735         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33736             document.body.scroll = "no";
33737         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33738             this.el.position('relative');
33739         }
33740         this.id = this.el.id;
33741         this.el.addClass("roo-layout-container");
33742         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33743         if(this.el.dom != document.body ) {
33744             this.el.on('resize', this.layout,this);
33745             this.el.on('show', this.layout,this);
33746         }
33747
33748     },
33749
33750     /**
33751      * Returns true if this layout is currently being updated
33752      * @return {Boolean}
33753      */
33754     isUpdating : function(){
33755         return this.updating;
33756     },
33757
33758     /**
33759      * Suspend the LayoutManager from doing auto-layouts while
33760      * making multiple add or remove calls
33761      */
33762     beginUpdate : function(){
33763         this.updating = true;
33764     },
33765
33766     /**
33767      * Restore auto-layouts and optionally disable the manager from performing a layout
33768      * @param {Boolean} noLayout true to disable a layout update
33769      */
33770     endUpdate : function(noLayout){
33771         this.updating = false;
33772         if(!noLayout){
33773             this.layout();
33774         }
33775     },
33776
33777     layout: function(){
33778         // abstract...
33779     },
33780
33781     onRegionResized : function(region, newSize){
33782         this.fireEvent("regionresized", region, newSize);
33783         this.layout();
33784     },
33785
33786     onRegionCollapsed : function(region){
33787         this.fireEvent("regioncollapsed", region);
33788     },
33789
33790     onRegionExpanded : function(region){
33791         this.fireEvent("regionexpanded", region);
33792     },
33793
33794     /**
33795      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33796      * performs box-model adjustments.
33797      * @return {Object} The size as an object {width: (the width), height: (the height)}
33798      */
33799     getViewSize : function()
33800     {
33801         var size;
33802         if(this.el.dom != document.body){
33803             size = this.el.getSize();
33804         }else{
33805             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33806         }
33807         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33808         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33809         return size;
33810     },
33811
33812     /**
33813      * Returns the Element this layout is bound to.
33814      * @return {Roo.Element}
33815      */
33816     getEl : function(){
33817         return this.el;
33818     },
33819
33820     /**
33821      * Returns the specified region.
33822      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33823      * @return {Roo.LayoutRegion}
33824      */
33825     getRegion : function(target){
33826         return this.regions[target.toLowerCase()];
33827     },
33828
33829     onWindowResize : function(){
33830         if(this.monitorWindowResize){
33831             this.layout();
33832         }
33833     }
33834 });
33835 /*
33836  * Based on:
33837  * Ext JS Library 1.1.1
33838  * Copyright(c) 2006-2007, Ext JS, LLC.
33839  *
33840  * Originally Released Under LGPL - original licence link has changed is not relivant.
33841  *
33842  * Fork - LGPL
33843  * <script type="text/javascript">
33844  */
33845 /**
33846  * @class Roo.bootstrap.layout.Border
33847  * @extends Roo.bootstrap.layout.Manager
33848  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33849  * please see: examples/bootstrap/nested.html<br><br>
33850  
33851 <b>The container the layout is rendered into can be either the body element or any other element.
33852 If it is not the body element, the container needs to either be an absolute positioned element,
33853 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33854 the container size if it is not the body element.</b>
33855
33856 * @constructor
33857 * Create a new Border
33858 * @param {Object} config Configuration options
33859  */
33860 Roo.bootstrap.layout.Border = function(config){
33861     config = config || {};
33862     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33863     
33864     
33865     
33866     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33867         if(config[region]){
33868             config[region].region = region;
33869             this.addRegion(config[region]);
33870         }
33871     },this);
33872     
33873 };
33874
33875 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33876
33877 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33878     /**
33879      * Creates and adds a new region if it doesn't already exist.
33880      * @param {String} target The target region key (north, south, east, west or center).
33881      * @param {Object} config The regions config object
33882      * @return {BorderLayoutRegion} The new region
33883      */
33884     addRegion : function(config)
33885     {
33886         if(!this.regions[config.region]){
33887             var r = this.factory(config);
33888             this.bindRegion(r);
33889         }
33890         return this.regions[config.region];
33891     },
33892
33893     // private (kinda)
33894     bindRegion : function(r){
33895         this.regions[r.config.region] = r;
33896         
33897         r.on("visibilitychange",    this.layout, this);
33898         r.on("paneladded",          this.layout, this);
33899         r.on("panelremoved",        this.layout, this);
33900         r.on("invalidated",         this.layout, this);
33901         r.on("resized",             this.onRegionResized, this);
33902         r.on("collapsed",           this.onRegionCollapsed, this);
33903         r.on("expanded",            this.onRegionExpanded, this);
33904     },
33905
33906     /**
33907      * Performs a layout update.
33908      */
33909     layout : function()
33910     {
33911         if(this.updating) {
33912             return;
33913         }
33914         
33915         // render all the rebions if they have not been done alreayd?
33916         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33917             if(this.regions[region] && !this.regions[region].bodyEl){
33918                 this.regions[region].onRender(this.el)
33919             }
33920         },this);
33921         
33922         var size = this.getViewSize();
33923         var w = size.width;
33924         var h = size.height;
33925         var centerW = w;
33926         var centerH = h;
33927         var centerY = 0;
33928         var centerX = 0;
33929         //var x = 0, y = 0;
33930
33931         var rs = this.regions;
33932         var north = rs["north"];
33933         var south = rs["south"]; 
33934         var west = rs["west"];
33935         var east = rs["east"];
33936         var center = rs["center"];
33937         //if(this.hideOnLayout){ // not supported anymore
33938             //c.el.setStyle("display", "none");
33939         //}
33940         if(north && north.isVisible()){
33941             var b = north.getBox();
33942             var m = north.getMargins();
33943             b.width = w - (m.left+m.right);
33944             b.x = m.left;
33945             b.y = m.top;
33946             centerY = b.height + b.y + m.bottom;
33947             centerH -= centerY;
33948             north.updateBox(this.safeBox(b));
33949         }
33950         if(south && south.isVisible()){
33951             var b = south.getBox();
33952             var m = south.getMargins();
33953             b.width = w - (m.left+m.right);
33954             b.x = m.left;
33955             var totalHeight = (b.height + m.top + m.bottom);
33956             b.y = h - totalHeight + m.top;
33957             centerH -= totalHeight;
33958             south.updateBox(this.safeBox(b));
33959         }
33960         if(west && west.isVisible()){
33961             var b = west.getBox();
33962             var m = west.getMargins();
33963             b.height = centerH - (m.top+m.bottom);
33964             b.x = m.left;
33965             b.y = centerY + m.top;
33966             var totalWidth = (b.width + m.left + m.right);
33967             centerX += totalWidth;
33968             centerW -= totalWidth;
33969             west.updateBox(this.safeBox(b));
33970         }
33971         if(east && east.isVisible()){
33972             var b = east.getBox();
33973             var m = east.getMargins();
33974             b.height = centerH - (m.top+m.bottom);
33975             var totalWidth = (b.width + m.left + m.right);
33976             b.x = w - totalWidth + m.left;
33977             b.y = centerY + m.top;
33978             centerW -= totalWidth;
33979             east.updateBox(this.safeBox(b));
33980         }
33981         if(center){
33982             var m = center.getMargins();
33983             var centerBox = {
33984                 x: centerX + m.left,
33985                 y: centerY + m.top,
33986                 width: centerW - (m.left+m.right),
33987                 height: centerH - (m.top+m.bottom)
33988             };
33989             //if(this.hideOnLayout){
33990                 //center.el.setStyle("display", "block");
33991             //}
33992             center.updateBox(this.safeBox(centerBox));
33993         }
33994         this.el.repaint();
33995         this.fireEvent("layout", this);
33996     },
33997
33998     // private
33999     safeBox : function(box){
34000         box.width = Math.max(0, box.width);
34001         box.height = Math.max(0, box.height);
34002         return box;
34003     },
34004
34005     /**
34006      * Adds a ContentPanel (or subclass) to this layout.
34007      * @param {String} target The target region key (north, south, east, west or center).
34008      * @param {Roo.ContentPanel} panel The panel to add
34009      * @return {Roo.ContentPanel} The added panel
34010      */
34011     add : function(target, panel){
34012          
34013         target = target.toLowerCase();
34014         return this.regions[target].add(panel);
34015     },
34016
34017     /**
34018      * Remove a ContentPanel (or subclass) to this layout.
34019      * @param {String} target The target region key (north, south, east, west or center).
34020      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34021      * @return {Roo.ContentPanel} The removed panel
34022      */
34023     remove : function(target, panel){
34024         target = target.toLowerCase();
34025         return this.regions[target].remove(panel);
34026     },
34027
34028     /**
34029      * Searches all regions for a panel with the specified id
34030      * @param {String} panelId
34031      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34032      */
34033     findPanel : function(panelId){
34034         var rs = this.regions;
34035         for(var target in rs){
34036             if(typeof rs[target] != "function"){
34037                 var p = rs[target].getPanel(panelId);
34038                 if(p){
34039                     return p;
34040                 }
34041             }
34042         }
34043         return null;
34044     },
34045
34046     /**
34047      * Searches all regions for a panel with the specified id and activates (shows) it.
34048      * @param {String/ContentPanel} panelId The panels id or the panel itself
34049      * @return {Roo.ContentPanel} The shown panel or null
34050      */
34051     showPanel : function(panelId) {
34052       var rs = this.regions;
34053       for(var target in rs){
34054          var r = rs[target];
34055          if(typeof r != "function"){
34056             if(r.hasPanel(panelId)){
34057                return r.showPanel(panelId);
34058             }
34059          }
34060       }
34061       return null;
34062    },
34063
34064    /**
34065      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34066      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34067      */
34068    /*
34069     restoreState : function(provider){
34070         if(!provider){
34071             provider = Roo.state.Manager;
34072         }
34073         var sm = new Roo.LayoutStateManager();
34074         sm.init(this, provider);
34075     },
34076 */
34077  
34078  
34079     /**
34080      * Adds a xtype elements to the layout.
34081      * <pre><code>
34082
34083 layout.addxtype({
34084        xtype : 'ContentPanel',
34085        region: 'west',
34086        items: [ .... ]
34087    }
34088 );
34089
34090 layout.addxtype({
34091         xtype : 'NestedLayoutPanel',
34092         region: 'west',
34093         layout: {
34094            center: { },
34095            west: { }   
34096         },
34097         items : [ ... list of content panels or nested layout panels.. ]
34098    }
34099 );
34100 </code></pre>
34101      * @param {Object} cfg Xtype definition of item to add.
34102      */
34103     addxtype : function(cfg)
34104     {
34105         // basically accepts a pannel...
34106         // can accept a layout region..!?!?
34107         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34108         
34109         
34110         // theory?  children can only be panels??
34111         
34112         //if (!cfg.xtype.match(/Panel$/)) {
34113         //    return false;
34114         //}
34115         var ret = false;
34116         
34117         if (typeof(cfg.region) == 'undefined') {
34118             Roo.log("Failed to add Panel, region was not set");
34119             Roo.log(cfg);
34120             return false;
34121         }
34122         var region = cfg.region;
34123         delete cfg.region;
34124         
34125           
34126         var xitems = [];
34127         if (cfg.items) {
34128             xitems = cfg.items;
34129             delete cfg.items;
34130         }
34131         var nb = false;
34132         
34133         switch(cfg.xtype) 
34134         {
34135             case 'Content':  // ContentPanel (el, cfg)
34136             case 'Scroll':  // ContentPanel (el, cfg)
34137             case 'View': 
34138                 cfg.autoCreate = true;
34139                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34140                 //} else {
34141                 //    var el = this.el.createChild();
34142                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34143                 //}
34144                 
34145                 this.add(region, ret);
34146                 break;
34147             
34148             /*
34149             case 'TreePanel': // our new panel!
34150                 cfg.el = this.el.createChild();
34151                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34152                 this.add(region, ret);
34153                 break;
34154             */
34155             
34156             case 'Nest': 
34157                 // create a new Layout (which is  a Border Layout...
34158                 
34159                 var clayout = cfg.layout;
34160                 clayout.el  = this.el.createChild();
34161                 clayout.items   = clayout.items  || [];
34162                 
34163                 delete cfg.layout;
34164                 
34165                 // replace this exitems with the clayout ones..
34166                 xitems = clayout.items;
34167                  
34168                 // force background off if it's in center...
34169                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34170                     cfg.background = false;
34171                 }
34172                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34173                 
34174                 
34175                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34176                 //console.log('adding nested layout panel '  + cfg.toSource());
34177                 this.add(region, ret);
34178                 nb = {}; /// find first...
34179                 break;
34180             
34181             case 'Grid':
34182                 
34183                 // needs grid and region
34184                 
34185                 //var el = this.getRegion(region).el.createChild();
34186                 /*
34187                  *var el = this.el.createChild();
34188                 // create the grid first...
34189                 cfg.grid.container = el;
34190                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34191                 */
34192                 
34193                 if (region == 'center' && this.active ) {
34194                     cfg.background = false;
34195                 }
34196                 
34197                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34198                 
34199                 this.add(region, ret);
34200                 /*
34201                 if (cfg.background) {
34202                     // render grid on panel activation (if panel background)
34203                     ret.on('activate', function(gp) {
34204                         if (!gp.grid.rendered) {
34205                     //        gp.grid.render(el);
34206                         }
34207                     });
34208                 } else {
34209                   //  cfg.grid.render(el);
34210                 }
34211                 */
34212                 break;
34213            
34214            
34215             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34216                 // it was the old xcomponent building that caused this before.
34217                 // espeically if border is the top element in the tree.
34218                 ret = this;
34219                 break; 
34220                 
34221                     
34222                 
34223                 
34224                 
34225             default:
34226                 /*
34227                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34228                     
34229                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34230                     this.add(region, ret);
34231                 } else {
34232                 */
34233                     Roo.log(cfg);
34234                     throw "Can not add '" + cfg.xtype + "' to Border";
34235                     return null;
34236              
34237                                 
34238              
34239         }
34240         this.beginUpdate();
34241         // add children..
34242         var region = '';
34243         var abn = {};
34244         Roo.each(xitems, function(i)  {
34245             region = nb && i.region ? i.region : false;
34246             
34247             var add = ret.addxtype(i);
34248            
34249             if (region) {
34250                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34251                 if (!i.background) {
34252                     abn[region] = nb[region] ;
34253                 }
34254             }
34255             
34256         });
34257         this.endUpdate();
34258
34259         // make the last non-background panel active..
34260         //if (nb) { Roo.log(abn); }
34261         if (nb) {
34262             
34263             for(var r in abn) {
34264                 region = this.getRegion(r);
34265                 if (region) {
34266                     // tried using nb[r], but it does not work..
34267                      
34268                     region.showPanel(abn[r]);
34269                    
34270                 }
34271             }
34272         }
34273         return ret;
34274         
34275     },
34276     
34277     
34278 // private
34279     factory : function(cfg)
34280     {
34281         
34282         var validRegions = Roo.bootstrap.layout.Border.regions;
34283
34284         var target = cfg.region;
34285         cfg.mgr = this;
34286         
34287         var r = Roo.bootstrap.layout;
34288         Roo.log(target);
34289         switch(target){
34290             case "north":
34291                 return new r.North(cfg);
34292             case "south":
34293                 return new r.South(cfg);
34294             case "east":
34295                 return new r.East(cfg);
34296             case "west":
34297                 return new r.West(cfg);
34298             case "center":
34299                 return new r.Center(cfg);
34300         }
34301         throw 'Layout region "'+target+'" not supported.';
34302     }
34303     
34304     
34305 });
34306  /*
34307  * Based on:
34308  * Ext JS Library 1.1.1
34309  * Copyright(c) 2006-2007, Ext JS, LLC.
34310  *
34311  * Originally Released Under LGPL - original licence link has changed is not relivant.
34312  *
34313  * Fork - LGPL
34314  * <script type="text/javascript">
34315  */
34316  
34317 /**
34318  * @class Roo.bootstrap.layout.Basic
34319  * @extends Roo.util.Observable
34320  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34321  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34322  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34323  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34324  * @cfg {string}   region  the region that it inhabits..
34325  * @cfg {bool}   skipConfig skip config?
34326  * 
34327
34328  */
34329 Roo.bootstrap.layout.Basic = function(config){
34330     
34331     this.mgr = config.mgr;
34332     
34333     this.position = config.region;
34334     
34335     var skipConfig = config.skipConfig;
34336     
34337     this.events = {
34338         /**
34339          * @scope Roo.BasicLayoutRegion
34340          */
34341         
34342         /**
34343          * @event beforeremove
34344          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34345          * @param {Roo.LayoutRegion} this
34346          * @param {Roo.ContentPanel} panel The panel
34347          * @param {Object} e The cancel event object
34348          */
34349         "beforeremove" : true,
34350         /**
34351          * @event invalidated
34352          * Fires when the layout for this region is changed.
34353          * @param {Roo.LayoutRegion} this
34354          */
34355         "invalidated" : true,
34356         /**
34357          * @event visibilitychange
34358          * Fires when this region is shown or hidden 
34359          * @param {Roo.LayoutRegion} this
34360          * @param {Boolean} visibility true or false
34361          */
34362         "visibilitychange" : true,
34363         /**
34364          * @event paneladded
34365          * Fires when a panel is added. 
34366          * @param {Roo.LayoutRegion} this
34367          * @param {Roo.ContentPanel} panel The panel
34368          */
34369         "paneladded" : true,
34370         /**
34371          * @event panelremoved
34372          * Fires when a panel is removed. 
34373          * @param {Roo.LayoutRegion} this
34374          * @param {Roo.ContentPanel} panel The panel
34375          */
34376         "panelremoved" : true,
34377         /**
34378          * @event beforecollapse
34379          * Fires when this region before collapse.
34380          * @param {Roo.LayoutRegion} this
34381          */
34382         "beforecollapse" : true,
34383         /**
34384          * @event collapsed
34385          * Fires when this region is collapsed.
34386          * @param {Roo.LayoutRegion} this
34387          */
34388         "collapsed" : true,
34389         /**
34390          * @event expanded
34391          * Fires when this region is expanded.
34392          * @param {Roo.LayoutRegion} this
34393          */
34394         "expanded" : true,
34395         /**
34396          * @event slideshow
34397          * Fires when this region is slid into view.
34398          * @param {Roo.LayoutRegion} this
34399          */
34400         "slideshow" : true,
34401         /**
34402          * @event slidehide
34403          * Fires when this region slides out of view. 
34404          * @param {Roo.LayoutRegion} this
34405          */
34406         "slidehide" : true,
34407         /**
34408          * @event panelactivated
34409          * Fires when a panel is activated. 
34410          * @param {Roo.LayoutRegion} this
34411          * @param {Roo.ContentPanel} panel The activated panel
34412          */
34413         "panelactivated" : true,
34414         /**
34415          * @event resized
34416          * Fires when the user resizes this region. 
34417          * @param {Roo.LayoutRegion} this
34418          * @param {Number} newSize The new size (width for east/west, height for north/south)
34419          */
34420         "resized" : true
34421     };
34422     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34423     this.panels = new Roo.util.MixedCollection();
34424     this.panels.getKey = this.getPanelId.createDelegate(this);
34425     this.box = null;
34426     this.activePanel = null;
34427     // ensure listeners are added...
34428     
34429     if (config.listeners || config.events) {
34430         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34431             listeners : config.listeners || {},
34432             events : config.events || {}
34433         });
34434     }
34435     
34436     if(skipConfig !== true){
34437         this.applyConfig(config);
34438     }
34439 };
34440
34441 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34442 {
34443     getPanelId : function(p){
34444         return p.getId();
34445     },
34446     
34447     applyConfig : function(config){
34448         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34449         this.config = config;
34450         
34451     },
34452     
34453     /**
34454      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34455      * the width, for horizontal (north, south) the height.
34456      * @param {Number} newSize The new width or height
34457      */
34458     resizeTo : function(newSize){
34459         var el = this.el ? this.el :
34460                  (this.activePanel ? this.activePanel.getEl() : null);
34461         if(el){
34462             switch(this.position){
34463                 case "east":
34464                 case "west":
34465                     el.setWidth(newSize);
34466                     this.fireEvent("resized", this, newSize);
34467                 break;
34468                 case "north":
34469                 case "south":
34470                     el.setHeight(newSize);
34471                     this.fireEvent("resized", this, newSize);
34472                 break;                
34473             }
34474         }
34475     },
34476     
34477     getBox : function(){
34478         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34479     },
34480     
34481     getMargins : function(){
34482         return this.margins;
34483     },
34484     
34485     updateBox : function(box){
34486         this.box = box;
34487         var el = this.activePanel.getEl();
34488         el.dom.style.left = box.x + "px";
34489         el.dom.style.top = box.y + "px";
34490         this.activePanel.setSize(box.width, box.height);
34491     },
34492     
34493     /**
34494      * Returns the container element for this region.
34495      * @return {Roo.Element}
34496      */
34497     getEl : function(){
34498         return this.activePanel;
34499     },
34500     
34501     /**
34502      * Returns true if this region is currently visible.
34503      * @return {Boolean}
34504      */
34505     isVisible : function(){
34506         return this.activePanel ? true : false;
34507     },
34508     
34509     setActivePanel : function(panel){
34510         panel = this.getPanel(panel);
34511         if(this.activePanel && this.activePanel != panel){
34512             this.activePanel.setActiveState(false);
34513             this.activePanel.getEl().setLeftTop(-10000,-10000);
34514         }
34515         this.activePanel = panel;
34516         panel.setActiveState(true);
34517         if(this.box){
34518             panel.setSize(this.box.width, this.box.height);
34519         }
34520         this.fireEvent("panelactivated", this, panel);
34521         this.fireEvent("invalidated");
34522     },
34523     
34524     /**
34525      * Show the specified panel.
34526      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34527      * @return {Roo.ContentPanel} The shown panel or null
34528      */
34529     showPanel : function(panel){
34530         panel = this.getPanel(panel);
34531         if(panel){
34532             this.setActivePanel(panel);
34533         }
34534         return panel;
34535     },
34536     
34537     /**
34538      * Get the active panel for this region.
34539      * @return {Roo.ContentPanel} The active panel or null
34540      */
34541     getActivePanel : function(){
34542         return this.activePanel;
34543     },
34544     
34545     /**
34546      * Add the passed ContentPanel(s)
34547      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34548      * @return {Roo.ContentPanel} The panel added (if only one was added)
34549      */
34550     add : function(panel){
34551         if(arguments.length > 1){
34552             for(var i = 0, len = arguments.length; i < len; i++) {
34553                 this.add(arguments[i]);
34554             }
34555             return null;
34556         }
34557         if(this.hasPanel(panel)){
34558             this.showPanel(panel);
34559             return panel;
34560         }
34561         var el = panel.getEl();
34562         if(el.dom.parentNode != this.mgr.el.dom){
34563             this.mgr.el.dom.appendChild(el.dom);
34564         }
34565         if(panel.setRegion){
34566             panel.setRegion(this);
34567         }
34568         this.panels.add(panel);
34569         el.setStyle("position", "absolute");
34570         if(!panel.background){
34571             this.setActivePanel(panel);
34572             if(this.config.initialSize && this.panels.getCount()==1){
34573                 this.resizeTo(this.config.initialSize);
34574             }
34575         }
34576         this.fireEvent("paneladded", this, panel);
34577         return panel;
34578     },
34579     
34580     /**
34581      * Returns true if the panel is in this region.
34582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34583      * @return {Boolean}
34584      */
34585     hasPanel : function(panel){
34586         if(typeof panel == "object"){ // must be panel obj
34587             panel = panel.getId();
34588         }
34589         return this.getPanel(panel) ? true : false;
34590     },
34591     
34592     /**
34593      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34594      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34595      * @param {Boolean} preservePanel Overrides the config preservePanel option
34596      * @return {Roo.ContentPanel} The panel that was removed
34597      */
34598     remove : function(panel, preservePanel){
34599         panel = this.getPanel(panel);
34600         if(!panel){
34601             return null;
34602         }
34603         var e = {};
34604         this.fireEvent("beforeremove", this, panel, e);
34605         if(e.cancel === true){
34606             return null;
34607         }
34608         var panelId = panel.getId();
34609         this.panels.removeKey(panelId);
34610         return panel;
34611     },
34612     
34613     /**
34614      * Returns the panel specified or null if it's not in this region.
34615      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34616      * @return {Roo.ContentPanel}
34617      */
34618     getPanel : function(id){
34619         if(typeof id == "object"){ // must be panel obj
34620             return id;
34621         }
34622         return this.panels.get(id);
34623     },
34624     
34625     /**
34626      * Returns this regions position (north/south/east/west/center).
34627      * @return {String} 
34628      */
34629     getPosition: function(){
34630         return this.position;    
34631     }
34632 });/*
34633  * Based on:
34634  * Ext JS Library 1.1.1
34635  * Copyright(c) 2006-2007, Ext JS, LLC.
34636  *
34637  * Originally Released Under LGPL - original licence link has changed is not relivant.
34638  *
34639  * Fork - LGPL
34640  * <script type="text/javascript">
34641  */
34642  
34643 /**
34644  * @class Roo.bootstrap.layout.Region
34645  * @extends Roo.bootstrap.layout.Basic
34646  * This class represents a region in a layout manager.
34647  
34648  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34649  * @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})
34650  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34651  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34652  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34653  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34654  * @cfg {String}    title           The title for the region (overrides panel titles)
34655  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34656  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34657  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34658  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34659  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34660  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34661  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34662  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34663  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34664  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34665
34666  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34667  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34668  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34669  * @cfg {Number}    width           For East/West panels
34670  * @cfg {Number}    height          For North/South panels
34671  * @cfg {Boolean}   split           To show the splitter
34672  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34673  * 
34674  * @cfg {string}   cls             Extra CSS classes to add to region
34675  * 
34676  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34677  * @cfg {string}   region  the region that it inhabits..
34678  *
34679
34680  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34681  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34682
34683  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34684  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34685  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34686  */
34687 Roo.bootstrap.layout.Region = function(config)
34688 {
34689     this.applyConfig(config);
34690
34691     var mgr = config.mgr;
34692     var pos = config.region;
34693     config.skipConfig = true;
34694     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34695     
34696     if (mgr.el) {
34697         this.onRender(mgr.el);   
34698     }
34699      
34700     this.visible = true;
34701     this.collapsed = false;
34702     this.unrendered_panels = [];
34703 };
34704
34705 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34706
34707     position: '', // set by wrapper (eg. north/south etc..)
34708     unrendered_panels : null,  // unrendered panels.
34709     createBody : function(){
34710         /** This region's body element 
34711         * @type Roo.Element */
34712         this.bodyEl = this.el.createChild({
34713                 tag: "div",
34714                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34715         });
34716     },
34717
34718     onRender: function(ctr, pos)
34719     {
34720         var dh = Roo.DomHelper;
34721         /** This region's container element 
34722         * @type Roo.Element */
34723         this.el = dh.append(ctr.dom, {
34724                 tag: "div",
34725                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34726             }, true);
34727         /** This region's title element 
34728         * @type Roo.Element */
34729     
34730         this.titleEl = dh.append(this.el.dom,
34731             {
34732                     tag: "div",
34733                     unselectable: "on",
34734                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34735                     children:[
34736                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34737                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34738                     ]}, true);
34739         
34740         this.titleEl.enableDisplayMode();
34741         /** This region's title text element 
34742         * @type HTMLElement */
34743         this.titleTextEl = this.titleEl.dom.firstChild;
34744         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34745         /*
34746         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34747         this.closeBtn.enableDisplayMode();
34748         this.closeBtn.on("click", this.closeClicked, this);
34749         this.closeBtn.hide();
34750     */
34751         this.createBody(this.config);
34752         if(this.config.hideWhenEmpty){
34753             this.hide();
34754             this.on("paneladded", this.validateVisibility, this);
34755             this.on("panelremoved", this.validateVisibility, this);
34756         }
34757         if(this.autoScroll){
34758             this.bodyEl.setStyle("overflow", "auto");
34759         }else{
34760             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34761         }
34762         //if(c.titlebar !== false){
34763             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34764                 this.titleEl.hide();
34765             }else{
34766                 this.titleEl.show();
34767                 if(this.config.title){
34768                     this.titleTextEl.innerHTML = this.config.title;
34769                 }
34770             }
34771         //}
34772         if(this.config.collapsed){
34773             this.collapse(true);
34774         }
34775         if(this.config.hidden){
34776             this.hide();
34777         }
34778         
34779         if (this.unrendered_panels && this.unrendered_panels.length) {
34780             for (var i =0;i< this.unrendered_panels.length; i++) {
34781                 this.add(this.unrendered_panels[i]);
34782             }
34783             this.unrendered_panels = null;
34784             
34785         }
34786         
34787     },
34788     
34789     applyConfig : function(c)
34790     {
34791         /*
34792          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34793             var dh = Roo.DomHelper;
34794             if(c.titlebar !== false){
34795                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34796                 this.collapseBtn.on("click", this.collapse, this);
34797                 this.collapseBtn.enableDisplayMode();
34798                 /*
34799                 if(c.showPin === true || this.showPin){
34800                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34801                     this.stickBtn.enableDisplayMode();
34802                     this.stickBtn.on("click", this.expand, this);
34803                     this.stickBtn.hide();
34804                 }
34805                 
34806             }
34807             */
34808             /** This region's collapsed element
34809             * @type Roo.Element */
34810             /*
34811              *
34812             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34813                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34814             ]}, true);
34815             
34816             if(c.floatable !== false){
34817                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34818                this.collapsedEl.on("click", this.collapseClick, this);
34819             }
34820
34821             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34822                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34823                    id: "message", unselectable: "on", style:{"float":"left"}});
34824                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34825              }
34826             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34827             this.expandBtn.on("click", this.expand, this);
34828             
34829         }
34830         
34831         if(this.collapseBtn){
34832             this.collapseBtn.setVisible(c.collapsible == true);
34833         }
34834         
34835         this.cmargins = c.cmargins || this.cmargins ||
34836                          (this.position == "west" || this.position == "east" ?
34837                              {top: 0, left: 2, right:2, bottom: 0} :
34838                              {top: 2, left: 0, right:0, bottom: 2});
34839         */
34840         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34841         
34842         
34843         this.bottomTabs = c.tabPosition != "top";
34844         
34845         this.autoScroll = c.autoScroll || false;
34846         
34847         
34848        
34849         
34850         this.duration = c.duration || .30;
34851         this.slideDuration = c.slideDuration || .45;
34852         this.config = c;
34853        
34854     },
34855     /**
34856      * Returns true if this region is currently visible.
34857      * @return {Boolean}
34858      */
34859     isVisible : function(){
34860         return this.visible;
34861     },
34862
34863     /**
34864      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34865      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34866      */
34867     //setCollapsedTitle : function(title){
34868     //    title = title || "&#160;";
34869      //   if(this.collapsedTitleTextEl){
34870       //      this.collapsedTitleTextEl.innerHTML = title;
34871        // }
34872     //},
34873
34874     getBox : function(){
34875         var b;
34876       //  if(!this.collapsed){
34877             b = this.el.getBox(false, true);
34878        // }else{
34879           //  b = this.collapsedEl.getBox(false, true);
34880         //}
34881         return b;
34882     },
34883
34884     getMargins : function(){
34885         return this.margins;
34886         //return this.collapsed ? this.cmargins : this.margins;
34887     },
34888 /*
34889     highlight : function(){
34890         this.el.addClass("x-layout-panel-dragover");
34891     },
34892
34893     unhighlight : function(){
34894         this.el.removeClass("x-layout-panel-dragover");
34895     },
34896 */
34897     updateBox : function(box)
34898     {
34899         if (!this.bodyEl) {
34900             return; // not rendered yet..
34901         }
34902         
34903         this.box = box;
34904         if(!this.collapsed){
34905             this.el.dom.style.left = box.x + "px";
34906             this.el.dom.style.top = box.y + "px";
34907             this.updateBody(box.width, box.height);
34908         }else{
34909             this.collapsedEl.dom.style.left = box.x + "px";
34910             this.collapsedEl.dom.style.top = box.y + "px";
34911             this.collapsedEl.setSize(box.width, box.height);
34912         }
34913         if(this.tabs){
34914             this.tabs.autoSizeTabs();
34915         }
34916     },
34917
34918     updateBody : function(w, h)
34919     {
34920         if(w !== null){
34921             this.el.setWidth(w);
34922             w -= this.el.getBorderWidth("rl");
34923             if(this.config.adjustments){
34924                 w += this.config.adjustments[0];
34925             }
34926         }
34927         if(h !== null && h > 0){
34928             this.el.setHeight(h);
34929             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34930             h -= this.el.getBorderWidth("tb");
34931             if(this.config.adjustments){
34932                 h += this.config.adjustments[1];
34933             }
34934             this.bodyEl.setHeight(h);
34935             if(this.tabs){
34936                 h = this.tabs.syncHeight(h);
34937             }
34938         }
34939         if(this.panelSize){
34940             w = w !== null ? w : this.panelSize.width;
34941             h = h !== null ? h : this.panelSize.height;
34942         }
34943         if(this.activePanel){
34944             var el = this.activePanel.getEl();
34945             w = w !== null ? w : el.getWidth();
34946             h = h !== null ? h : el.getHeight();
34947             this.panelSize = {width: w, height: h};
34948             this.activePanel.setSize(w, h);
34949         }
34950         if(Roo.isIE && this.tabs){
34951             this.tabs.el.repaint();
34952         }
34953     },
34954
34955     /**
34956      * Returns the container element for this region.
34957      * @return {Roo.Element}
34958      */
34959     getEl : function(){
34960         return this.el;
34961     },
34962
34963     /**
34964      * Hides this region.
34965      */
34966     hide : function(){
34967         //if(!this.collapsed){
34968             this.el.dom.style.left = "-2000px";
34969             this.el.hide();
34970         //}else{
34971          //   this.collapsedEl.dom.style.left = "-2000px";
34972          //   this.collapsedEl.hide();
34973        // }
34974         this.visible = false;
34975         this.fireEvent("visibilitychange", this, false);
34976     },
34977
34978     /**
34979      * Shows this region if it was previously hidden.
34980      */
34981     show : function(){
34982         //if(!this.collapsed){
34983             this.el.show();
34984         //}else{
34985         //    this.collapsedEl.show();
34986        // }
34987         this.visible = true;
34988         this.fireEvent("visibilitychange", this, true);
34989     },
34990 /*
34991     closeClicked : function(){
34992         if(this.activePanel){
34993             this.remove(this.activePanel);
34994         }
34995     },
34996
34997     collapseClick : function(e){
34998         if(this.isSlid){
34999            e.stopPropagation();
35000            this.slideIn();
35001         }else{
35002            e.stopPropagation();
35003            this.slideOut();
35004         }
35005     },
35006 */
35007     /**
35008      * Collapses this region.
35009      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35010      */
35011     /*
35012     collapse : function(skipAnim, skipCheck = false){
35013         if(this.collapsed) {
35014             return;
35015         }
35016         
35017         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35018             
35019             this.collapsed = true;
35020             if(this.split){
35021                 this.split.el.hide();
35022             }
35023             if(this.config.animate && skipAnim !== true){
35024                 this.fireEvent("invalidated", this);
35025                 this.animateCollapse();
35026             }else{
35027                 this.el.setLocation(-20000,-20000);
35028                 this.el.hide();
35029                 this.collapsedEl.show();
35030                 this.fireEvent("collapsed", this);
35031                 this.fireEvent("invalidated", this);
35032             }
35033         }
35034         
35035     },
35036 */
35037     animateCollapse : function(){
35038         // overridden
35039     },
35040
35041     /**
35042      * Expands this region if it was previously collapsed.
35043      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35044      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35045      */
35046     /*
35047     expand : function(e, skipAnim){
35048         if(e) {
35049             e.stopPropagation();
35050         }
35051         if(!this.collapsed || this.el.hasActiveFx()) {
35052             return;
35053         }
35054         if(this.isSlid){
35055             this.afterSlideIn();
35056             skipAnim = true;
35057         }
35058         this.collapsed = false;
35059         if(this.config.animate && skipAnim !== true){
35060             this.animateExpand();
35061         }else{
35062             this.el.show();
35063             if(this.split){
35064                 this.split.el.show();
35065             }
35066             this.collapsedEl.setLocation(-2000,-2000);
35067             this.collapsedEl.hide();
35068             this.fireEvent("invalidated", this);
35069             this.fireEvent("expanded", this);
35070         }
35071     },
35072 */
35073     animateExpand : function(){
35074         // overridden
35075     },
35076
35077     initTabs : function()
35078     {
35079         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35080         
35081         var ts = new Roo.bootstrap.panel.Tabs({
35082                 el: this.bodyEl.dom,
35083                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35084                 disableTooltips: this.config.disableTabTips,
35085                 toolbar : this.config.toolbar
35086             });
35087         
35088         if(this.config.hideTabs){
35089             ts.stripWrap.setDisplayed(false);
35090         }
35091         this.tabs = ts;
35092         ts.resizeTabs = this.config.resizeTabs === true;
35093         ts.minTabWidth = this.config.minTabWidth || 40;
35094         ts.maxTabWidth = this.config.maxTabWidth || 250;
35095         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35096         ts.monitorResize = false;
35097         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35098         ts.bodyEl.addClass('roo-layout-tabs-body');
35099         this.panels.each(this.initPanelAsTab, this);
35100     },
35101
35102     initPanelAsTab : function(panel){
35103         var ti = this.tabs.addTab(
35104             panel.getEl().id,
35105             panel.getTitle(),
35106             null,
35107             this.config.closeOnTab && panel.isClosable(),
35108             panel.tpl
35109         );
35110         if(panel.tabTip !== undefined){
35111             ti.setTooltip(panel.tabTip);
35112         }
35113         ti.on("activate", function(){
35114               this.setActivePanel(panel);
35115         }, this);
35116         
35117         if(this.config.closeOnTab){
35118             ti.on("beforeclose", function(t, e){
35119                 e.cancel = true;
35120                 this.remove(panel);
35121             }, this);
35122         }
35123         
35124         panel.tabItem = ti;
35125         
35126         return ti;
35127     },
35128
35129     updatePanelTitle : function(panel, title)
35130     {
35131         if(this.activePanel == panel){
35132             this.updateTitle(title);
35133         }
35134         if(this.tabs){
35135             var ti = this.tabs.getTab(panel.getEl().id);
35136             ti.setText(title);
35137             if(panel.tabTip !== undefined){
35138                 ti.setTooltip(panel.tabTip);
35139             }
35140         }
35141     },
35142
35143     updateTitle : function(title){
35144         if(this.titleTextEl && !this.config.title){
35145             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35146         }
35147     },
35148
35149     setActivePanel : function(panel)
35150     {
35151         panel = this.getPanel(panel);
35152         if(this.activePanel && this.activePanel != panel){
35153             this.activePanel.setActiveState(false);
35154         }
35155         this.activePanel = panel;
35156         panel.setActiveState(true);
35157         if(this.panelSize){
35158             panel.setSize(this.panelSize.width, this.panelSize.height);
35159         }
35160         if(this.closeBtn){
35161             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35162         }
35163         this.updateTitle(panel.getTitle());
35164         if(this.tabs){
35165             this.fireEvent("invalidated", this);
35166         }
35167         this.fireEvent("panelactivated", this, panel);
35168     },
35169
35170     /**
35171      * Shows the specified panel.
35172      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35173      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35174      */
35175     showPanel : function(panel)
35176     {
35177         panel = this.getPanel(panel);
35178         if(panel){
35179             if(this.tabs){
35180                 var tab = this.tabs.getTab(panel.getEl().id);
35181                 if(tab.isHidden()){
35182                     this.tabs.unhideTab(tab.id);
35183                 }
35184                 tab.activate();
35185             }else{
35186                 this.setActivePanel(panel);
35187             }
35188         }
35189         return panel;
35190     },
35191
35192     /**
35193      * Get the active panel for this region.
35194      * @return {Roo.ContentPanel} The active panel or null
35195      */
35196     getActivePanel : function(){
35197         return this.activePanel;
35198     },
35199
35200     validateVisibility : function(){
35201         if(this.panels.getCount() < 1){
35202             this.updateTitle("&#160;");
35203             this.closeBtn.hide();
35204             this.hide();
35205         }else{
35206             if(!this.isVisible()){
35207                 this.show();
35208             }
35209         }
35210     },
35211
35212     /**
35213      * Adds the passed ContentPanel(s) to this region.
35214      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35215      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35216      */
35217     add : function(panel)
35218     {
35219         if(arguments.length > 1){
35220             for(var i = 0, len = arguments.length; i < len; i++) {
35221                 this.add(arguments[i]);
35222             }
35223             return null;
35224         }
35225         
35226         // if we have not been rendered yet, then we can not really do much of this..
35227         if (!this.bodyEl) {
35228             this.unrendered_panels.push(panel);
35229             return panel;
35230         }
35231         
35232         
35233         
35234         
35235         if(this.hasPanel(panel)){
35236             this.showPanel(panel);
35237             return panel;
35238         }
35239         panel.setRegion(this);
35240         this.panels.add(panel);
35241        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35242             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35243             // and hide them... ???
35244             this.bodyEl.dom.appendChild(panel.getEl().dom);
35245             if(panel.background !== true){
35246                 this.setActivePanel(panel);
35247             }
35248             this.fireEvent("paneladded", this, panel);
35249             return panel;
35250         }
35251         */
35252         if(!this.tabs){
35253             this.initTabs();
35254         }else{
35255             this.initPanelAsTab(panel);
35256         }
35257         
35258         
35259         if(panel.background !== true){
35260             this.tabs.activate(panel.getEl().id);
35261         }
35262         this.fireEvent("paneladded", this, panel);
35263         return panel;
35264     },
35265
35266     /**
35267      * Hides the tab for the specified panel.
35268      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35269      */
35270     hidePanel : function(panel){
35271         if(this.tabs && (panel = this.getPanel(panel))){
35272             this.tabs.hideTab(panel.getEl().id);
35273         }
35274     },
35275
35276     /**
35277      * Unhides the tab for a previously hidden panel.
35278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35279      */
35280     unhidePanel : function(panel){
35281         if(this.tabs && (panel = this.getPanel(panel))){
35282             this.tabs.unhideTab(panel.getEl().id);
35283         }
35284     },
35285
35286     clearPanels : function(){
35287         while(this.panels.getCount() > 0){
35288              this.remove(this.panels.first());
35289         }
35290     },
35291
35292     /**
35293      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35294      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35295      * @param {Boolean} preservePanel Overrides the config preservePanel option
35296      * @return {Roo.ContentPanel} The panel that was removed
35297      */
35298     remove : function(panel, preservePanel)
35299     {
35300         panel = this.getPanel(panel);
35301         if(!panel){
35302             return null;
35303         }
35304         var e = {};
35305         this.fireEvent("beforeremove", this, panel, e);
35306         if(e.cancel === true){
35307             return null;
35308         }
35309         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35310         var panelId = panel.getId();
35311         this.panels.removeKey(panelId);
35312         if(preservePanel){
35313             document.body.appendChild(panel.getEl().dom);
35314         }
35315         if(this.tabs){
35316             this.tabs.removeTab(panel.getEl().id);
35317         }else if (!preservePanel){
35318             this.bodyEl.dom.removeChild(panel.getEl().dom);
35319         }
35320         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35321             var p = this.panels.first();
35322             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35323             tempEl.appendChild(p.getEl().dom);
35324             this.bodyEl.update("");
35325             this.bodyEl.dom.appendChild(p.getEl().dom);
35326             tempEl = null;
35327             this.updateTitle(p.getTitle());
35328             this.tabs = null;
35329             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35330             this.setActivePanel(p);
35331         }
35332         panel.setRegion(null);
35333         if(this.activePanel == panel){
35334             this.activePanel = null;
35335         }
35336         if(this.config.autoDestroy !== false && preservePanel !== true){
35337             try{panel.destroy();}catch(e){}
35338         }
35339         this.fireEvent("panelremoved", this, panel);
35340         return panel;
35341     },
35342
35343     /**
35344      * Returns the TabPanel component used by this region
35345      * @return {Roo.TabPanel}
35346      */
35347     getTabs : function(){
35348         return this.tabs;
35349     },
35350
35351     createTool : function(parentEl, className){
35352         var btn = Roo.DomHelper.append(parentEl, {
35353             tag: "div",
35354             cls: "x-layout-tools-button",
35355             children: [ {
35356                 tag: "div",
35357                 cls: "roo-layout-tools-button-inner " + className,
35358                 html: "&#160;"
35359             }]
35360         }, true);
35361         btn.addClassOnOver("roo-layout-tools-button-over");
35362         return btn;
35363     }
35364 });/*
35365  * Based on:
35366  * Ext JS Library 1.1.1
35367  * Copyright(c) 2006-2007, Ext JS, LLC.
35368  *
35369  * Originally Released Under LGPL - original licence link has changed is not relivant.
35370  *
35371  * Fork - LGPL
35372  * <script type="text/javascript">
35373  */
35374  
35375
35376
35377 /**
35378  * @class Roo.SplitLayoutRegion
35379  * @extends Roo.LayoutRegion
35380  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35381  */
35382 Roo.bootstrap.layout.Split = function(config){
35383     this.cursor = config.cursor;
35384     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35385 };
35386
35387 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35388 {
35389     splitTip : "Drag to resize.",
35390     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35391     useSplitTips : false,
35392
35393     applyConfig : function(config){
35394         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35395     },
35396     
35397     onRender : function(ctr,pos) {
35398         
35399         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35400         if(!this.config.split){
35401             return;
35402         }
35403         if(!this.split){
35404             
35405             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35406                             tag: "div",
35407                             id: this.el.id + "-split",
35408                             cls: "roo-layout-split roo-layout-split-"+this.position,
35409                             html: "&#160;"
35410             });
35411             /** The SplitBar for this region 
35412             * @type Roo.SplitBar */
35413             // does not exist yet...
35414             Roo.log([this.position, this.orientation]);
35415             
35416             this.split = new Roo.bootstrap.SplitBar({
35417                 dragElement : splitEl,
35418                 resizingElement: this.el,
35419                 orientation : this.orientation
35420             });
35421             
35422             this.split.on("moved", this.onSplitMove, this);
35423             this.split.useShim = this.config.useShim === true;
35424             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35425             if(this.useSplitTips){
35426                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35427             }
35428             //if(config.collapsible){
35429             //    this.split.el.on("dblclick", this.collapse,  this);
35430             //}
35431         }
35432         if(typeof this.config.minSize != "undefined"){
35433             this.split.minSize = this.config.minSize;
35434         }
35435         if(typeof this.config.maxSize != "undefined"){
35436             this.split.maxSize = this.config.maxSize;
35437         }
35438         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35439             this.hideSplitter();
35440         }
35441         
35442     },
35443
35444     getHMaxSize : function(){
35445          var cmax = this.config.maxSize || 10000;
35446          var center = this.mgr.getRegion("center");
35447          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35448     },
35449
35450     getVMaxSize : function(){
35451          var cmax = this.config.maxSize || 10000;
35452          var center = this.mgr.getRegion("center");
35453          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35454     },
35455
35456     onSplitMove : function(split, newSize){
35457         this.fireEvent("resized", this, newSize);
35458     },
35459     
35460     /** 
35461      * Returns the {@link Roo.SplitBar} for this region.
35462      * @return {Roo.SplitBar}
35463      */
35464     getSplitBar : function(){
35465         return this.split;
35466     },
35467     
35468     hide : function(){
35469         this.hideSplitter();
35470         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35471     },
35472
35473     hideSplitter : function(){
35474         if(this.split){
35475             this.split.el.setLocation(-2000,-2000);
35476             this.split.el.hide();
35477         }
35478     },
35479
35480     show : function(){
35481         if(this.split){
35482             this.split.el.show();
35483         }
35484         Roo.bootstrap.layout.Split.superclass.show.call(this);
35485     },
35486     
35487     beforeSlide: function(){
35488         if(Roo.isGecko){// firefox overflow auto bug workaround
35489             this.bodyEl.clip();
35490             if(this.tabs) {
35491                 this.tabs.bodyEl.clip();
35492             }
35493             if(this.activePanel){
35494                 this.activePanel.getEl().clip();
35495                 
35496                 if(this.activePanel.beforeSlide){
35497                     this.activePanel.beforeSlide();
35498                 }
35499             }
35500         }
35501     },
35502     
35503     afterSlide : function(){
35504         if(Roo.isGecko){// firefox overflow auto bug workaround
35505             this.bodyEl.unclip();
35506             if(this.tabs) {
35507                 this.tabs.bodyEl.unclip();
35508             }
35509             if(this.activePanel){
35510                 this.activePanel.getEl().unclip();
35511                 if(this.activePanel.afterSlide){
35512                     this.activePanel.afterSlide();
35513                 }
35514             }
35515         }
35516     },
35517
35518     initAutoHide : function(){
35519         if(this.autoHide !== false){
35520             if(!this.autoHideHd){
35521                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35522                 this.autoHideHd = {
35523                     "mouseout": function(e){
35524                         if(!e.within(this.el, true)){
35525                             st.delay(500);
35526                         }
35527                     },
35528                     "mouseover" : function(e){
35529                         st.cancel();
35530                     },
35531                     scope : this
35532                 };
35533             }
35534             this.el.on(this.autoHideHd);
35535         }
35536     },
35537
35538     clearAutoHide : function(){
35539         if(this.autoHide !== false){
35540             this.el.un("mouseout", this.autoHideHd.mouseout);
35541             this.el.un("mouseover", this.autoHideHd.mouseover);
35542         }
35543     },
35544
35545     clearMonitor : function(){
35546         Roo.get(document).un("click", this.slideInIf, this);
35547     },
35548
35549     // these names are backwards but not changed for compat
35550     slideOut : function(){
35551         if(this.isSlid || this.el.hasActiveFx()){
35552             return;
35553         }
35554         this.isSlid = true;
35555         if(this.collapseBtn){
35556             this.collapseBtn.hide();
35557         }
35558         this.closeBtnState = this.closeBtn.getStyle('display');
35559         this.closeBtn.hide();
35560         if(this.stickBtn){
35561             this.stickBtn.show();
35562         }
35563         this.el.show();
35564         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35565         this.beforeSlide();
35566         this.el.setStyle("z-index", 10001);
35567         this.el.slideIn(this.getSlideAnchor(), {
35568             callback: function(){
35569                 this.afterSlide();
35570                 this.initAutoHide();
35571                 Roo.get(document).on("click", this.slideInIf, this);
35572                 this.fireEvent("slideshow", this);
35573             },
35574             scope: this,
35575             block: true
35576         });
35577     },
35578
35579     afterSlideIn : function(){
35580         this.clearAutoHide();
35581         this.isSlid = false;
35582         this.clearMonitor();
35583         this.el.setStyle("z-index", "");
35584         if(this.collapseBtn){
35585             this.collapseBtn.show();
35586         }
35587         this.closeBtn.setStyle('display', this.closeBtnState);
35588         if(this.stickBtn){
35589             this.stickBtn.hide();
35590         }
35591         this.fireEvent("slidehide", this);
35592     },
35593
35594     slideIn : function(cb){
35595         if(!this.isSlid || this.el.hasActiveFx()){
35596             Roo.callback(cb);
35597             return;
35598         }
35599         this.isSlid = false;
35600         this.beforeSlide();
35601         this.el.slideOut(this.getSlideAnchor(), {
35602             callback: function(){
35603                 this.el.setLeftTop(-10000, -10000);
35604                 this.afterSlide();
35605                 this.afterSlideIn();
35606                 Roo.callback(cb);
35607             },
35608             scope: this,
35609             block: true
35610         });
35611     },
35612     
35613     slideInIf : function(e){
35614         if(!e.within(this.el)){
35615             this.slideIn();
35616         }
35617     },
35618
35619     animateCollapse : function(){
35620         this.beforeSlide();
35621         this.el.setStyle("z-index", 20000);
35622         var anchor = this.getSlideAnchor();
35623         this.el.slideOut(anchor, {
35624             callback : function(){
35625                 this.el.setStyle("z-index", "");
35626                 this.collapsedEl.slideIn(anchor, {duration:.3});
35627                 this.afterSlide();
35628                 this.el.setLocation(-10000,-10000);
35629                 this.el.hide();
35630                 this.fireEvent("collapsed", this);
35631             },
35632             scope: this,
35633             block: true
35634         });
35635     },
35636
35637     animateExpand : function(){
35638         this.beforeSlide();
35639         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35640         this.el.setStyle("z-index", 20000);
35641         this.collapsedEl.hide({
35642             duration:.1
35643         });
35644         this.el.slideIn(this.getSlideAnchor(), {
35645             callback : function(){
35646                 this.el.setStyle("z-index", "");
35647                 this.afterSlide();
35648                 if(this.split){
35649                     this.split.el.show();
35650                 }
35651                 this.fireEvent("invalidated", this);
35652                 this.fireEvent("expanded", this);
35653             },
35654             scope: this,
35655             block: true
35656         });
35657     },
35658
35659     anchors : {
35660         "west" : "left",
35661         "east" : "right",
35662         "north" : "top",
35663         "south" : "bottom"
35664     },
35665
35666     sanchors : {
35667         "west" : "l",
35668         "east" : "r",
35669         "north" : "t",
35670         "south" : "b"
35671     },
35672
35673     canchors : {
35674         "west" : "tl-tr",
35675         "east" : "tr-tl",
35676         "north" : "tl-bl",
35677         "south" : "bl-tl"
35678     },
35679
35680     getAnchor : function(){
35681         return this.anchors[this.position];
35682     },
35683
35684     getCollapseAnchor : function(){
35685         return this.canchors[this.position];
35686     },
35687
35688     getSlideAnchor : function(){
35689         return this.sanchors[this.position];
35690     },
35691
35692     getAlignAdj : function(){
35693         var cm = this.cmargins;
35694         switch(this.position){
35695             case "west":
35696                 return [0, 0];
35697             break;
35698             case "east":
35699                 return [0, 0];
35700             break;
35701             case "north":
35702                 return [0, 0];
35703             break;
35704             case "south":
35705                 return [0, 0];
35706             break;
35707         }
35708     },
35709
35710     getExpandAdj : function(){
35711         var c = this.collapsedEl, cm = this.cmargins;
35712         switch(this.position){
35713             case "west":
35714                 return [-(cm.right+c.getWidth()+cm.left), 0];
35715             break;
35716             case "east":
35717                 return [cm.right+c.getWidth()+cm.left, 0];
35718             break;
35719             case "north":
35720                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35721             break;
35722             case "south":
35723                 return [0, cm.top+cm.bottom+c.getHeight()];
35724             break;
35725         }
35726     }
35727 });/*
35728  * Based on:
35729  * Ext JS Library 1.1.1
35730  * Copyright(c) 2006-2007, Ext JS, LLC.
35731  *
35732  * Originally Released Under LGPL - original licence link has changed is not relivant.
35733  *
35734  * Fork - LGPL
35735  * <script type="text/javascript">
35736  */
35737 /*
35738  * These classes are private internal classes
35739  */
35740 Roo.bootstrap.layout.Center = function(config){
35741     config.region = "center";
35742     Roo.bootstrap.layout.Region.call(this, config);
35743     this.visible = true;
35744     this.minWidth = config.minWidth || 20;
35745     this.minHeight = config.minHeight || 20;
35746 };
35747
35748 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35749     hide : function(){
35750         // center panel can't be hidden
35751     },
35752     
35753     show : function(){
35754         // center panel can't be hidden
35755     },
35756     
35757     getMinWidth: function(){
35758         return this.minWidth;
35759     },
35760     
35761     getMinHeight: function(){
35762         return this.minHeight;
35763     }
35764 });
35765
35766
35767
35768
35769  
35770
35771
35772
35773
35774
35775 Roo.bootstrap.layout.North = function(config)
35776 {
35777     config.region = 'north';
35778     config.cursor = 'n-resize';
35779     
35780     Roo.bootstrap.layout.Split.call(this, config);
35781     
35782     
35783     if(this.split){
35784         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35785         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35786         this.split.el.addClass("roo-layout-split-v");
35787     }
35788     var size = config.initialSize || config.height;
35789     if(typeof size != "undefined"){
35790         this.el.setHeight(size);
35791     }
35792 };
35793 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35794 {
35795     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35796     
35797     
35798     
35799     getBox : function(){
35800         if(this.collapsed){
35801             return this.collapsedEl.getBox();
35802         }
35803         var box = this.el.getBox();
35804         if(this.split){
35805             box.height += this.split.el.getHeight();
35806         }
35807         return box;
35808     },
35809     
35810     updateBox : function(box){
35811         if(this.split && !this.collapsed){
35812             box.height -= this.split.el.getHeight();
35813             this.split.el.setLeft(box.x);
35814             this.split.el.setTop(box.y+box.height);
35815             this.split.el.setWidth(box.width);
35816         }
35817         if(this.collapsed){
35818             this.updateBody(box.width, null);
35819         }
35820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35821     }
35822 });
35823
35824
35825
35826
35827
35828 Roo.bootstrap.layout.South = function(config){
35829     config.region = 'south';
35830     config.cursor = 's-resize';
35831     Roo.bootstrap.layout.Split.call(this, config);
35832     if(this.split){
35833         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35834         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35835         this.split.el.addClass("roo-layout-split-v");
35836     }
35837     var size = config.initialSize || config.height;
35838     if(typeof size != "undefined"){
35839         this.el.setHeight(size);
35840     }
35841 };
35842
35843 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35844     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35845     getBox : function(){
35846         if(this.collapsed){
35847             return this.collapsedEl.getBox();
35848         }
35849         var box = this.el.getBox();
35850         if(this.split){
35851             var sh = this.split.el.getHeight();
35852             box.height += sh;
35853             box.y -= sh;
35854         }
35855         return box;
35856     },
35857     
35858     updateBox : function(box){
35859         if(this.split && !this.collapsed){
35860             var sh = this.split.el.getHeight();
35861             box.height -= sh;
35862             box.y += sh;
35863             this.split.el.setLeft(box.x);
35864             this.split.el.setTop(box.y-sh);
35865             this.split.el.setWidth(box.width);
35866         }
35867         if(this.collapsed){
35868             this.updateBody(box.width, null);
35869         }
35870         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35871     }
35872 });
35873
35874 Roo.bootstrap.layout.East = function(config){
35875     config.region = "east";
35876     config.cursor = "e-resize";
35877     Roo.bootstrap.layout.Split.call(this, config);
35878     if(this.split){
35879         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35880         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35881         this.split.el.addClass("roo-layout-split-h");
35882     }
35883     var size = config.initialSize || config.width;
35884     if(typeof size != "undefined"){
35885         this.el.setWidth(size);
35886     }
35887 };
35888 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35889     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35890     getBox : function(){
35891         if(this.collapsed){
35892             return this.collapsedEl.getBox();
35893         }
35894         var box = this.el.getBox();
35895         if(this.split){
35896             var sw = this.split.el.getWidth();
35897             box.width += sw;
35898             box.x -= sw;
35899         }
35900         return box;
35901     },
35902
35903     updateBox : function(box){
35904         if(this.split && !this.collapsed){
35905             var sw = this.split.el.getWidth();
35906             box.width -= sw;
35907             this.split.el.setLeft(box.x);
35908             this.split.el.setTop(box.y);
35909             this.split.el.setHeight(box.height);
35910             box.x += sw;
35911         }
35912         if(this.collapsed){
35913             this.updateBody(null, box.height);
35914         }
35915         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35916     }
35917 });
35918
35919 Roo.bootstrap.layout.West = function(config){
35920     config.region = "west";
35921     config.cursor = "w-resize";
35922     
35923     Roo.bootstrap.layout.Split.call(this, config);
35924     if(this.split){
35925         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35926         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35927         this.split.el.addClass("roo-layout-split-h");
35928     }
35929     
35930 };
35931 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35932     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35933     
35934     onRender: function(ctr, pos)
35935     {
35936         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35937         var size = this.config.initialSize || this.config.width;
35938         if(typeof size != "undefined"){
35939             this.el.setWidth(size);
35940         }
35941     },
35942     
35943     getBox : function(){
35944         if(this.collapsed){
35945             return this.collapsedEl.getBox();
35946         }
35947         var box = this.el.getBox();
35948         if(this.split){
35949             box.width += this.split.el.getWidth();
35950         }
35951         return box;
35952     },
35953     
35954     updateBox : function(box){
35955         if(this.split && !this.collapsed){
35956             var sw = this.split.el.getWidth();
35957             box.width -= sw;
35958             this.split.el.setLeft(box.x+box.width);
35959             this.split.el.setTop(box.y);
35960             this.split.el.setHeight(box.height);
35961         }
35962         if(this.collapsed){
35963             this.updateBody(null, box.height);
35964         }
35965         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35966     }
35967 });
35968 Roo.namespace("Roo.bootstrap.panel");/*
35969  * Based on:
35970  * Ext JS Library 1.1.1
35971  * Copyright(c) 2006-2007, Ext JS, LLC.
35972  *
35973  * Originally Released Under LGPL - original licence link has changed is not relivant.
35974  *
35975  * Fork - LGPL
35976  * <script type="text/javascript">
35977  */
35978 /**
35979  * @class Roo.ContentPanel
35980  * @extends Roo.util.Observable
35981  * A basic ContentPanel element.
35982  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35983  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35984  * @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
35985  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35986  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35987  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35988  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35989  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35990  * @cfg {String} title          The title for this panel
35991  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35992  * @cfg {String} url            Calls {@link #setUrl} with this value
35993  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35994  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35995  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35996  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35997  * @cfg {Boolean} badges render the badges
35998
35999  * @constructor
36000  * Create a new ContentPanel.
36001  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36002  * @param {String/Object} config A string to set only the title or a config object
36003  * @param {String} content (optional) Set the HTML content for this panel
36004  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36005  */
36006 Roo.bootstrap.panel.Content = function( config){
36007     
36008     this.tpl = config.tpl || false;
36009     
36010     var el = config.el;
36011     var content = config.content;
36012
36013     if(config.autoCreate){ // xtype is available if this is called from factory
36014         el = Roo.id();
36015     }
36016     this.el = Roo.get(el);
36017     if(!this.el && config && config.autoCreate){
36018         if(typeof config.autoCreate == "object"){
36019             if(!config.autoCreate.id){
36020                 config.autoCreate.id = config.id||el;
36021             }
36022             this.el = Roo.DomHelper.append(document.body,
36023                         config.autoCreate, true);
36024         }else{
36025             var elcfg =  {   tag: "div",
36026                             cls: "roo-layout-inactive-content",
36027                             id: config.id||el
36028                             };
36029             if (config.html) {
36030                 elcfg.html = config.html;
36031                 
36032             }
36033                         
36034             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36035         }
36036     } 
36037     this.closable = false;
36038     this.loaded = false;
36039     this.active = false;
36040    
36041       
36042     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36043         
36044         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36045         
36046         this.wrapEl = this.el; //this.el.wrap();
36047         var ti = [];
36048         if (config.toolbar.items) {
36049             ti = config.toolbar.items ;
36050             delete config.toolbar.items ;
36051         }
36052         
36053         var nitems = [];
36054         this.toolbar.render(this.wrapEl, 'before');
36055         for(var i =0;i < ti.length;i++) {
36056           //  Roo.log(['add child', items[i]]);
36057             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36058         }
36059         this.toolbar.items = nitems;
36060         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36061         delete config.toolbar;
36062         
36063     }
36064     /*
36065     // xtype created footer. - not sure if will work as we normally have to render first..
36066     if (this.footer && !this.footer.el && this.footer.xtype) {
36067         if (!this.wrapEl) {
36068             this.wrapEl = this.el.wrap();
36069         }
36070     
36071         this.footer.container = this.wrapEl.createChild();
36072          
36073         this.footer = Roo.factory(this.footer, Roo);
36074         
36075     }
36076     */
36077     
36078      if(typeof config == "string"){
36079         this.title = config;
36080     }else{
36081         Roo.apply(this, config);
36082     }
36083     
36084     if(this.resizeEl){
36085         this.resizeEl = Roo.get(this.resizeEl, true);
36086     }else{
36087         this.resizeEl = this.el;
36088     }
36089     // handle view.xtype
36090     
36091  
36092     
36093     
36094     this.addEvents({
36095         /**
36096          * @event activate
36097          * Fires when this panel is activated. 
36098          * @param {Roo.ContentPanel} this
36099          */
36100         "activate" : true,
36101         /**
36102          * @event deactivate
36103          * Fires when this panel is activated. 
36104          * @param {Roo.ContentPanel} this
36105          */
36106         "deactivate" : true,
36107
36108         /**
36109          * @event resize
36110          * Fires when this panel is resized if fitToFrame is true.
36111          * @param {Roo.ContentPanel} this
36112          * @param {Number} width The width after any component adjustments
36113          * @param {Number} height The height after any component adjustments
36114          */
36115         "resize" : true,
36116         
36117          /**
36118          * @event render
36119          * Fires when this tab is created
36120          * @param {Roo.ContentPanel} this
36121          */
36122         "render" : true
36123         
36124         
36125         
36126     });
36127     
36128
36129     
36130     
36131     if(this.autoScroll){
36132         this.resizeEl.setStyle("overflow", "auto");
36133     } else {
36134         // fix randome scrolling
36135         //this.el.on('scroll', function() {
36136         //    Roo.log('fix random scolling');
36137         //    this.scrollTo('top',0); 
36138         //});
36139     }
36140     content = content || this.content;
36141     if(content){
36142         this.setContent(content);
36143     }
36144     if(config && config.url){
36145         this.setUrl(this.url, this.params, this.loadOnce);
36146     }
36147     
36148     
36149     
36150     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36151     
36152     if (this.view && typeof(this.view.xtype) != 'undefined') {
36153         this.view.el = this.el.appendChild(document.createElement("div"));
36154         this.view = Roo.factory(this.view); 
36155         this.view.render  &&  this.view.render(false, '');  
36156     }
36157     
36158     
36159     this.fireEvent('render', this);
36160 };
36161
36162 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36163     
36164     tabTip : '',
36165     
36166     setRegion : function(region){
36167         this.region = region;
36168         this.setActiveClass(region && !this.background);
36169     },
36170     
36171     
36172     setActiveClass: function(state)
36173     {
36174         if(state){
36175            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36176            this.el.setStyle('position','relative');
36177         }else{
36178            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36179            this.el.setStyle('position', 'absolute');
36180         } 
36181     },
36182     
36183     /**
36184      * Returns the toolbar for this Panel if one was configured. 
36185      * @return {Roo.Toolbar} 
36186      */
36187     getToolbar : function(){
36188         return this.toolbar;
36189     },
36190     
36191     setActiveState : function(active)
36192     {
36193         this.active = active;
36194         this.setActiveClass(active);
36195         if(!active){
36196             this.fireEvent("deactivate", this);
36197         }else{
36198             this.fireEvent("activate", this);
36199         }
36200     },
36201     /**
36202      * Updates this panel's element
36203      * @param {String} content The new content
36204      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36205     */
36206     setContent : function(content, loadScripts){
36207         this.el.update(content, loadScripts);
36208     },
36209
36210     ignoreResize : function(w, h){
36211         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36212             return true;
36213         }else{
36214             this.lastSize = {width: w, height: h};
36215             return false;
36216         }
36217     },
36218     /**
36219      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36220      * @return {Roo.UpdateManager} The UpdateManager
36221      */
36222     getUpdateManager : function(){
36223         return this.el.getUpdateManager();
36224     },
36225      /**
36226      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36227      * @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:
36228 <pre><code>
36229 panel.load({
36230     url: "your-url.php",
36231     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36232     callback: yourFunction,
36233     scope: yourObject, //(optional scope)
36234     discardUrl: false,
36235     nocache: false,
36236     text: "Loading...",
36237     timeout: 30,
36238     scripts: false
36239 });
36240 </code></pre>
36241      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36242      * 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.
36243      * @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}
36244      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36245      * @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.
36246      * @return {Roo.ContentPanel} this
36247      */
36248     load : function(){
36249         var um = this.el.getUpdateManager();
36250         um.update.apply(um, arguments);
36251         return this;
36252     },
36253
36254
36255     /**
36256      * 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.
36257      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36258      * @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)
36259      * @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)
36260      * @return {Roo.UpdateManager} The UpdateManager
36261      */
36262     setUrl : function(url, params, loadOnce){
36263         if(this.refreshDelegate){
36264             this.removeListener("activate", this.refreshDelegate);
36265         }
36266         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36267         this.on("activate", this.refreshDelegate);
36268         return this.el.getUpdateManager();
36269     },
36270     
36271     _handleRefresh : function(url, params, loadOnce){
36272         if(!loadOnce || !this.loaded){
36273             var updater = this.el.getUpdateManager();
36274             updater.update(url, params, this._setLoaded.createDelegate(this));
36275         }
36276     },
36277     
36278     _setLoaded : function(){
36279         this.loaded = true;
36280     }, 
36281     
36282     /**
36283      * Returns this panel's id
36284      * @return {String} 
36285      */
36286     getId : function(){
36287         return this.el.id;
36288     },
36289     
36290     /** 
36291      * Returns this panel's element - used by regiosn to add.
36292      * @return {Roo.Element} 
36293      */
36294     getEl : function(){
36295         return this.wrapEl || this.el;
36296     },
36297     
36298    
36299     
36300     adjustForComponents : function(width, height)
36301     {
36302         //Roo.log('adjustForComponents ');
36303         if(this.resizeEl != this.el){
36304             width -= this.el.getFrameWidth('lr');
36305             height -= this.el.getFrameWidth('tb');
36306         }
36307         if(this.toolbar){
36308             var te = this.toolbar.getEl();
36309             te.setWidth(width);
36310             height -= te.getHeight();
36311         }
36312         if(this.footer){
36313             var te = this.footer.getEl();
36314             te.setWidth(width);
36315             height -= te.getHeight();
36316         }
36317         
36318         
36319         if(this.adjustments){
36320             width += this.adjustments[0];
36321             height += this.adjustments[1];
36322         }
36323         return {"width": width, "height": height};
36324     },
36325     
36326     setSize : function(width, height){
36327         if(this.fitToFrame && !this.ignoreResize(width, height)){
36328             if(this.fitContainer && this.resizeEl != this.el){
36329                 this.el.setSize(width, height);
36330             }
36331             var size = this.adjustForComponents(width, height);
36332             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36333             this.fireEvent('resize', this, size.width, size.height);
36334         }
36335     },
36336     
36337     /**
36338      * Returns this panel's title
36339      * @return {String} 
36340      */
36341     getTitle : function(){
36342         
36343         if (typeof(this.title) != 'object') {
36344             return this.title;
36345         }
36346         
36347         var t = '';
36348         for (var k in this.title) {
36349             if (!this.title.hasOwnProperty(k)) {
36350                 continue;
36351             }
36352             
36353             if (k.indexOf('-') >= 0) {
36354                 var s = k.split('-');
36355                 for (var i = 0; i<s.length; i++) {
36356                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36357                 }
36358             } else {
36359                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36360             }
36361         }
36362         return t;
36363     },
36364     
36365     /**
36366      * Set this panel's title
36367      * @param {String} title
36368      */
36369     setTitle : function(title){
36370         this.title = title;
36371         if(this.region){
36372             this.region.updatePanelTitle(this, title);
36373         }
36374     },
36375     
36376     /**
36377      * Returns true is this panel was configured to be closable
36378      * @return {Boolean} 
36379      */
36380     isClosable : function(){
36381         return this.closable;
36382     },
36383     
36384     beforeSlide : function(){
36385         this.el.clip();
36386         this.resizeEl.clip();
36387     },
36388     
36389     afterSlide : function(){
36390         this.el.unclip();
36391         this.resizeEl.unclip();
36392     },
36393     
36394     /**
36395      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36396      *   Will fail silently if the {@link #setUrl} method has not been called.
36397      *   This does not activate the panel, just updates its content.
36398      */
36399     refresh : function(){
36400         if(this.refreshDelegate){
36401            this.loaded = false;
36402            this.refreshDelegate();
36403         }
36404     },
36405     
36406     /**
36407      * Destroys this panel
36408      */
36409     destroy : function(){
36410         this.el.removeAllListeners();
36411         var tempEl = document.createElement("span");
36412         tempEl.appendChild(this.el.dom);
36413         tempEl.innerHTML = "";
36414         this.el.remove();
36415         this.el = null;
36416     },
36417     
36418     /**
36419      * form - if the content panel contains a form - this is a reference to it.
36420      * @type {Roo.form.Form}
36421      */
36422     form : false,
36423     /**
36424      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36425      *    This contains a reference to it.
36426      * @type {Roo.View}
36427      */
36428     view : false,
36429     
36430       /**
36431      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36432      * <pre><code>
36433
36434 layout.addxtype({
36435        xtype : 'Form',
36436        items: [ .... ]
36437    }
36438 );
36439
36440 </code></pre>
36441      * @param {Object} cfg Xtype definition of item to add.
36442      */
36443     
36444     
36445     getChildContainer: function () {
36446         return this.getEl();
36447     }
36448     
36449     
36450     /*
36451         var  ret = new Roo.factory(cfg);
36452         return ret;
36453         
36454         
36455         // add form..
36456         if (cfg.xtype.match(/^Form$/)) {
36457             
36458             var el;
36459             //if (this.footer) {
36460             //    el = this.footer.container.insertSibling(false, 'before');
36461             //} else {
36462                 el = this.el.createChild();
36463             //}
36464
36465             this.form = new  Roo.form.Form(cfg);
36466             
36467             
36468             if ( this.form.allItems.length) {
36469                 this.form.render(el.dom);
36470             }
36471             return this.form;
36472         }
36473         // should only have one of theses..
36474         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36475             // views.. should not be just added - used named prop 'view''
36476             
36477             cfg.el = this.el.appendChild(document.createElement("div"));
36478             // factory?
36479             
36480             var ret = new Roo.factory(cfg);
36481              
36482              ret.render && ret.render(false, ''); // render blank..
36483             this.view = ret;
36484             return ret;
36485         }
36486         return false;
36487     }
36488     \*/
36489 });
36490  
36491 /**
36492  * @class Roo.bootstrap.panel.Grid
36493  * @extends Roo.bootstrap.panel.Content
36494  * @constructor
36495  * Create a new GridPanel.
36496  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36497  * @param {Object} config A the config object
36498   
36499  */
36500
36501
36502
36503 Roo.bootstrap.panel.Grid = function(config)
36504 {
36505     
36506       
36507     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36508         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36509
36510     config.el = this.wrapper;
36511     //this.el = this.wrapper;
36512     
36513       if (config.container) {
36514         // ctor'ed from a Border/panel.grid
36515         
36516         
36517         this.wrapper.setStyle("overflow", "hidden");
36518         this.wrapper.addClass('roo-grid-container');
36519
36520     }
36521     
36522     
36523     if(config.toolbar){
36524         var tool_el = this.wrapper.createChild();    
36525         this.toolbar = Roo.factory(config.toolbar);
36526         var ti = [];
36527         if (config.toolbar.items) {
36528             ti = config.toolbar.items ;
36529             delete config.toolbar.items ;
36530         }
36531         
36532         var nitems = [];
36533         this.toolbar.render(tool_el);
36534         for(var i =0;i < ti.length;i++) {
36535           //  Roo.log(['add child', items[i]]);
36536             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36537         }
36538         this.toolbar.items = nitems;
36539         
36540         delete config.toolbar;
36541     }
36542     
36543     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36544     config.grid.scrollBody = true;;
36545     config.grid.monitorWindowResize = false; // turn off autosizing
36546     config.grid.autoHeight = false;
36547     config.grid.autoWidth = false;
36548     
36549     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36550     
36551     if (config.background) {
36552         // render grid on panel activation (if panel background)
36553         this.on('activate', function(gp) {
36554             if (!gp.grid.rendered) {
36555                 gp.grid.render(this.wrapper);
36556                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36557             }
36558         });
36559             
36560     } else {
36561         this.grid.render(this.wrapper);
36562         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36563
36564     }
36565     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36566     // ??? needed ??? config.el = this.wrapper;
36567     
36568     
36569     
36570   
36571     // xtype created footer. - not sure if will work as we normally have to render first..
36572     if (this.footer && !this.footer.el && this.footer.xtype) {
36573         
36574         var ctr = this.grid.getView().getFooterPanel(true);
36575         this.footer.dataSource = this.grid.dataSource;
36576         this.footer = Roo.factory(this.footer, Roo);
36577         this.footer.render(ctr);
36578         
36579     }
36580     
36581     
36582     
36583     
36584      
36585 };
36586
36587 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36588     getId : function(){
36589         return this.grid.id;
36590     },
36591     
36592     /**
36593      * Returns the grid for this panel
36594      * @return {Roo.bootstrap.Table} 
36595      */
36596     getGrid : function(){
36597         return this.grid;    
36598     },
36599     
36600     setSize : function(width, height){
36601         if(!this.ignoreResize(width, height)){
36602             var grid = this.grid;
36603             var size = this.adjustForComponents(width, height);
36604             var gridel = grid.getGridEl();
36605             gridel.setSize(size.width, size.height);
36606             /*
36607             var thd = grid.getGridEl().select('thead',true).first();
36608             var tbd = grid.getGridEl().select('tbody', true).first();
36609             if (tbd) {
36610                 tbd.setSize(width, height - thd.getHeight());
36611             }
36612             */
36613             grid.autoSize();
36614         }
36615     },
36616      
36617     
36618     
36619     beforeSlide : function(){
36620         this.grid.getView().scroller.clip();
36621     },
36622     
36623     afterSlide : function(){
36624         this.grid.getView().scroller.unclip();
36625     },
36626     
36627     destroy : function(){
36628         this.grid.destroy();
36629         delete this.grid;
36630         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36631     }
36632 });
36633
36634 /**
36635  * @class Roo.bootstrap.panel.Nest
36636  * @extends Roo.bootstrap.panel.Content
36637  * @constructor
36638  * Create a new Panel, that can contain a layout.Border.
36639  * 
36640  * 
36641  * @param {Roo.BorderLayout} layout The layout for this panel
36642  * @param {String/Object} config A string to set only the title or a config object
36643  */
36644 Roo.bootstrap.panel.Nest = function(config)
36645 {
36646     // construct with only one argument..
36647     /* FIXME - implement nicer consturctors
36648     if (layout.layout) {
36649         config = layout;
36650         layout = config.layout;
36651         delete config.layout;
36652     }
36653     if (layout.xtype && !layout.getEl) {
36654         // then layout needs constructing..
36655         layout = Roo.factory(layout, Roo);
36656     }
36657     */
36658     
36659     config.el =  config.layout.getEl();
36660     
36661     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36662     
36663     config.layout.monitorWindowResize = false; // turn off autosizing
36664     this.layout = config.layout;
36665     this.layout.getEl().addClass("roo-layout-nested-layout");
36666     
36667     
36668     
36669     
36670 };
36671
36672 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36673
36674     setSize : function(width, height){
36675         if(!this.ignoreResize(width, height)){
36676             var size = this.adjustForComponents(width, height);
36677             var el = this.layout.getEl();
36678             if (size.height < 1) {
36679                 el.setWidth(size.width);   
36680             } else {
36681                 el.setSize(size.width, size.height);
36682             }
36683             var touch = el.dom.offsetWidth;
36684             this.layout.layout();
36685             // ie requires a double layout on the first pass
36686             if(Roo.isIE && !this.initialized){
36687                 this.initialized = true;
36688                 this.layout.layout();
36689             }
36690         }
36691     },
36692     
36693     // activate all subpanels if not currently active..
36694     
36695     setActiveState : function(active){
36696         this.active = active;
36697         this.setActiveClass(active);
36698         
36699         if(!active){
36700             this.fireEvent("deactivate", this);
36701             return;
36702         }
36703         
36704         this.fireEvent("activate", this);
36705         // not sure if this should happen before or after..
36706         if (!this.layout) {
36707             return; // should not happen..
36708         }
36709         var reg = false;
36710         for (var r in this.layout.regions) {
36711             reg = this.layout.getRegion(r);
36712             if (reg.getActivePanel()) {
36713                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36714                 reg.setActivePanel(reg.getActivePanel());
36715                 continue;
36716             }
36717             if (!reg.panels.length) {
36718                 continue;
36719             }
36720             reg.showPanel(reg.getPanel(0));
36721         }
36722         
36723         
36724         
36725         
36726     },
36727     
36728     /**
36729      * Returns the nested BorderLayout for this panel
36730      * @return {Roo.BorderLayout} 
36731      */
36732     getLayout : function(){
36733         return this.layout;
36734     },
36735     
36736      /**
36737      * Adds a xtype elements to the layout of the nested panel
36738      * <pre><code>
36739
36740 panel.addxtype({
36741        xtype : 'ContentPanel',
36742        region: 'west',
36743        items: [ .... ]
36744    }
36745 );
36746
36747 panel.addxtype({
36748         xtype : 'NestedLayoutPanel',
36749         region: 'west',
36750         layout: {
36751            center: { },
36752            west: { }   
36753         },
36754         items : [ ... list of content panels or nested layout panels.. ]
36755    }
36756 );
36757 </code></pre>
36758      * @param {Object} cfg Xtype definition of item to add.
36759      */
36760     addxtype : function(cfg) {
36761         return this.layout.addxtype(cfg);
36762     
36763     }
36764 });        /*
36765  * Based on:
36766  * Ext JS Library 1.1.1
36767  * Copyright(c) 2006-2007, Ext JS, LLC.
36768  *
36769  * Originally Released Under LGPL - original licence link has changed is not relivant.
36770  *
36771  * Fork - LGPL
36772  * <script type="text/javascript">
36773  */
36774 /**
36775  * @class Roo.TabPanel
36776  * @extends Roo.util.Observable
36777  * A lightweight tab container.
36778  * <br><br>
36779  * Usage:
36780  * <pre><code>
36781 // basic tabs 1, built from existing content
36782 var tabs = new Roo.TabPanel("tabs1");
36783 tabs.addTab("script", "View Script");
36784 tabs.addTab("markup", "View Markup");
36785 tabs.activate("script");
36786
36787 // more advanced tabs, built from javascript
36788 var jtabs = new Roo.TabPanel("jtabs");
36789 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36790
36791 // set up the UpdateManager
36792 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36793 var updater = tab2.getUpdateManager();
36794 updater.setDefaultUrl("ajax1.htm");
36795 tab2.on('activate', updater.refresh, updater, true);
36796
36797 // Use setUrl for Ajax loading
36798 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36799 tab3.setUrl("ajax2.htm", null, true);
36800
36801 // Disabled tab
36802 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36803 tab4.disable();
36804
36805 jtabs.activate("jtabs-1");
36806  * </code></pre>
36807  * @constructor
36808  * Create a new TabPanel.
36809  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36810  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36811  */
36812 Roo.bootstrap.panel.Tabs = function(config){
36813     /**
36814     * The container element for this TabPanel.
36815     * @type Roo.Element
36816     */
36817     this.el = Roo.get(config.el);
36818     delete config.el;
36819     if(config){
36820         if(typeof config == "boolean"){
36821             this.tabPosition = config ? "bottom" : "top";
36822         }else{
36823             Roo.apply(this, config);
36824         }
36825     }
36826     
36827     if(this.tabPosition == "bottom"){
36828         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36829         this.el.addClass("roo-tabs-bottom");
36830     }
36831     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36832     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36833     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36834     if(Roo.isIE){
36835         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36836     }
36837     if(this.tabPosition != "bottom"){
36838         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36839          * @type Roo.Element
36840          */
36841         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36842         this.el.addClass("roo-tabs-top");
36843     }
36844     this.items = [];
36845
36846     this.bodyEl.setStyle("position", "relative");
36847
36848     this.active = null;
36849     this.activateDelegate = this.activate.createDelegate(this);
36850
36851     this.addEvents({
36852         /**
36853          * @event tabchange
36854          * Fires when the active tab changes
36855          * @param {Roo.TabPanel} this
36856          * @param {Roo.TabPanelItem} activePanel The new active tab
36857          */
36858         "tabchange": true,
36859         /**
36860          * @event beforetabchange
36861          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36862          * @param {Roo.TabPanel} this
36863          * @param {Object} e Set cancel to true on this object to cancel the tab change
36864          * @param {Roo.TabPanelItem} tab The tab being changed to
36865          */
36866         "beforetabchange" : true
36867     });
36868
36869     Roo.EventManager.onWindowResize(this.onResize, this);
36870     this.cpad = this.el.getPadding("lr");
36871     this.hiddenCount = 0;
36872
36873
36874     // toolbar on the tabbar support...
36875     if (this.toolbar) {
36876         alert("no toolbar support yet");
36877         this.toolbar  = false;
36878         /*
36879         var tcfg = this.toolbar;
36880         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36881         this.toolbar = new Roo.Toolbar(tcfg);
36882         if (Roo.isSafari) {
36883             var tbl = tcfg.container.child('table', true);
36884             tbl.setAttribute('width', '100%');
36885         }
36886         */
36887         
36888     }
36889    
36890
36891
36892     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36893 };
36894
36895 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36896     /*
36897      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36898      */
36899     tabPosition : "top",
36900     /*
36901      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36902      */
36903     currentTabWidth : 0,
36904     /*
36905      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36906      */
36907     minTabWidth : 40,
36908     /*
36909      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36910      */
36911     maxTabWidth : 250,
36912     /*
36913      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36914      */
36915     preferredTabWidth : 175,
36916     /*
36917      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36918      */
36919     resizeTabs : false,
36920     /*
36921      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36922      */
36923     monitorResize : true,
36924     /*
36925      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36926      */
36927     toolbar : false,
36928
36929     /**
36930      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36931      * @param {String} id The id of the div to use <b>or create</b>
36932      * @param {String} text The text for the tab
36933      * @param {String} content (optional) Content to put in the TabPanelItem body
36934      * @param {Boolean} closable (optional) True to create a close icon on the tab
36935      * @return {Roo.TabPanelItem} The created TabPanelItem
36936      */
36937     addTab : function(id, text, content, closable, tpl)
36938     {
36939         var item = new Roo.bootstrap.panel.TabItem({
36940             panel: this,
36941             id : id,
36942             text : text,
36943             closable : closable,
36944             tpl : tpl
36945         });
36946         this.addTabItem(item);
36947         if(content){
36948             item.setContent(content);
36949         }
36950         return item;
36951     },
36952
36953     /**
36954      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36955      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36956      * @return {Roo.TabPanelItem}
36957      */
36958     getTab : function(id){
36959         return this.items[id];
36960     },
36961
36962     /**
36963      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36964      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36965      */
36966     hideTab : function(id){
36967         var t = this.items[id];
36968         if(!t.isHidden()){
36969            t.setHidden(true);
36970            this.hiddenCount++;
36971            this.autoSizeTabs();
36972         }
36973     },
36974
36975     /**
36976      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36977      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36978      */
36979     unhideTab : function(id){
36980         var t = this.items[id];
36981         if(t.isHidden()){
36982            t.setHidden(false);
36983            this.hiddenCount--;
36984            this.autoSizeTabs();
36985         }
36986     },
36987
36988     /**
36989      * Adds an existing {@link Roo.TabPanelItem}.
36990      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36991      */
36992     addTabItem : function(item){
36993         this.items[item.id] = item;
36994         this.items.push(item);
36995       //  if(this.resizeTabs){
36996     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36997   //         this.autoSizeTabs();
36998 //        }else{
36999 //            item.autoSize();
37000        // }
37001     },
37002
37003     /**
37004      * Removes a {@link Roo.TabPanelItem}.
37005      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37006      */
37007     removeTab : function(id){
37008         var items = this.items;
37009         var tab = items[id];
37010         if(!tab) { return; }
37011         var index = items.indexOf(tab);
37012         if(this.active == tab && items.length > 1){
37013             var newTab = this.getNextAvailable(index);
37014             if(newTab) {
37015                 newTab.activate();
37016             }
37017         }
37018         this.stripEl.dom.removeChild(tab.pnode.dom);
37019         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37020             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37021         }
37022         items.splice(index, 1);
37023         delete this.items[tab.id];
37024         tab.fireEvent("close", tab);
37025         tab.purgeListeners();
37026         this.autoSizeTabs();
37027     },
37028
37029     getNextAvailable : function(start){
37030         var items = this.items;
37031         var index = start;
37032         // look for a next tab that will slide over to
37033         // replace the one being removed
37034         while(index < items.length){
37035             var item = items[++index];
37036             if(item && !item.isHidden()){
37037                 return item;
37038             }
37039         }
37040         // if one isn't found select the previous tab (on the left)
37041         index = start;
37042         while(index >= 0){
37043             var item = items[--index];
37044             if(item && !item.isHidden()){
37045                 return item;
37046             }
37047         }
37048         return null;
37049     },
37050
37051     /**
37052      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37053      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37054      */
37055     disableTab : function(id){
37056         var tab = this.items[id];
37057         if(tab && this.active != tab){
37058             tab.disable();
37059         }
37060     },
37061
37062     /**
37063      * Enables a {@link Roo.TabPanelItem} that is disabled.
37064      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37065      */
37066     enableTab : function(id){
37067         var tab = this.items[id];
37068         tab.enable();
37069     },
37070
37071     /**
37072      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37073      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37074      * @return {Roo.TabPanelItem} The TabPanelItem.
37075      */
37076     activate : function(id){
37077         var tab = this.items[id];
37078         if(!tab){
37079             return null;
37080         }
37081         if(tab == this.active || tab.disabled){
37082             return tab;
37083         }
37084         var e = {};
37085         this.fireEvent("beforetabchange", this, e, tab);
37086         if(e.cancel !== true && !tab.disabled){
37087             if(this.active){
37088                 this.active.hide();
37089             }
37090             this.active = this.items[id];
37091             this.active.show();
37092             this.fireEvent("tabchange", this, this.active);
37093         }
37094         return tab;
37095     },
37096
37097     /**
37098      * Gets the active {@link Roo.TabPanelItem}.
37099      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37100      */
37101     getActiveTab : function(){
37102         return this.active;
37103     },
37104
37105     /**
37106      * Updates the tab body element to fit the height of the container element
37107      * for overflow scrolling
37108      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37109      */
37110     syncHeight : function(targetHeight){
37111         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37112         var bm = this.bodyEl.getMargins();
37113         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37114         this.bodyEl.setHeight(newHeight);
37115         return newHeight;
37116     },
37117
37118     onResize : function(){
37119         if(this.monitorResize){
37120             this.autoSizeTabs();
37121         }
37122     },
37123
37124     /**
37125      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37126      */
37127     beginUpdate : function(){
37128         this.updating = true;
37129     },
37130
37131     /**
37132      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37133      */
37134     endUpdate : function(){
37135         this.updating = false;
37136         this.autoSizeTabs();
37137     },
37138
37139     /**
37140      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37141      */
37142     autoSizeTabs : function(){
37143         var count = this.items.length;
37144         var vcount = count - this.hiddenCount;
37145         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37146             return;
37147         }
37148         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37149         var availWidth = Math.floor(w / vcount);
37150         var b = this.stripBody;
37151         if(b.getWidth() > w){
37152             var tabs = this.items;
37153             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37154             if(availWidth < this.minTabWidth){
37155                 /*if(!this.sleft){    // incomplete scrolling code
37156                     this.createScrollButtons();
37157                 }
37158                 this.showScroll();
37159                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37160             }
37161         }else{
37162             if(this.currentTabWidth < this.preferredTabWidth){
37163                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37164             }
37165         }
37166     },
37167
37168     /**
37169      * Returns the number of tabs in this TabPanel.
37170      * @return {Number}
37171      */
37172      getCount : function(){
37173          return this.items.length;
37174      },
37175
37176     /**
37177      * Resizes all the tabs to the passed width
37178      * @param {Number} The new width
37179      */
37180     setTabWidth : function(width){
37181         this.currentTabWidth = width;
37182         for(var i = 0, len = this.items.length; i < len; i++) {
37183                 if(!this.items[i].isHidden()) {
37184                 this.items[i].setWidth(width);
37185             }
37186         }
37187     },
37188
37189     /**
37190      * Destroys this TabPanel
37191      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37192      */
37193     destroy : function(removeEl){
37194         Roo.EventManager.removeResizeListener(this.onResize, this);
37195         for(var i = 0, len = this.items.length; i < len; i++){
37196             this.items[i].purgeListeners();
37197         }
37198         if(removeEl === true){
37199             this.el.update("");
37200             this.el.remove();
37201         }
37202     },
37203     
37204     createStrip : function(container)
37205     {
37206         var strip = document.createElement("nav");
37207         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37208         container.appendChild(strip);
37209         return strip;
37210     },
37211     
37212     createStripList : function(strip)
37213     {
37214         // div wrapper for retard IE
37215         // returns the "tr" element.
37216         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37217         //'<div class="x-tabs-strip-wrap">'+
37218           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37219           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37220         return strip.firstChild; //.firstChild.firstChild.firstChild;
37221     },
37222     createBody : function(container)
37223     {
37224         var body = document.createElement("div");
37225         Roo.id(body, "tab-body");
37226         //Roo.fly(body).addClass("x-tabs-body");
37227         Roo.fly(body).addClass("tab-content");
37228         container.appendChild(body);
37229         return body;
37230     },
37231     createItemBody :function(bodyEl, id){
37232         var body = Roo.getDom(id);
37233         if(!body){
37234             body = document.createElement("div");
37235             body.id = id;
37236         }
37237         //Roo.fly(body).addClass("x-tabs-item-body");
37238         Roo.fly(body).addClass("tab-pane");
37239          bodyEl.insertBefore(body, bodyEl.firstChild);
37240         return body;
37241     },
37242     /** @private */
37243     createStripElements :  function(stripEl, text, closable, tpl)
37244     {
37245         var td = document.createElement("li"); // was td..
37246         
37247         
37248         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37249         
37250         
37251         stripEl.appendChild(td);
37252         /*if(closable){
37253             td.className = "x-tabs-closable";
37254             if(!this.closeTpl){
37255                 this.closeTpl = new Roo.Template(
37256                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37257                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37258                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37259                 );
37260             }
37261             var el = this.closeTpl.overwrite(td, {"text": text});
37262             var close = el.getElementsByTagName("div")[0];
37263             var inner = el.getElementsByTagName("em")[0];
37264             return {"el": el, "close": close, "inner": inner};
37265         } else {
37266         */
37267         // not sure what this is..
37268 //            if(!this.tabTpl){
37269                 //this.tabTpl = new Roo.Template(
37270                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37271                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37272                 //);
37273 //                this.tabTpl = new Roo.Template(
37274 //                   '<a href="#">' +
37275 //                   '<span unselectable="on"' +
37276 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37277 //                            ' >{text}</span></a>'
37278 //                );
37279 //                
37280 //            }
37281
37282
37283             var template = tpl || this.tabTpl || false;
37284             
37285             if(!template){
37286                 
37287                 template = new Roo.Template(
37288                    '<a href="#">' +
37289                    '<span unselectable="on"' +
37290                             (this.disableTooltips ? '' : ' title="{text}"') +
37291                             ' >{text}</span></a>'
37292                 );
37293             }
37294             
37295             switch (typeof(template)) {
37296                 case 'object' :
37297                     break;
37298                 case 'string' :
37299                     template = new Roo.Template(template);
37300                     break;
37301                 default :
37302                     break;
37303             }
37304             
37305             var el = template.overwrite(td, {"text": text});
37306             
37307             var inner = el.getElementsByTagName("span")[0];
37308             
37309             return {"el": el, "inner": inner};
37310             
37311     }
37312         
37313     
37314 });
37315
37316 /**
37317  * @class Roo.TabPanelItem
37318  * @extends Roo.util.Observable
37319  * Represents an individual item (tab plus body) in a TabPanel.
37320  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37321  * @param {String} id The id of this TabPanelItem
37322  * @param {String} text The text for the tab of this TabPanelItem
37323  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37324  */
37325 Roo.bootstrap.panel.TabItem = function(config){
37326     /**
37327      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37328      * @type Roo.TabPanel
37329      */
37330     this.tabPanel = config.panel;
37331     /**
37332      * The id for this TabPanelItem
37333      * @type String
37334      */
37335     this.id = config.id;
37336     /** @private */
37337     this.disabled = false;
37338     /** @private */
37339     this.text = config.text;
37340     /** @private */
37341     this.loaded = false;
37342     this.closable = config.closable;
37343
37344     /**
37345      * The body element for this TabPanelItem.
37346      * @type Roo.Element
37347      */
37348     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37349     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37350     this.bodyEl.setStyle("display", "block");
37351     this.bodyEl.setStyle("zoom", "1");
37352     //this.hideAction();
37353
37354     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37355     /** @private */
37356     this.el = Roo.get(els.el);
37357     this.inner = Roo.get(els.inner, true);
37358     this.textEl = Roo.get(this.el.dom.firstChild, true);
37359     this.pnode = Roo.get(els.el.parentNode, true);
37360     this.el.on("mousedown", this.onTabMouseDown, this);
37361     this.el.on("click", this.onTabClick, this);
37362     /** @private */
37363     if(config.closable){
37364         var c = Roo.get(els.close, true);
37365         c.dom.title = this.closeText;
37366         c.addClassOnOver("close-over");
37367         c.on("click", this.closeClick, this);
37368      }
37369
37370     this.addEvents({
37371          /**
37372          * @event activate
37373          * Fires when this tab becomes the active tab.
37374          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37375          * @param {Roo.TabPanelItem} this
37376          */
37377         "activate": true,
37378         /**
37379          * @event beforeclose
37380          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37381          * @param {Roo.TabPanelItem} this
37382          * @param {Object} e Set cancel to true on this object to cancel the close.
37383          */
37384         "beforeclose": true,
37385         /**
37386          * @event close
37387          * Fires when this tab is closed.
37388          * @param {Roo.TabPanelItem} this
37389          */
37390          "close": true,
37391         /**
37392          * @event deactivate
37393          * Fires when this tab is no longer the active tab.
37394          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37395          * @param {Roo.TabPanelItem} this
37396          */
37397          "deactivate" : true
37398     });
37399     this.hidden = false;
37400
37401     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37402 };
37403
37404 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37405            {
37406     purgeListeners : function(){
37407        Roo.util.Observable.prototype.purgeListeners.call(this);
37408        this.el.removeAllListeners();
37409     },
37410     /**
37411      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37412      */
37413     show : function(){
37414         this.pnode.addClass("active");
37415         this.showAction();
37416         if(Roo.isOpera){
37417             this.tabPanel.stripWrap.repaint();
37418         }
37419         this.fireEvent("activate", this.tabPanel, this);
37420     },
37421
37422     /**
37423      * Returns true if this tab is the active tab.
37424      * @return {Boolean}
37425      */
37426     isActive : function(){
37427         return this.tabPanel.getActiveTab() == this;
37428     },
37429
37430     /**
37431      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37432      */
37433     hide : function(){
37434         this.pnode.removeClass("active");
37435         this.hideAction();
37436         this.fireEvent("deactivate", this.tabPanel, this);
37437     },
37438
37439     hideAction : function(){
37440         this.bodyEl.hide();
37441         this.bodyEl.setStyle("position", "absolute");
37442         this.bodyEl.setLeft("-20000px");
37443         this.bodyEl.setTop("-20000px");
37444     },
37445
37446     showAction : function(){
37447         this.bodyEl.setStyle("position", "relative");
37448         this.bodyEl.setTop("");
37449         this.bodyEl.setLeft("");
37450         this.bodyEl.show();
37451     },
37452
37453     /**
37454      * Set the tooltip for the tab.
37455      * @param {String} tooltip The tab's tooltip
37456      */
37457     setTooltip : function(text){
37458         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37459             this.textEl.dom.qtip = text;
37460             this.textEl.dom.removeAttribute('title');
37461         }else{
37462             this.textEl.dom.title = text;
37463         }
37464     },
37465
37466     onTabClick : function(e){
37467         e.preventDefault();
37468         this.tabPanel.activate(this.id);
37469     },
37470
37471     onTabMouseDown : function(e){
37472         e.preventDefault();
37473         this.tabPanel.activate(this.id);
37474     },
37475 /*
37476     getWidth : function(){
37477         return this.inner.getWidth();
37478     },
37479
37480     setWidth : function(width){
37481         var iwidth = width - this.pnode.getPadding("lr");
37482         this.inner.setWidth(iwidth);
37483         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37484         this.pnode.setWidth(width);
37485     },
37486 */
37487     /**
37488      * Show or hide the tab
37489      * @param {Boolean} hidden True to hide or false to show.
37490      */
37491     setHidden : function(hidden){
37492         this.hidden = hidden;
37493         this.pnode.setStyle("display", hidden ? "none" : "");
37494     },
37495
37496     /**
37497      * Returns true if this tab is "hidden"
37498      * @return {Boolean}
37499      */
37500     isHidden : function(){
37501         return this.hidden;
37502     },
37503
37504     /**
37505      * Returns the text for this tab
37506      * @return {String}
37507      */
37508     getText : function(){
37509         return this.text;
37510     },
37511     /*
37512     autoSize : function(){
37513         //this.el.beginMeasure();
37514         this.textEl.setWidth(1);
37515         /*
37516          *  #2804 [new] Tabs in Roojs
37517          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37518          */
37519         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37520         //this.el.endMeasure();
37521     //},
37522
37523     /**
37524      * Sets the text for the tab (Note: this also sets the tooltip text)
37525      * @param {String} text The tab's text and tooltip
37526      */
37527     setText : function(text){
37528         this.text = text;
37529         this.textEl.update(text);
37530         this.setTooltip(text);
37531         //if(!this.tabPanel.resizeTabs){
37532         //    this.autoSize();
37533         //}
37534     },
37535     /**
37536      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37537      */
37538     activate : function(){
37539         this.tabPanel.activate(this.id);
37540     },
37541
37542     /**
37543      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37544      */
37545     disable : function(){
37546         if(this.tabPanel.active != this){
37547             this.disabled = true;
37548             this.pnode.addClass("disabled");
37549         }
37550     },
37551
37552     /**
37553      * Enables this TabPanelItem if it was previously disabled.
37554      */
37555     enable : function(){
37556         this.disabled = false;
37557         this.pnode.removeClass("disabled");
37558     },
37559
37560     /**
37561      * Sets the content for this TabPanelItem.
37562      * @param {String} content The content
37563      * @param {Boolean} loadScripts true to look for and load scripts
37564      */
37565     setContent : function(content, loadScripts){
37566         this.bodyEl.update(content, loadScripts);
37567     },
37568
37569     /**
37570      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37571      * @return {Roo.UpdateManager} The UpdateManager
37572      */
37573     getUpdateManager : function(){
37574         return this.bodyEl.getUpdateManager();
37575     },
37576
37577     /**
37578      * Set a URL to be used to load the content for this TabPanelItem.
37579      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37580      * @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)
37581      * @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)
37582      * @return {Roo.UpdateManager} The UpdateManager
37583      */
37584     setUrl : function(url, params, loadOnce){
37585         if(this.refreshDelegate){
37586             this.un('activate', this.refreshDelegate);
37587         }
37588         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37589         this.on("activate", this.refreshDelegate);
37590         return this.bodyEl.getUpdateManager();
37591     },
37592
37593     /** @private */
37594     _handleRefresh : function(url, params, loadOnce){
37595         if(!loadOnce || !this.loaded){
37596             var updater = this.bodyEl.getUpdateManager();
37597             updater.update(url, params, this._setLoaded.createDelegate(this));
37598         }
37599     },
37600
37601     /**
37602      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37603      *   Will fail silently if the setUrl method has not been called.
37604      *   This does not activate the panel, just updates its content.
37605      */
37606     refresh : function(){
37607         if(this.refreshDelegate){
37608            this.loaded = false;
37609            this.refreshDelegate();
37610         }
37611     },
37612
37613     /** @private */
37614     _setLoaded : function(){
37615         this.loaded = true;
37616     },
37617
37618     /** @private */
37619     closeClick : function(e){
37620         var o = {};
37621         e.stopEvent();
37622         this.fireEvent("beforeclose", this, o);
37623         if(o.cancel !== true){
37624             this.tabPanel.removeTab(this.id);
37625         }
37626     },
37627     /**
37628      * The text displayed in the tooltip for the close icon.
37629      * @type String
37630      */
37631     closeText : "Close this tab"
37632 });