5cc3839e17e91c1acf2d239938ccf90c1a2b393b
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.weightClass = ["btn-default", 
538                        "btn-primary", 
539                        "btn-success", 
540                        "btn-info", 
541                        "btn-warning",
542                        "btn-danger",
543                        "btn-link"
544                       ],  
545     this.addEvents({
546         // raw events
547         /**
548          * @event click
549          * When a butotn is pressed
550          * @param {Roo.bootstrap.Button} this
551          * @param {Roo.EventObject} e
552          */
553         "click" : true,
554          /**
555          * @event toggle
556          * After the button has been toggles
557          * @param {Roo.EventObject} e
558          * @param {boolean} pressed (also available as button.pressed)
559          */
560         "toggle" : true
561     });
562 };
563
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
565     html: false,
566     active: false,
567     weight: '',
568     size: '',
569     tag: 'button',
570     href: '',
571     disabled: false,
572     isClose: false,
573     glyphicon: '',
574     badge: '',
575     theme: 'default',
576     inverse: false,
577     
578     toggle: false,
579     ontext: 'ON',
580     offtext: 'OFF',
581     defaulton: true,
582     preventDefault: true,
583     removeClass: false,
584     name: false,
585     target: false,
586     
587     
588     pressed : null,
589      
590     
591     getAutoCreate : function(){
592         
593         var cfg = {
594             tag : 'button',
595             cls : 'roo-button',
596             html: ''
597         };
598         
599         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
601             this.tag = 'button';
602         } else {
603             cfg.tag = this.tag;
604         }
605         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606         
607         if (this.toggle == true) {
608             cfg={
609                 tag: 'div',
610                 cls: 'slider-frame roo-button',
611                 cn: [
612                     {
613                         tag: 'span',
614                         'data-on-text':'ON',
615                         'data-off-text':'OFF',
616                         cls: 'slider-button',
617                         html: this.offtext
618                     }
619                 ]
620             };
621             
622             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623                 cfg.cls += ' '+this.weight;
624             }
625             
626             return cfg;
627         }
628         
629         if (this.isClose) {
630             cfg.cls += ' close';
631             
632             cfg["aria-hidden"] = true;
633             
634             cfg.html = "&times;";
635             
636             return cfg;
637         }
638         
639          
640         if (this.theme==='default') {
641             cfg.cls = 'btn roo-button';
642             
643             //if (this.parentType != 'Navbar') {
644             this.weight = this.weight.length ?  this.weight : 'default';
645             //}
646             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647                 
648                 cfg.cls += ' btn-' + this.weight;
649             }
650         } else if (this.theme==='glow') {
651             
652             cfg.tag = 'a';
653             cfg.cls = 'btn-glow roo-button';
654             
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' ' + this.weight;
658             }
659         }
660    
661         
662         if (this.inverse) {
663             this.cls += ' inverse';
664         }
665         
666         
667         if (this.active) {
668             cfg.cls += ' active';
669         }
670         
671         if (this.disabled) {
672             cfg.disabled = 'disabled';
673         }
674         
675         if (this.items) {
676             Roo.log('changing to ul' );
677             cfg.tag = 'ul';
678             this.glyphicon = 'caret';
679         }
680         
681         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682          
683         //gsRoo.log(this.parentType);
684         if (this.parentType === 'Navbar' && !this.parent().bar) {
685             Roo.log('changing to li?');
686             
687             cfg.tag = 'li';
688             
689             cfg.cls = '';
690             cfg.cn =  [{
691                 tag : 'a',
692                 cls : 'roo-button',
693                 html : this.html,
694                 href : this.href || '#'
695             }];
696             if (this.menu) {
697                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
698                 cfg.cls += ' dropdown';
699             }   
700             
701             delete cfg.html;
702             
703         }
704         
705        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
706         
707         if (this.glyphicon) {
708             cfg.html = ' ' + cfg.html;
709             
710             cfg.cn = [
711                 {
712                     tag: 'span',
713                     cls: 'glyphicon glyphicon-' + this.glyphicon
714                 }
715             ];
716         }
717         
718         if (this.badge) {
719             cfg.html += ' ';
720             
721             cfg.tag = 'a';
722             
723 //            cfg.cls='btn roo-button';
724             
725             cfg.href=this.href;
726             
727             var value = cfg.html;
728             
729             if(this.glyphicon){
730                 value = {
731                             tag: 'span',
732                             cls: 'glyphicon glyphicon-' + this.glyphicon,
733                             html: this.html
734                         };
735                 
736             }
737             
738             cfg.cn = [
739                 value,
740                 {
741                     tag: 'span',
742                     cls: 'badge',
743                     html: this.badge
744                 }
745             ];
746             
747             cfg.html='';
748         }
749         
750         if (this.menu) {
751             cfg.cls += ' dropdown';
752             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
753         }
754         
755         if (cfg.tag !== 'a' && this.href !== '') {
756             throw "Tag must be a to set href.";
757         } else if (this.href.length > 0) {
758             cfg.href = this.href;
759         }
760         
761         if(this.removeClass){
762             cfg.cls = '';
763         }
764         
765         if(this.target){
766             cfg.target = this.target;
767         }
768         
769         return cfg;
770     },
771     initEvents: function() {
772        // Roo.log('init events?');
773 //        Roo.log(this.el.dom);
774         // add the menu...
775         
776         if (typeof (this.menu) != 'undefined') {
777             this.menu.parentType = this.xtype;
778             this.menu.triggerEl = this.el;
779             this.addxtype(Roo.apply({}, this.menu));
780         }
781
782
783        if (this.el.hasClass('roo-button')) {
784             this.el.on('click', this.onClick, this);
785        } else {
786             this.el.select('.roo-button').on('click', this.onClick, this);
787        }
788        
789        if(this.removeClass){
790            this.el.on('click', this.onClick, this);
791        }
792        
793        this.el.enableDisplayMode();
794         
795     },
796     onClick : function(e)
797     {
798         if (this.disabled) {
799             return;
800         }
801         
802         
803         Roo.log('button on click ');
804         if(this.preventDefault){
805             e.preventDefault();
806         }
807         if (this.pressed === true || this.pressed === false) {
808             this.pressed = !this.pressed;
809             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810             this.fireEvent('toggle', this, e, this.pressed);
811         }
812         
813         
814         this.fireEvent('click', this, e);
815     },
816     
817     /**
818      * Enables this button
819      */
820     enable : function()
821     {
822         this.disabled = false;
823         this.el.removeClass('disabled');
824     },
825     
826     /**
827      * Disable this button
828      */
829     disable : function()
830     {
831         this.disabled = true;
832         this.el.addClass('disabled');
833     },
834      /**
835      * sets the active state on/off, 
836      * @param {Boolean} state (optional) Force a particular state
837      */
838     setActive : function(v) {
839         
840         this.el[v ? 'addClass' : 'removeClass']('active');
841     },
842      /**
843      * toggles the current active state 
844      */
845     toggleActive : function()
846     {
847        var active = this.el.hasClass('active');
848        this.setActive(!active);
849        
850         
851     },
852     setText : function(str)
853     {
854         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
855     },
856     getText : function()
857     {
858         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859     },
860     hide: function() {
861        
862      
863         this.el.hide();   
864     },
865     show: function() {
866        
867         this.el.show();   
868     },
869     setWeight : function(str)
870     {
871           this.el.removeClass(this.weightClass);
872         this.el.addClass('btn-' + str);        
873     }
874     
875     
876 });
877
878  /*
879  * - LGPL
880  *
881  * column
882  * 
883  */
884
885 /**
886  * @class Roo.bootstrap.Column
887  * @extends Roo.bootstrap.Component
888  * Bootstrap Column class
889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
897  *
898  * 
899  * @cfg {Boolean} hidden (true|false) hide the element
900  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901  * @cfg {String} fa (ban|check|...) font awesome icon
902  * @cfg {Number} fasize (1|2|....) font awsome size
903
904  * @cfg {String} icon (info-sign|check|...) glyphicon name
905
906  * @cfg {String} html content of column.
907  * 
908  * @constructor
909  * Create a new Column
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.Column = function(config){
914     Roo.bootstrap.Column.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
918     
919     xs: false,
920     sm: false,
921     md: false,
922     lg: false,
923     xsoff: false,
924     smoff: false,
925     mdoff: false,
926     lgoff: false,
927     html: '',
928     offset: 0,
929     alert: false,
930     fa: false,
931     icon : false,
932     hidden : false,
933     fasize : 1,
934     
935     getAutoCreate : function(){
936         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
937         
938         cfg = {
939             tag: 'div',
940             cls: 'column'
941         };
942         
943         var settings=this;
944         ['xs','sm','md','lg'].map(function(size){
945             //Roo.log( size + ':' + settings[size]);
946             
947             if (settings[size+'off'] !== false) {
948                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
949             }
950             
951             if (settings[size] === false) {
952                 return;
953             }
954             
955             if (!settings[size]) { // 0 = hidden
956                 cfg.cls += ' hidden-' + size;
957                 return;
958             }
959             cfg.cls += ' col-' + size + '-' + settings[size];
960             
961         });
962         
963         if (this.hidden) {
964             cfg.cls += ' hidden';
965         }
966         
967         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968             cfg.cls +=' alert alert-' + this.alert;
969         }
970         
971         
972         if (this.html.length) {
973             cfg.html = this.html;
974         }
975         if (this.fa) {
976             var fasize = '';
977             if (this.fasize > 1) {
978                 fasize = ' fa-' + this.fasize + 'x';
979             }
980             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
981             
982             
983         }
984         if (this.icon) {
985             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
986         }
987         
988         return cfg;
989     }
990    
991 });
992
993  
994
995  /*
996  * - LGPL
997  *
998  * page container.
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Container
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Container class
1007  * @cfg {Boolean} jumbotron is it a jumbotron element
1008  * @cfg {String} html content of element
1009  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1011  * @cfg {String} header content of header (for panel)
1012  * @cfg {String} footer content of footer (for panel)
1013  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014  * @cfg {String} tag (header|aside|section) type of HTML tag.
1015  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016  * @cfg {String} fa font awesome icon
1017  * @cfg {String} icon (info-sign|check|...) glyphicon name
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {Boolean} expandable (true|false) default false
1020  * @cfg {Boolean} expanded (true|false) default true
1021  * @cfg {String} rheader contet on the right of header
1022  * @cfg {Boolean} clickable (true|false) default false
1023
1024  *     
1025  * @constructor
1026  * Create a new Container
1027  * @param {Object} config The config object
1028  */
1029
1030 Roo.bootstrap.Container = function(config){
1031     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035          /**
1036          * @event expand
1037          * After the panel has been expand
1038          * 
1039          * @param {Roo.bootstrap.Container} this
1040          */
1041         "expand" : true,
1042         /**
1043          * @event collapse
1044          * After the panel has been collapsed
1045          * 
1046          * @param {Roo.bootstrap.Container} this
1047          */
1048         "collapse" : true,
1049         /**
1050          * @event click
1051          * When a element is chick
1052          * @param {Roo.bootstrap.Container} this
1053          * @param {Roo.EventObject} e
1054          */
1055         "click" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1060     
1061     jumbotron : false,
1062     well: '',
1063     panel : '',
1064     header: '',
1065     footer : '',
1066     sticky: '',
1067     tag : false,
1068     alert : false,
1069     fa: false,
1070     icon : false,
1071     expandable : false,
1072     rheader : '',
1073     expanded : true,
1074     clickable: false,
1075   
1076      
1077     getChildContainer : function() {
1078         
1079         if(!this.el){
1080             return false;
1081         }
1082         
1083         if (this.panel.length) {
1084             return this.el.select('.panel-body',true).first();
1085         }
1086         
1087         return this.el;
1088     },
1089     
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : this.tag || 'div',
1095             html : '',
1096             cls : ''
1097         };
1098         if (this.jumbotron) {
1099             cfg.cls = 'jumbotron';
1100         }
1101         
1102         
1103         
1104         // - this is applied by the parent..
1105         //if (this.cls) {
1106         //    cfg.cls = this.cls + '';
1107         //}
1108         
1109         if (this.sticky.length) {
1110             
1111             var bd = Roo.get(document.body);
1112             if (!bd.hasClass('bootstrap-sticky')) {
1113                 bd.addClass('bootstrap-sticky');
1114                 Roo.select('html',true).setStyle('height', '100%');
1115             }
1116              
1117             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1118         }
1119         
1120         
1121         if (this.well.length) {
1122             switch (this.well) {
1123                 case 'lg':
1124                 case 'sm':
1125                     cfg.cls +=' well well-' +this.well;
1126                     break;
1127                 default:
1128                     cfg.cls +=' well';
1129                     break;
1130             }
1131         }
1132         
1133         if (this.hidden) {
1134             cfg.cls += ' hidden';
1135         }
1136         
1137         
1138         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139             cfg.cls +=' alert alert-' + this.alert;
1140         }
1141         
1142         var body = cfg;
1143         
1144         if (this.panel.length) {
1145             cfg.cls += ' panel panel-' + this.panel;
1146             cfg.cn = [];
1147             if (this.header.length) {
1148                 
1149                 var h = [];
1150                 
1151                 if(this.expandable){
1152                     
1153                     cfg.cls = cfg.cls + ' expandable';
1154                     
1155                     h.push({
1156                         tag: 'i',
1157                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1158                     });
1159                     
1160                 }
1161                 
1162                 h.push(
1163                     {
1164                         tag: 'span',
1165                         cls : 'panel-title',
1166                         html : (this.expandable ? '&nbsp;' : '') + this.header
1167                     },
1168                     {
1169                         tag: 'span',
1170                         cls: 'panel-header-right',
1171                         html: this.rheader
1172                     }
1173                 );
1174                 
1175                 cfg.cn.push({
1176                     cls : 'panel-heading',
1177                     style : this.expandable ? 'cursor: pointer' : '',
1178                     cn : h
1179                 });
1180                 
1181             }
1182             
1183             body = false;
1184             cfg.cn.push({
1185                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1186                 html : this.html
1187             });
1188             
1189             
1190             if (this.footer.length) {
1191                 cfg.cn.push({
1192                     cls : 'panel-footer',
1193                     html : this.footer
1194                     
1195                 });
1196             }
1197             
1198         }
1199         
1200         if (body) {
1201             body.html = this.html || cfg.html;
1202             // prefix with the icons..
1203             if (this.fa) {
1204                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1205             }
1206             if (this.icon) {
1207                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1208             }
1209             
1210             
1211         }
1212         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213             cfg.cls =  'container';
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() 
1220     {
1221         if(this.expandable){
1222             var headerEl = this.headerEl();
1223         
1224             if(headerEl){
1225                 headerEl.on('click', this.onToggleClick, this);
1226             }
1227         }
1228         
1229         if(this.clickable){
1230             this.el.on('click', this.onClick, this);
1231         }
1232         
1233     },
1234     
1235     onToggleClick : function()
1236     {
1237         var headerEl = this.headerEl();
1238         
1239         if(!headerEl){
1240             return;
1241         }
1242         
1243         if(this.expanded){
1244             this.collapse();
1245             return;
1246         }
1247         
1248         this.expand();
1249     },
1250     
1251     expand : function()
1252     {
1253         if(this.fireEvent('expand', this)) {
1254             
1255             this.expanded = true;
1256             
1257             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258             
1259             this.el.select('.panel-body',true).first().removeClass('hide');
1260             
1261             var toggleEl = this.toggleEl();
1262
1263             if(!toggleEl){
1264                 return;
1265             }
1266
1267             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1268         }
1269         
1270     },
1271     
1272     collapse : function()
1273     {
1274         if(this.fireEvent('collapse', this)) {
1275             
1276             this.expanded = false;
1277             
1278             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279             this.el.select('.panel-body',true).first().addClass('hide');
1280         
1281             var toggleEl = this.toggleEl();
1282
1283             if(!toggleEl){
1284                 return;
1285             }
1286
1287             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1288         }
1289     },
1290     
1291     toggleEl : function()
1292     {
1293         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1294             return;
1295         }
1296         
1297         return this.el.select('.panel-heading .fa',true).first();
1298     },
1299     
1300     headerEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading',true).first()
1307     },
1308     
1309     bodyEl : function()
1310     {
1311         if(!this.el || !this.panel.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-body',true).first()
1316     },
1317     
1318     titleEl : function()
1319     {
1320         if(!this.el || !this.panel.length || !this.header.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-title',true).first();
1325     },
1326     
1327     setTitle : function(v)
1328     {
1329         var titleEl = this.titleEl();
1330         
1331         if(!titleEl){
1332             return;
1333         }
1334         
1335         titleEl.dom.innerHTML = v;
1336     },
1337     
1338     getTitle : function()
1339     {
1340         
1341         var titleEl = this.titleEl();
1342         
1343         if(!titleEl){
1344             return '';
1345         }
1346         
1347         return titleEl.dom.innerHTML;
1348     },
1349     
1350     setRightTitle : function(v)
1351     {
1352         var t = this.el.select('.panel-header-right',true).first();
1353         
1354         if(!t){
1355             return;
1356         }
1357         
1358         t.dom.innerHTML = v;
1359     },
1360     
1361     onClick : function(e)
1362     {
1363         e.preventDefault();
1364         
1365         this.fireEvent('click', this, e);
1366     }
1367    
1368 });
1369
1370  /*
1371  * - LGPL
1372  *
1373  * image
1374  * 
1375  */
1376
1377
1378 /**
1379  * @class Roo.bootstrap.Img
1380  * @extends Roo.bootstrap.Component
1381  * Bootstrap Img class
1382  * @cfg {Boolean} imgResponsive false | true
1383  * @cfg {String} border rounded | circle | thumbnail
1384  * @cfg {String} src image source
1385  * @cfg {String} alt image alternative text
1386  * @cfg {String} href a tag href
1387  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388  * @cfg {String} xsUrl xs image source
1389  * @cfg {String} smUrl sm image source
1390  * @cfg {String} mdUrl md image source
1391  * @cfg {String} lgUrl lg image source
1392  * 
1393  * @constructor
1394  * Create a new Input
1395  * @param {Object} config The config object
1396  */
1397
1398 Roo.bootstrap.Img = function(config){
1399     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1400     
1401     this.addEvents({
1402         // img events
1403         /**
1404          * @event click
1405          * The img click event for the img.
1406          * @param {Roo.EventObject} e
1407          */
1408         "click" : true
1409     });
1410 };
1411
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1413     
1414     imgResponsive: true,
1415     border: '',
1416     src: 'about:blank',
1417     href: false,
1418     target: false,
1419     xsUrl: '',
1420     smUrl: '',
1421     mdUrl: '',
1422     lgUrl: '',
1423
1424     getAutoCreate : function()
1425     {   
1426         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427             return this.createSingleImg();
1428         }
1429         
1430         var cfg = {
1431             tag: 'div',
1432             cls: 'roo-image-responsive-group',
1433             cn: []
1434         };
1435         var _this = this;
1436         
1437         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438             
1439             if(!_this[size + 'Url']){
1440                 return;
1441             }
1442             
1443             var img = {
1444                 tag: 'img',
1445                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446                 html: _this.html || cfg.html,
1447                 src: _this[size + 'Url']
1448             };
1449             
1450             img.cls += ' roo-image-responsive-' + size;
1451             
1452             var s = ['xs', 'sm', 'md', 'lg'];
1453             
1454             s.splice(s.indexOf(size), 1);
1455             
1456             Roo.each(s, function(ss){
1457                 img.cls += ' hidden-' + ss;
1458             });
1459             
1460             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461                 cfg.cls += ' img-' + _this.border;
1462             }
1463             
1464             if(_this.alt){
1465                 cfg.alt = _this.alt;
1466             }
1467             
1468             if(_this.href){
1469                 var a = {
1470                     tag: 'a',
1471                     href: _this.href,
1472                     cn: [
1473                         img
1474                     ]
1475                 };
1476
1477                 if(this.target){
1478                     a.target = _this.target;
1479                 }
1480             }
1481             
1482             cfg.cn.push((_this.href) ? a : img);
1483             
1484         });
1485         
1486         return cfg;
1487     },
1488     
1489     createSingleImg : function()
1490     {
1491         var cfg = {
1492             tag: 'img',
1493             cls: (this.imgResponsive) ? 'img-responsive' : '',
1494             html : null,
1495             src : 'about:blank'  // just incase src get's set to undefined?!?
1496         };
1497         
1498         cfg.html = this.html || cfg.html;
1499         
1500         cfg.src = this.src || cfg.src;
1501         
1502         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503             cfg.cls += ' img-' + this.border;
1504         }
1505         
1506         if(this.alt){
1507             cfg.alt = this.alt;
1508         }
1509         
1510         if(this.href){
1511             var a = {
1512                 tag: 'a',
1513                 href: this.href,
1514                 cn: [
1515                     cfg
1516                 ]
1517             };
1518             
1519             if(this.target){
1520                 a.target = this.target;
1521             }
1522             
1523         }
1524         
1525         return (this.href) ? a : cfg;
1526     },
1527     
1528     initEvents: function() 
1529     {
1530         if(!this.href){
1531             this.el.on('click', this.onClick, this);
1532         }
1533         
1534     },
1535     
1536     onClick : function(e)
1537     {
1538         Roo.log('img onclick');
1539         this.fireEvent('click', this, e);
1540     },
1541     /**
1542      * Sets the url of the image - used to update it
1543      * @param {String} url the url of the image
1544      */
1545     
1546     setSrc : function(url)
1547     {
1548         this.src =  url;
1549         
1550         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551             this.el.dom.src =  url;
1552             return;
1553         }
1554         
1555         this.el.select('img', true).first().dom.src =  url;
1556     }
1557     
1558     
1559    
1560 });
1561
1562  /*
1563  * - LGPL
1564  *
1565  * image
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Link
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Link Class
1574  * @cfg {String} alt image alternative text
1575  * @cfg {String} href a tag href
1576  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577  * @cfg {String} html the content of the link.
1578  * @cfg {String} anchor name for the anchor link
1579  * @cfg {String} fa - favicon
1580
1581  * @cfg {Boolean} preventDefault (true | false) default false
1582
1583  * 
1584  * @constructor
1585  * Create a new Input
1586  * @param {Object} config The config object
1587  */
1588
1589 Roo.bootstrap.Link = function(config){
1590     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1591     
1592     this.addEvents({
1593         // img events
1594         /**
1595          * @event click
1596          * The img click event for the img.
1597          * @param {Roo.EventObject} e
1598          */
1599         "click" : true
1600     });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1604     
1605     href: false,
1606     target: false,
1607     preventDefault: false,
1608     anchor : false,
1609     alt : false,
1610     fa: false,
1611
1612
1613     getAutoCreate : function()
1614     {
1615         var html = this.html || '';
1616         
1617         if (this.fa !== false) {
1618             html = '<i class="fa fa-' + this.fa + '"></i>';
1619         }
1620         var cfg = {
1621             tag: 'a'
1622         };
1623         // anchor's do not require html/href...
1624         if (this.anchor === false) {
1625             cfg.html = html;
1626             cfg.href = this.href || '#';
1627         } else {
1628             cfg.name = this.anchor;
1629             if (this.html !== false || this.fa !== false) {
1630                 cfg.html = html;
1631             }
1632             if (this.href !== false) {
1633                 cfg.href = this.href;
1634             }
1635         }
1636         
1637         if(this.alt !== false){
1638             cfg.alt = this.alt;
1639         }
1640         
1641         
1642         if(this.target !== false) {
1643             cfg.target = this.target;
1644         }
1645         
1646         return cfg;
1647     },
1648     
1649     initEvents: function() {
1650         
1651         if(!this.href || this.preventDefault){
1652             this.el.on('click', this.onClick, this);
1653         }
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         if(this.preventDefault){
1659             e.preventDefault();
1660         }
1661         //Roo.log('img onclick');
1662         this.fireEvent('click', this, e);
1663     }
1664    
1665 });
1666
1667  /*
1668  * - LGPL
1669  *
1670  * header
1671  * 
1672  */
1673
1674 /**
1675  * @class Roo.bootstrap.Header
1676  * @extends Roo.bootstrap.Component
1677  * Bootstrap Header class
1678  * @cfg {String} html content of header
1679  * @cfg {Number} level (1|2|3|4|5|6) default 1
1680  * 
1681  * @constructor
1682  * Create a new Header
1683  * @param {Object} config The config object
1684  */
1685
1686
1687 Roo.bootstrap.Header  = function(config){
1688     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1689 };
1690
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1692     
1693     //href : false,
1694     html : false,
1695     level : 1,
1696     
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         
1702         
1703         var cfg = {
1704             tag: 'h' + (1 *this.level),
1705             html: this.html || ''
1706         } ;
1707         
1708         return cfg;
1709     }
1710    
1711 });
1712
1713  
1714
1715  /*
1716  * Based on:
1717  * Ext JS Library 1.1.1
1718  * Copyright(c) 2006-2007, Ext JS, LLC.
1719  *
1720  * Originally Released Under LGPL - original licence link has changed is not relivant.
1721  *
1722  * Fork - LGPL
1723  * <script type="text/javascript">
1724  */
1725  
1726 /**
1727  * @class Roo.bootstrap.MenuMgr
1728  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1729  * @singleton
1730  */
1731 Roo.bootstrap.MenuMgr = function(){
1732    var menus, active, groups = {}, attached = false, lastShow = new Date();
1733
1734    // private - called when first menu is created
1735    function init(){
1736        menus = {};
1737        active = new Roo.util.MixedCollection();
1738        Roo.get(document).addKeyListener(27, function(){
1739            if(active.length > 0){
1740                hideAll();
1741            }
1742        });
1743    }
1744
1745    // private
1746    function hideAll(){
1747        if(active && active.length > 0){
1748            var c = active.clone();
1749            c.each(function(m){
1750                m.hide();
1751            });
1752        }
1753    }
1754
1755    // private
1756    function onHide(m){
1757        active.remove(m);
1758        if(active.length < 1){
1759            Roo.get(document).un("mouseup", onMouseDown);
1760             
1761            attached = false;
1762        }
1763    }
1764
1765    // private
1766    function onShow(m){
1767        var last = active.last();
1768        lastShow = new Date();
1769        active.add(m);
1770        if(!attached){
1771           Roo.get(document).on("mouseup", onMouseDown);
1772            
1773            attached = true;
1774        }
1775        if(m.parentMenu){
1776           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777           m.parentMenu.activeChild = m;
1778        }else if(last && last.isVisible()){
1779           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1780        }
1781    }
1782
1783    // private
1784    function onBeforeHide(m){
1785        if(m.activeChild){
1786            m.activeChild.hide();
1787        }
1788        if(m.autoHideTimer){
1789            clearTimeout(m.autoHideTimer);
1790            delete m.autoHideTimer;
1791        }
1792    }
1793
1794    // private
1795    function onBeforeShow(m){
1796        var pm = m.parentMenu;
1797        if(!pm && !m.allowOtherMenus){
1798            hideAll();
1799        }else if(pm && pm.activeChild && active != m){
1800            pm.activeChild.hide();
1801        }
1802    }
1803
1804    // private this should really trigger on mouseup..
1805    function onMouseDown(e){
1806         Roo.log("on Mouse Up");
1807         
1808         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809             Roo.log("MenuManager hideAll");
1810             hideAll();
1811             e.stopEvent();
1812         }
1813         
1814         
1815    }
1816
1817    // private
1818    function onBeforeCheck(mi, state){
1819        if(state){
1820            var g = groups[mi.group];
1821            for(var i = 0, l = g.length; i < l; i++){
1822                if(g[i] != mi){
1823                    g[i].setChecked(false);
1824                }
1825            }
1826        }
1827    }
1828
1829    return {
1830
1831        /**
1832         * Hides all menus that are currently visible
1833         */
1834        hideAll : function(){
1835             hideAll();  
1836        },
1837
1838        // private
1839        register : function(menu){
1840            if(!menus){
1841                init();
1842            }
1843            menus[menu.id] = menu;
1844            menu.on("beforehide", onBeforeHide);
1845            menu.on("hide", onHide);
1846            menu.on("beforeshow", onBeforeShow);
1847            menu.on("show", onShow);
1848            var g = menu.group;
1849            if(g && menu.events["checkchange"]){
1850                if(!groups[g]){
1851                    groups[g] = [];
1852                }
1853                groups[g].push(menu);
1854                menu.on("checkchange", onCheck);
1855            }
1856        },
1857
1858         /**
1859          * Returns a {@link Roo.menu.Menu} object
1860          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861          * be used to generate and return a new Menu instance.
1862          */
1863        get : function(menu){
1864            if(typeof menu == "string"){ // menu id
1865                return menus[menu];
1866            }else if(menu.events){  // menu instance
1867                return menu;
1868            }
1869            /*else if(typeof menu.length == 'number'){ // array of menu items?
1870                return new Roo.bootstrap.Menu({items:menu});
1871            }else{ // otherwise, must be a config
1872                return new Roo.bootstrap.Menu(menu);
1873            }
1874            */
1875            return false;
1876        },
1877
1878        // private
1879        unregister : function(menu){
1880            delete menus[menu.id];
1881            menu.un("beforehide", onBeforeHide);
1882            menu.un("hide", onHide);
1883            menu.un("beforeshow", onBeforeShow);
1884            menu.un("show", onShow);
1885            var g = menu.group;
1886            if(g && menu.events["checkchange"]){
1887                groups[g].remove(menu);
1888                menu.un("checkchange", onCheck);
1889            }
1890        },
1891
1892        // private
1893        registerCheckable : function(menuItem){
1894            var g = menuItem.group;
1895            if(g){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menuItem);
1900                menuItem.on("beforecheckchange", onBeforeCheck);
1901            }
1902        },
1903
1904        // private
1905        unregisterCheckable : function(menuItem){
1906            var g = menuItem.group;
1907            if(g){
1908                groups[g].remove(menuItem);
1909                menuItem.un("beforecheckchange", onBeforeCheck);
1910            }
1911        }
1912    };
1913 }();/*
1914  * - LGPL
1915  *
1916  * menu
1917  * 
1918  */
1919
1920 /**
1921  * @class Roo.bootstrap.Menu
1922  * @extends Roo.bootstrap.Component
1923  * Bootstrap Menu class - container for MenuItems
1924  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1926  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1927  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1928  * 
1929  * @constructor
1930  * Create a new Menu
1931  * @param {Object} config The config object
1932  */
1933
1934
1935 Roo.bootstrap.Menu = function(config){
1936     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937     if (this.registerMenu && this.type != 'treeview')  {
1938         Roo.bootstrap.MenuMgr.register(this);
1939     }
1940     this.addEvents({
1941         /**
1942          * @event beforeshow
1943          * Fires before this menu is displayed
1944          * @param {Roo.menu.Menu} this
1945          */
1946         beforeshow : true,
1947         /**
1948          * @event beforehide
1949          * Fires before this menu is hidden
1950          * @param {Roo.menu.Menu} this
1951          */
1952         beforehide : true,
1953         /**
1954          * @event show
1955          * Fires after this menu is displayed
1956          * @param {Roo.menu.Menu} this
1957          */
1958         show : true,
1959         /**
1960          * @event hide
1961          * Fires after this menu is hidden
1962          * @param {Roo.menu.Menu} this
1963          */
1964         hide : true,
1965         /**
1966          * @event click
1967          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968          * @param {Roo.menu.Menu} this
1969          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970          * @param {Roo.EventObject} e
1971          */
1972         click : true,
1973         /**
1974          * @event mouseover
1975          * Fires when the mouse is hovering over this menu
1976          * @param {Roo.menu.Menu} this
1977          * @param {Roo.EventObject} e
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          */
1980         mouseover : true,
1981         /**
1982          * @event mouseout
1983          * Fires when the mouse exits this menu
1984          * @param {Roo.menu.Menu} this
1985          * @param {Roo.EventObject} e
1986          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1987          */
1988         mouseout : true,
1989         /**
1990          * @event itemclick
1991          * Fires when a menu item contained in this menu is clicked
1992          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993          * @param {Roo.EventObject} e
1994          */
1995         itemclick: true
1996     });
1997     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1998 };
1999
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2001     
2002    /// html : false,
2003     //align : '',
2004     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2005     type: false,
2006     /**
2007      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008      */
2009     registerMenu : true,
2010     
2011     menuItems :false, // stores the menu items..
2012     
2013     hidden:true,
2014         
2015     parentMenu : false,
2016     
2017     stopEvent : true,
2018     
2019     isLink : false,
2020     
2021     getChildContainer : function() {
2022         return this.el;  
2023     },
2024     
2025     getAutoCreate : function(){
2026          
2027         //if (['right'].indexOf(this.align)!==-1) {
2028         //    cfg.cn[1].cls += ' pull-right'
2029         //}
2030         
2031         
2032         var cfg = {
2033             tag : 'ul',
2034             cls : 'dropdown-menu' ,
2035             style : 'z-index:1000'
2036             
2037         };
2038         
2039         if (this.type === 'submenu') {
2040             cfg.cls = 'submenu active';
2041         }
2042         if (this.type === 'treeview') {
2043             cfg.cls = 'treeview-menu';
2044         }
2045         
2046         return cfg;
2047     },
2048     initEvents : function() {
2049         
2050        // Roo.log("ADD event");
2051        // Roo.log(this.triggerEl.dom);
2052         
2053         this.triggerEl.on('click', this.onTriggerClick, this);
2054         
2055         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056         
2057         this.triggerEl.addClass('dropdown-toggle');
2058         
2059         if (Roo.isTouch) {
2060             this.el.on('touchstart'  , this.onTouch, this);
2061         }
2062         this.el.on('click' , this.onClick, this);
2063
2064         this.el.on("mouseover", this.onMouseOver, this);
2065         this.el.on("mouseout", this.onMouseOut, this);
2066         
2067     },
2068     
2069     findTargetItem : function(e)
2070     {
2071         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2072         if(!t){
2073             return false;
2074         }
2075         //Roo.log(t);         Roo.log(t.id);
2076         if(t && t.id){
2077             //Roo.log(this.menuitems);
2078             return this.menuitems.get(t.id);
2079             
2080             //return this.items.get(t.menuItemId);
2081         }
2082         
2083         return false;
2084     },
2085     
2086     onTouch : function(e) 
2087     {
2088         Roo.log("menu.onTouch");
2089         //e.stopEvent(); this make the user popdown broken
2090         this.onClick(e);
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log("menu.onClick");
2096         
2097         var t = this.findTargetItem(e);
2098         if(!t || t.isContainer){
2099             return;
2100         }
2101         Roo.log(e);
2102         /*
2103         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2104             if(t == this.activeItem && t.shouldDeactivate(e)){
2105                 this.activeItem.deactivate();
2106                 delete this.activeItem;
2107                 return;
2108             }
2109             if(t.canActivate){
2110                 this.setActiveItem(t, true);
2111             }
2112             return;
2113             
2114             
2115         }
2116         */
2117        
2118         Roo.log('pass click event');
2119         
2120         t.onClick(e);
2121         
2122         this.fireEvent("click", this, t, e);
2123         
2124         var _this = this;
2125         
2126         (function() { _this.hide(); }).defer(100);
2127     },
2128     
2129     onMouseOver : function(e){
2130         var t  = this.findTargetItem(e);
2131         //Roo.log(t);
2132         //if(t){
2133         //    if(t.canActivate && !t.disabled){
2134         //        this.setActiveItem(t, true);
2135         //    }
2136         //}
2137         
2138         this.fireEvent("mouseover", this, e, t);
2139     },
2140     isVisible : function(){
2141         return !this.hidden;
2142     },
2143      onMouseOut : function(e){
2144         var t  = this.findTargetItem(e);
2145         
2146         //if(t ){
2147         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2148         //        this.activeItem.deactivate();
2149         //        delete this.activeItem;
2150         //    }
2151         //}
2152         this.fireEvent("mouseout", this, e, t);
2153     },
2154     
2155     
2156     /**
2157      * Displays this menu relative to another element
2158      * @param {String/HTMLElement/Roo.Element} element The element to align to
2159      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2160      * the element (defaults to this.defaultAlign)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     show : function(el, pos, parentMenu){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         this.fireEvent("beforeshow", this);
2169         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2170     },
2171      /**
2172      * Displays this menu at a specific xy position
2173      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2174      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2175      */
2176     showAt : function(xy, parentMenu, /* private: */_e){
2177         this.parentMenu = parentMenu;
2178         if(!this.el){
2179             this.render();
2180         }
2181         if(_e !== false){
2182             this.fireEvent("beforeshow", this);
2183             //xy = this.el.adjustForConstraints(xy);
2184         }
2185         
2186         //this.el.show();
2187         this.hideMenuItems();
2188         this.hidden = false;
2189         this.triggerEl.addClass('open');
2190         
2191         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2192             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2193         }
2194         
2195         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2196             this.el.setXY(xy);
2197         }
2198         
2199         this.focus();
2200         this.fireEvent("show", this);
2201     },
2202     
2203     focus : function(){
2204         return;
2205         if(!this.hidden){
2206             this.doFocus.defer(50, this);
2207         }
2208     },
2209
2210     doFocus : function(){
2211         if(!this.hidden){
2212             this.focusEl.focus();
2213         }
2214     },
2215
2216     /**
2217      * Hides this menu and optionally all parent menus
2218      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2219      */
2220     hide : function(deep)
2221     {
2222         
2223         this.hideMenuItems();
2224         if(this.el && this.isVisible()){
2225             this.fireEvent("beforehide", this);
2226             if(this.activeItem){
2227                 this.activeItem.deactivate();
2228                 this.activeItem = null;
2229             }
2230             this.triggerEl.removeClass('open');;
2231             this.hidden = true;
2232             this.fireEvent("hide", this);
2233         }
2234         if(deep === true && this.parentMenu){
2235             this.parentMenu.hide(true);
2236         }
2237     },
2238     
2239     onTriggerClick : function(e)
2240     {
2241         Roo.log('trigger click');
2242         
2243         var target = e.getTarget();
2244         
2245         Roo.log(target.nodeName.toLowerCase());
2246         
2247         if(target.nodeName.toLowerCase() === 'i'){
2248             e.preventDefault();
2249         }
2250         
2251     },
2252     
2253     onTriggerPress  : function(e)
2254     {
2255         Roo.log('trigger press');
2256         //Roo.log(e.getTarget());
2257        // Roo.log(this.triggerEl.dom);
2258        
2259         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2260         var pel = Roo.get(e.getTarget());
2261         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2262             Roo.log('is treeview or dropdown?');
2263             return;
2264         }
2265         
2266         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2267             return;
2268         }
2269         
2270         if (this.isVisible()) {
2271             Roo.log('hide');
2272             this.hide();
2273         } else {
2274             Roo.log('show');
2275             this.show(this.triggerEl, false, false);
2276         }
2277         
2278         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2279             e.stopEvent();
2280         }
2281         
2282     },
2283        
2284     
2285     hideMenuItems : function()
2286     {
2287         Roo.log("hide Menu Items");
2288         if (!this.el) { 
2289             return;
2290         }
2291         //$(backdrop).remove()
2292         this.el.select('.open',true).each(function(aa) {
2293             
2294             aa.removeClass('open');
2295           //var parent = getParent($(this))
2296           //var relatedTarget = { relatedTarget: this }
2297           
2298            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2299           //if (e.isDefaultPrevented()) return
2300            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2301         });
2302     },
2303     addxtypeChild : function (tree, cntr) {
2304         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2305           
2306         this.menuitems.add(comp);
2307         return comp;
2308
2309     },
2310     getEl : function()
2311     {
2312         Roo.log(this.el);
2313         return this.el;
2314     }
2315 });
2316
2317  
2318  /*
2319  * - LGPL
2320  *
2321  * menu item
2322  * 
2323  */
2324
2325
2326 /**
2327  * @class Roo.bootstrap.MenuItem
2328  * @extends Roo.bootstrap.Component
2329  * Bootstrap MenuItem class
2330  * @cfg {String} html the menu label
2331  * @cfg {String} href the link
2332  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2333  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2334  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2335  * @cfg {String} fa favicon to show on left of menu item.
2336  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2337  * 
2338  * 
2339  * @constructor
2340  * Create a new MenuItem
2341  * @param {Object} config The config object
2342  */
2343
2344
2345 Roo.bootstrap.MenuItem = function(config){
2346     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2347     this.addEvents({
2348         // raw events
2349         /**
2350          * @event click
2351          * The raw click event for the entire grid.
2352          * @param {Roo.bootstrap.MenuItem} this
2353          * @param {Roo.EventObject} e
2354          */
2355         "click" : true
2356     });
2357 };
2358
2359 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2360     
2361     href : false,
2362     html : false,
2363     preventDefault: false,
2364     isContainer : false,
2365     active : false,
2366     fa: false,
2367     
2368     getAutoCreate : function(){
2369         
2370         if(this.isContainer){
2371             return {
2372                 tag: 'li',
2373                 cls: 'dropdown-menu-item'
2374             };
2375         }
2376         var ctag = {
2377             tag: 'span',
2378             html: 'Link'
2379         };
2380         
2381         var anc = {
2382             tag : 'a',
2383             href : '#',
2384             cn : [  ]
2385         };
2386         
2387         if (this.fa !== false) {
2388             anc.cn.push({
2389                 tag : 'i',
2390                 cls : 'fa fa-' + this.fa
2391             });
2392         }
2393         
2394         anc.cn.push(ctag);
2395         
2396         
2397         var cfg= {
2398             tag: 'li',
2399             cls: 'dropdown-menu-item',
2400             cn: [ anc ]
2401         };
2402         if (this.parent().type == 'treeview') {
2403             cfg.cls = 'treeview-menu';
2404         }
2405         if (this.active) {
2406             cfg.cls += ' active';
2407         }
2408         
2409         
2410         
2411         anc.href = this.href || cfg.cn[0].href ;
2412         ctag.html = this.html || cfg.cn[0].html ;
2413         return cfg;
2414     },
2415     
2416     initEvents: function()
2417     {
2418         if (this.parent().type == 'treeview') {
2419             this.el.select('a').on('click', this.onClick, this);
2420         }
2421         if (this.menu) {
2422             this.menu.parentType = this.xtype;
2423             this.menu.triggerEl = this.el;
2424             this.menu = this.addxtype(Roo.apply({}, this.menu));
2425         }
2426         
2427     },
2428     onClick : function(e)
2429     {
2430         Roo.log('item on click ');
2431         
2432         if(this.preventDefault){
2433             e.preventDefault();
2434         }
2435         //this.parent().hideMenuItems();
2436         
2437         this.fireEvent('click', this, e);
2438     },
2439     getEl : function()
2440     {
2441         return this.el;
2442     } 
2443 });
2444
2445  
2446
2447  /*
2448  * - LGPL
2449  *
2450  * menu separator
2451  * 
2452  */
2453
2454
2455 /**
2456  * @class Roo.bootstrap.MenuSeparator
2457  * @extends Roo.bootstrap.Component
2458  * Bootstrap MenuSeparator class
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuSeparator = function(config){
2467     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2468 };
2469
2470 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2471     
2472     getAutoCreate : function(){
2473         var cfg = {
2474             cls: 'divider',
2475             tag : 'li'
2476         };
2477         
2478         return cfg;
2479     }
2480    
2481 });
2482
2483  
2484
2485  
2486 /*
2487 * Licence: LGPL
2488 */
2489
2490 /**
2491  * @class Roo.bootstrap.Modal
2492  * @extends Roo.bootstrap.Component
2493  * Bootstrap Modal class
2494  * @cfg {String} title Title of dialog
2495  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2496  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2497  * @cfg {Boolean} specificTitle default false
2498  * @cfg {Array} buttons Array of buttons or standard button set..
2499  * @cfg {String} buttonPosition (left|right|center) default right
2500  * @cfg {Boolean} animate default true
2501  * @cfg {Boolean} allow_close default true
2502  * @cfg {Boolean} fitwindow default false
2503  * @cfg {String} size (sm|lg) default empty
2504  *
2505  *
2506  * @constructor
2507  * Create a new Modal Dialog
2508  * @param {Object} config The config object
2509  */
2510
2511 Roo.bootstrap.Modal = function(config){
2512     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2513     this.addEvents({
2514         // raw events
2515         /**
2516          * @event btnclick
2517          * The raw btnclick event for the button
2518          * @param {Roo.EventObject} e
2519          */
2520         "btnclick" : true,
2521         /**
2522          * @event resize
2523          * Fire when dialog resize
2524          * @param {Roo.bootstrap.Modal} this
2525          * @param {Roo.EventObject} e
2526          */
2527         "resize" : true
2528     });
2529     this.buttons = this.buttons || [];
2530
2531     if (this.tmpl) {
2532         this.tmpl = Roo.factory(this.tmpl);
2533     }
2534
2535 };
2536
2537 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2538
2539     title : 'test dialog',
2540
2541     buttons : false,
2542
2543     // set on load...
2544
2545     html: false,
2546
2547     tmp: false,
2548
2549     specificTitle: false,
2550
2551     buttonPosition: 'right',
2552
2553     allow_close : true,
2554
2555     animate : true,
2556
2557     fitwindow: false,
2558
2559
2560      // private
2561     dialogEl: false,
2562     bodyEl:  false,
2563     footerEl:  false,
2564     titleEl:  false,
2565     closeEl:  false,
2566
2567     size: '',
2568
2569
2570     onRender : function(ct, position)
2571     {
2572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2573
2574         if(!this.el){
2575             var cfg = Roo.apply({},  this.getAutoCreate());
2576             cfg.id = Roo.id();
2577             //if(!cfg.name){
2578             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2579             //}
2580             //if (!cfg.name.length) {
2581             //    delete cfg.name;
2582            // }
2583             if (this.cls) {
2584                 cfg.cls += ' ' + this.cls;
2585             }
2586             if (this.style) {
2587                 cfg.style = this.style;
2588             }
2589             this.el = Roo.get(document.body).createChild(cfg, position);
2590         }
2591         //var type = this.el.dom.type;
2592
2593
2594         if(this.tabIndex !== undefined){
2595             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2596         }
2597
2598         this.dialogEl = this.el.select('.modal-dialog',true).first();
2599         this.bodyEl = this.el.select('.modal-body',true).first();
2600         this.closeEl = this.el.select('.modal-header .close', true).first();
2601         this.headerEl = this.el.select('.modal-header',true).first();
2602         this.titleEl = this.el.select('.modal-title',true).first();
2603         this.footerEl = this.el.select('.modal-footer',true).first();
2604
2605         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2606         this.maskEl.enableDisplayMode("block");
2607         this.maskEl.hide();
2608         //this.el.addClass("x-dlg-modal");
2609
2610         if (this.buttons.length) {
2611             Roo.each(this.buttons, function(bb) {
2612                 var b = Roo.apply({}, bb);
2613                 b.xns = b.xns || Roo.bootstrap;
2614                 b.xtype = b.xtype || 'Button';
2615                 if (typeof(b.listeners) == 'undefined') {
2616                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2617                 }
2618
2619                 var btn = Roo.factory(b);
2620
2621                 btn.render(this.el.select('.modal-footer div').first());
2622
2623             },this);
2624         }
2625         // render the children.
2626         var nitems = [];
2627
2628         if(typeof(this.items) != 'undefined'){
2629             var items = this.items;
2630             delete this.items;
2631
2632             for(var i =0;i < items.length;i++) {
2633                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2634             }
2635         }
2636
2637         this.items = nitems;
2638
2639         // where are these used - they used to be body/close/footer
2640
2641
2642         this.initEvents();
2643         //this.el.addClass([this.fieldClass, this.cls]);
2644
2645     },
2646
2647     getAutoCreate : function(){
2648
2649
2650         var bdy = {
2651                 cls : 'modal-body',
2652                 html : this.html || ''
2653         };
2654
2655         var title = {
2656             tag: 'h4',
2657             cls : 'modal-title',
2658             html : this.title
2659         };
2660
2661         if(this.specificTitle){
2662             title = this.title;
2663
2664         };
2665
2666         var header = [];
2667         if (this.allow_close) {
2668             header.push({
2669                 tag: 'button',
2670                 cls : 'close',
2671                 html : '&times'
2672             });
2673         }
2674
2675         header.push(title);
2676
2677         var size = '';
2678
2679         if(this.size.length){
2680             size = 'modal-' + this.size;
2681         }
2682
2683         var modal = {
2684             cls: "modal",
2685             style : 'display: none',
2686             cn : [
2687                 {
2688                     cls: "modal-dialog " + size,
2689                     cn : [
2690                         {
2691                             cls : "modal-content",
2692                             cn : [
2693                                 {
2694                                     cls : 'modal-header',
2695                                     cn : header
2696                                 },
2697                                 bdy,
2698                                 {
2699                                     cls : 'modal-footer',
2700                                     cn : [
2701                                         {
2702                                             tag: 'div',
2703                                             cls: 'btn-' + this.buttonPosition
2704                                         }
2705                                     ]
2706
2707                                 }
2708
2709
2710                             ]
2711
2712                         }
2713                     ]
2714
2715                 }
2716             ]
2717         };
2718
2719         if(this.animate){
2720             modal.cls += ' fade';
2721         }
2722
2723         return modal;
2724
2725     },
2726     getChildContainer : function() {
2727
2728          return this.bodyEl;
2729
2730     },
2731     getButtonContainer : function() {
2732          return this.el.select('.modal-footer div',true).first();
2733
2734     },
2735     initEvents : function()
2736     {
2737         if (this.allow_close) {
2738             this.closeEl.on('click', this.hide, this);
2739         }
2740         Roo.EventManager.onWindowResize(this.resize, this, true);
2741
2742
2743     },
2744
2745     resize : function()
2746     {
2747         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2748         if (this.fitwindow) {
2749             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2750             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2751             this.setSize(w,h);
2752         }
2753     },
2754
2755     setSize : function(w,h)
2756     {
2757         if (!w && !h) {
2758             return;
2759         }
2760         this.resizeTo(w,h);
2761     },
2762
2763     show : function() {
2764
2765         if (!this.rendered) {
2766             this.render();
2767         }
2768
2769         this.el.setStyle('display', 'block');
2770
2771         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2772             var _this = this;
2773             (function(){
2774                 this.el.addClass('in');
2775             }).defer(50, this);
2776         }else{
2777             this.el.addClass('in');
2778
2779         }
2780
2781         // not sure how we can show data in here..
2782         //if (this.tmpl) {
2783         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2784         //}
2785
2786         Roo.get(document.body).addClass("x-body-masked");
2787         
2788         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2789         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2790         this.maskEl.show();
2791         
2792         this.resize();
2793         
2794         this.fireEvent('show', this);
2795
2796         // set zindex here - otherwise it appears to be ignored...
2797         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2798
2799         (function () {
2800             this.items.forEach( function(e) {
2801                 e.layout ? e.layout() : false;
2802
2803             });
2804         }).defer(100,this);
2805
2806     },
2807     hide : function()
2808     {
2809         if(this.fireEvent("beforehide", this) !== false){
2810             this.maskEl.hide();
2811             Roo.get(document.body).removeClass("x-body-masked");
2812             this.el.removeClass('in');
2813             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2814
2815             if(this.animate){ // why
2816                 var _this = this;
2817                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2818             }else{
2819                 this.el.setStyle('display', 'none');
2820             }
2821             this.fireEvent('hide', this);
2822         }
2823     },
2824
2825     addButton : function(str, cb)
2826     {
2827
2828
2829         var b = Roo.apply({}, { html : str } );
2830         b.xns = b.xns || Roo.bootstrap;
2831         b.xtype = b.xtype || 'Button';
2832         if (typeof(b.listeners) == 'undefined') {
2833             b.listeners = { click : cb.createDelegate(this)  };
2834         }
2835
2836         var btn = Roo.factory(b);
2837
2838         btn.render(this.el.select('.modal-footer div').first());
2839
2840         return btn;
2841
2842     },
2843
2844     setDefaultButton : function(btn)
2845     {
2846         //this.el.select('.modal-footer').()
2847     },
2848     diff : false,
2849
2850     resizeTo: function(w,h)
2851     {
2852         // skip.. ?? why??
2853
2854         this.dialogEl.setWidth(w);
2855         if (this.diff === false) {
2856             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2857         }
2858
2859         this.bodyEl.setHeight(h-this.diff);
2860
2861         this.fireEvent('resize', this);
2862
2863     },
2864     setContentSize  : function(w, h)
2865     {
2866
2867     },
2868     onButtonClick: function(btn,e)
2869     {
2870         //Roo.log([a,b,c]);
2871         this.fireEvent('btnclick', btn.name, e);
2872     },
2873      /**
2874      * Set the title of the Dialog
2875      * @param {String} str new Title
2876      */
2877     setTitle: function(str) {
2878         this.titleEl.dom.innerHTML = str;
2879     },
2880     /**
2881      * Set the body of the Dialog
2882      * @param {String} str new Title
2883      */
2884     setBody: function(str) {
2885         this.bodyEl.dom.innerHTML = str;
2886     },
2887     /**
2888      * Set the body of the Dialog using the template
2889      * @param {Obj} data - apply this data to the template and replace the body contents.
2890      */
2891     applyBody: function(obj)
2892     {
2893         if (!this.tmpl) {
2894             Roo.log("Error - using apply Body without a template");
2895             //code
2896         }
2897         this.tmpl.overwrite(this.bodyEl, obj);
2898     }
2899
2900 });
2901
2902
2903 Roo.apply(Roo.bootstrap.Modal,  {
2904     /**
2905          * Button config that displays a single OK button
2906          * @type Object
2907          */
2908         OK :  [{
2909             name : 'ok',
2910             weight : 'primary',
2911             html : 'OK'
2912         }],
2913         /**
2914          * Button config that displays Yes and No buttons
2915          * @type Object
2916          */
2917         YESNO : [
2918             {
2919                 name  : 'no',
2920                 html : 'No'
2921             },
2922             {
2923                 name  :'yes',
2924                 weight : 'primary',
2925                 html : 'Yes'
2926             }
2927         ],
2928
2929         /**
2930          * Button config that displays OK and Cancel buttons
2931          * @type Object
2932          */
2933         OKCANCEL : [
2934             {
2935                name : 'cancel',
2936                 html : 'Cancel'
2937             },
2938             {
2939                 name : 'ok',
2940                 weight : 'primary',
2941                 html : 'OK'
2942             }
2943         ],
2944         /**
2945          * Button config that displays Yes, No and Cancel buttons
2946          * @type Object
2947          */
2948         YESNOCANCEL : [
2949             {
2950                 name : 'yes',
2951                 weight : 'primary',
2952                 html : 'Yes'
2953             },
2954             {
2955                 name : 'no',
2956                 html : 'No'
2957             },
2958             {
2959                 name : 'cancel',
2960                 html : 'Cancel'
2961             }
2962         ],
2963         
2964         zIndex : 10001
2965 });
2966 /*
2967  * - LGPL
2968  *
2969  * messagebox - can be used as a replace
2970  * 
2971  */
2972 /**
2973  * @class Roo.MessageBox
2974  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2975  * Example usage:
2976  *<pre><code>
2977 // Basic alert:
2978 Roo.Msg.alert('Status', 'Changes saved successfully.');
2979
2980 // Prompt for user data:
2981 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2982     if (btn == 'ok'){
2983         // process text value...
2984     }
2985 });
2986
2987 // Show a dialog using config options:
2988 Roo.Msg.show({
2989    title:'Save Changes?',
2990    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2991    buttons: Roo.Msg.YESNOCANCEL,
2992    fn: processResult,
2993    animEl: 'elId'
2994 });
2995 </code></pre>
2996  * @singleton
2997  */
2998 Roo.bootstrap.MessageBox = function(){
2999     var dlg, opt, mask, waitTimer;
3000     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3001     var buttons, activeTextEl, bwidth;
3002
3003     
3004     // private
3005     var handleButton = function(button){
3006         dlg.hide();
3007         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3008     };
3009
3010     // private
3011     var handleHide = function(){
3012         if(opt && opt.cls){
3013             dlg.el.removeClass(opt.cls);
3014         }
3015         //if(waitTimer){
3016         //    Roo.TaskMgr.stop(waitTimer);
3017         //    waitTimer = null;
3018         //}
3019     };
3020
3021     // private
3022     var updateButtons = function(b){
3023         var width = 0;
3024         if(!b){
3025             buttons["ok"].hide();
3026             buttons["cancel"].hide();
3027             buttons["yes"].hide();
3028             buttons["no"].hide();
3029             //dlg.footer.dom.style.display = 'none';
3030             return width;
3031         }
3032         dlg.footerEl.dom.style.display = '';
3033         for(var k in buttons){
3034             if(typeof buttons[k] != "function"){
3035                 if(b[k]){
3036                     buttons[k].show();
3037                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3038                     width += buttons[k].el.getWidth()+15;
3039                 }else{
3040                     buttons[k].hide();
3041                 }
3042             }
3043         }
3044         return width;
3045     };
3046
3047     // private
3048     var handleEsc = function(d, k, e){
3049         if(opt && opt.closable !== false){
3050             dlg.hide();
3051         }
3052         if(e){
3053             e.stopEvent();
3054         }
3055     };
3056
3057     return {
3058         /**
3059          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3060          * @return {Roo.BasicDialog} The BasicDialog element
3061          */
3062         getDialog : function(){
3063            if(!dlg){
3064                 dlg = new Roo.bootstrap.Modal( {
3065                     //draggable: true,
3066                     //resizable:false,
3067                     //constraintoviewport:false,
3068                     //fixedcenter:true,
3069                     //collapsible : false,
3070                     //shim:true,
3071                     //modal: true,
3072                 //    width: 'auto',
3073                   //  height:100,
3074                     //buttonAlign:"center",
3075                     closeClick : function(){
3076                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3077                             handleButton("no");
3078                         }else{
3079                             handleButton("cancel");
3080                         }
3081                     }
3082                 });
3083                 dlg.render();
3084                 dlg.on("hide", handleHide);
3085                 mask = dlg.mask;
3086                 //dlg.addKeyListener(27, handleEsc);
3087                 buttons = {};
3088                 this.buttons = buttons;
3089                 var bt = this.buttonText;
3090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3094                 //Roo.log(buttons);
3095                 bodyEl = dlg.bodyEl.createChild({
3096
3097                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3098                         '<textarea class="roo-mb-textarea"></textarea>' +
3099                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3100                 });
3101                 msgEl = bodyEl.dom.firstChild;
3102                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3103                 textboxEl.enableDisplayMode();
3104                 textboxEl.addKeyListener([10,13], function(){
3105                     if(dlg.isVisible() && opt && opt.buttons){
3106                         if(opt.buttons.ok){
3107                             handleButton("ok");
3108                         }else if(opt.buttons.yes){
3109                             handleButton("yes");
3110                         }
3111                     }
3112                 });
3113                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3114                 textareaEl.enableDisplayMode();
3115                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3116                 progressEl.enableDisplayMode();
3117                 
3118                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3119                 //var pf = progressEl.dom.firstChild;
3120                 //if (pf) {
3121                     //pp = Roo.get(pf.firstChild);
3122                     //pp.setHeight(pf.offsetHeight);
3123                 //}
3124                 
3125             }
3126             return dlg;
3127         },
3128
3129         /**
3130          * Updates the message box body text
3131          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3132          * the XHTML-compliant non-breaking space character '&amp;#160;')
3133          * @return {Roo.MessageBox} This message box
3134          */
3135         updateText : function(text)
3136         {
3137             if(!dlg.isVisible() && !opt.width){
3138                 dlg.dialogEl.setWidth(this.maxWidth);
3139                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3140             }
3141             msgEl.innerHTML = text || '&#160;';
3142       
3143             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3144             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3145             var w = Math.max(
3146                     Math.min(opt.width || cw , this.maxWidth), 
3147                     Math.max(opt.minWidth || this.minWidth, bwidth)
3148             );
3149             if(opt.prompt){
3150                 activeTextEl.setWidth(w);
3151             }
3152             if(dlg.isVisible()){
3153                 dlg.fixedcenter = false;
3154             }
3155             // to big, make it scroll. = But as usual stupid IE does not support
3156             // !important..
3157             
3158             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3159                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3160                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3161             } else {
3162                 bodyEl.dom.style.height = '';
3163                 bodyEl.dom.style.overflowY = '';
3164             }
3165             if (cw > w) {
3166                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3167             } else {
3168                 bodyEl.dom.style.overflowX = '';
3169             }
3170             
3171             dlg.setContentSize(w, bodyEl.getHeight());
3172             if(dlg.isVisible()){
3173                 dlg.fixedcenter = true;
3174             }
3175             return this;
3176         },
3177
3178         /**
3179          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3180          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3181          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3182          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3183          * @return {Roo.MessageBox} This message box
3184          */
3185         updateProgress : function(value, text){
3186             if(text){
3187                 this.updateText(text);
3188             }
3189             if (pp) { // weird bug on my firefox - for some reason this is not defined
3190                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3191             }
3192             return this;
3193         },        
3194
3195         /**
3196          * Returns true if the message box is currently displayed
3197          * @return {Boolean} True if the message box is visible, else false
3198          */
3199         isVisible : function(){
3200             return dlg && dlg.isVisible();  
3201         },
3202
3203         /**
3204          * Hides the message box if it is displayed
3205          */
3206         hide : function(){
3207             if(this.isVisible()){
3208                 dlg.hide();
3209             }  
3210         },
3211
3212         /**
3213          * Displays a new message box, or reinitializes an existing message box, based on the config options
3214          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3215          * The following config object properties are supported:
3216          * <pre>
3217 Property    Type             Description
3218 ----------  ---------------  ------------------------------------------------------------------------------------
3219 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3220                                    closes (defaults to undefined)
3221 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3222                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3223 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3224                                    progress and wait dialogs will ignore this property and always hide the
3225                                    close button as they can only be closed programmatically.
3226 cls               String           A custom CSS class to apply to the message box element
3227 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3228                                    displayed (defaults to 75)
3229 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3230                                    function will be btn (the name of the button that was clicked, if applicable,
3231                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3232                                    Progress and wait dialogs will ignore this option since they do not respond to
3233                                    user actions and can only be closed programmatically, so any required function
3234                                    should be called by the same code after it closes the dialog.
3235 icon              String           A CSS class that provides a background image to be used as an icon for
3236                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3237 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3238 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3239 modal             Boolean          False to allow user interaction with the page while the message box is
3240                                    displayed (defaults to true)
3241 msg               String           A string that will replace the existing message box body text (defaults
3242                                    to the XHTML-compliant non-breaking space character '&#160;')
3243 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3244 progress          Boolean          True to display a progress bar (defaults to false)
3245 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3246 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3247 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3248 title             String           The title text
3249 value             String           The string value to set into the active textbox element if displayed
3250 wait              Boolean          True to display a progress bar (defaults to false)
3251 width             Number           The width of the dialog in pixels
3252 </pre>
3253          *
3254          * Example usage:
3255          * <pre><code>
3256 Roo.Msg.show({
3257    title: 'Address',
3258    msg: 'Please enter your address:',
3259    width: 300,
3260    buttons: Roo.MessageBox.OKCANCEL,
3261    multiline: true,
3262    fn: saveAddress,
3263    animEl: 'addAddressBtn'
3264 });
3265 </code></pre>
3266          * @param {Object} config Configuration options
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         show : function(options)
3270         {
3271             
3272             // this causes nightmares if you show one dialog after another
3273             // especially on callbacks..
3274              
3275             if(this.isVisible()){
3276                 
3277                 this.hide();
3278                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3279                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3280                 Roo.log("New Dialog Message:" +  options.msg )
3281                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3282                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3283                 
3284             }
3285             var d = this.getDialog();
3286             opt = options;
3287             d.setTitle(opt.title || "&#160;");
3288             d.closeEl.setDisplayed(opt.closable !== false);
3289             activeTextEl = textboxEl;
3290             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3291             if(opt.prompt){
3292                 if(opt.multiline){
3293                     textboxEl.hide();
3294                     textareaEl.show();
3295                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3296                         opt.multiline : this.defaultTextHeight);
3297                     activeTextEl = textareaEl;
3298                 }else{
3299                     textboxEl.show();
3300                     textareaEl.hide();
3301                 }
3302             }else{
3303                 textboxEl.hide();
3304                 textareaEl.hide();
3305             }
3306             progressEl.setDisplayed(opt.progress === true);
3307             this.updateProgress(0);
3308             activeTextEl.dom.value = opt.value || "";
3309             if(opt.prompt){
3310                 dlg.setDefaultButton(activeTextEl);
3311             }else{
3312                 var bs = opt.buttons;
3313                 var db = null;
3314                 if(bs && bs.ok){
3315                     db = buttons["ok"];
3316                 }else if(bs && bs.yes){
3317                     db = buttons["yes"];
3318                 }
3319                 dlg.setDefaultButton(db);
3320             }
3321             bwidth = updateButtons(opt.buttons);
3322             this.updateText(opt.msg);
3323             if(opt.cls){
3324                 d.el.addClass(opt.cls);
3325             }
3326             d.proxyDrag = opt.proxyDrag === true;
3327             d.modal = opt.modal !== false;
3328             d.mask = opt.modal !== false ? mask : false;
3329             if(!d.isVisible()){
3330                 // force it to the end of the z-index stack so it gets a cursor in FF
3331                 document.body.appendChild(dlg.el.dom);
3332                 d.animateTarget = null;
3333                 d.show(options.animEl);
3334             }
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3340          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3341          * and closing the message box when the process is complete.
3342          * @param {String} title The title bar text
3343          * @param {String} msg The message box body text
3344          * @return {Roo.MessageBox} This message box
3345          */
3346         progress : function(title, msg){
3347             this.show({
3348                 title : title,
3349                 msg : msg,
3350                 buttons: false,
3351                 progress:true,
3352                 closable:false,
3353                 minWidth: this.minProgressWidth,
3354                 modal : true
3355             });
3356             return this;
3357         },
3358
3359         /**
3360          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3361          * If a callback function is passed it will be called after the user clicks the button, and the
3362          * id of the button that was clicked will be passed as the only parameter to the callback
3363          * (could also be the top-right close button).
3364          * @param {String} title The title bar text
3365          * @param {String} msg The message box body text
3366          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3367          * @param {Object} scope (optional) The scope of the callback function
3368          * @return {Roo.MessageBox} This message box
3369          */
3370         alert : function(title, msg, fn, scope)
3371         {
3372             this.show({
3373                 title : title,
3374                 msg : msg,
3375                 buttons: this.OK,
3376                 fn: fn,
3377                 closable : false,
3378                 scope : scope,
3379                 modal : true
3380             });
3381             return this;
3382         },
3383
3384         /**
3385          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3386          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3387          * You are responsible for closing the message box when the process is complete.
3388          * @param {String} msg The message box body text
3389          * @param {String} title (optional) The title bar text
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         wait : function(msg, title){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: false,
3397                 closable:false,
3398                 progress:true,
3399                 modal:true,
3400                 width:300,
3401                 wait:true
3402             });
3403             waitTimer = Roo.TaskMgr.start({
3404                 run: function(i){
3405                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3406                 },
3407                 interval: 1000
3408             });
3409             return this;
3410         },
3411
3412         /**
3413          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3414          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3415          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3416          * @param {String} title The title bar text
3417          * @param {String} msg The message box body text
3418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3419          * @param {Object} scope (optional) The scope of the callback function
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         confirm : function(title, msg, fn, scope){
3423             this.show({
3424                 title : title,
3425                 msg : msg,
3426                 buttons: this.YESNO,
3427                 fn: fn,
3428                 scope : scope,
3429                 modal : true
3430             });
3431             return this;
3432         },
3433
3434         /**
3435          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3436          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3437          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3438          * (could also be the top-right close button) and the text that was entered will be passed as the two
3439          * parameters to the callback.
3440          * @param {String} title The title bar text
3441          * @param {String} msg The message box body text
3442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3443          * @param {Object} scope (optional) The scope of the callback function
3444          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3445          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         prompt : function(title, msg, fn, scope, multiline){
3449             this.show({
3450                 title : title,
3451                 msg : msg,
3452                 buttons: this.OKCANCEL,
3453                 fn: fn,
3454                 minWidth:250,
3455                 scope : scope,
3456                 prompt:true,
3457                 multiline: multiline,
3458                 modal : true
3459             });
3460             return this;
3461         },
3462
3463         /**
3464          * Button config that displays a single OK button
3465          * @type Object
3466          */
3467         OK : {ok:true},
3468         /**
3469          * Button config that displays Yes and No buttons
3470          * @type Object
3471          */
3472         YESNO : {yes:true, no:true},
3473         /**
3474          * Button config that displays OK and Cancel buttons
3475          * @type Object
3476          */
3477         OKCANCEL : {ok:true, cancel:true},
3478         /**
3479          * Button config that displays Yes, No and Cancel buttons
3480          * @type Object
3481          */
3482         YESNOCANCEL : {yes:true, no:true, cancel:true},
3483
3484         /**
3485          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3486          * @type Number
3487          */
3488         defaultTextHeight : 75,
3489         /**
3490          * The maximum width in pixels of the message box (defaults to 600)
3491          * @type Number
3492          */
3493         maxWidth : 600,
3494         /**
3495          * The minimum width in pixels of the message box (defaults to 100)
3496          * @type Number
3497          */
3498         minWidth : 100,
3499         /**
3500          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3501          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3502          * @type Number
3503          */
3504         minProgressWidth : 250,
3505         /**
3506          * An object containing the default button text strings that can be overriden for localized language support.
3507          * Supported properties are: ok, cancel, yes and no.
3508          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3509          * @type Object
3510          */
3511         buttonText : {
3512             ok : "OK",
3513             cancel : "Cancel",
3514             yes : "Yes",
3515             no : "No"
3516         }
3517     };
3518 }();
3519
3520 /**
3521  * Shorthand for {@link Roo.MessageBox}
3522  */
3523 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3524 Roo.Msg = Roo.Msg || Roo.MessageBox;
3525 /*
3526  * - LGPL
3527  *
3528  * navbar
3529  * 
3530  */
3531
3532 /**
3533  * @class Roo.bootstrap.Navbar
3534  * @extends Roo.bootstrap.Component
3535  * Bootstrap Navbar class
3536
3537  * @constructor
3538  * Create a new Navbar
3539  * @param {Object} config The config object
3540  */
3541
3542
3543 Roo.bootstrap.Navbar = function(config){
3544     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3545     this.addEvents({
3546         // raw events
3547         /**
3548          * @event beforetoggle
3549          * Fire before toggle the menu
3550          * @param {Roo.EventObject} e
3551          */
3552         "beforetoggle" : true
3553     });
3554 };
3555
3556 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3557     
3558     
3559    
3560     // private
3561     navItems : false,
3562     loadMask : false,
3563     
3564     
3565     getAutoCreate : function(){
3566         
3567         
3568         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3569         
3570     },
3571     
3572     initEvents :function ()
3573     {
3574         //Roo.log(this.el.select('.navbar-toggle',true));
3575         this.el.select('.navbar-toggle',true).on('click', function() {
3576             if(this.fireEvent('beforetoggle', this) !== false){
3577                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3578             }
3579             
3580         }, this);
3581         
3582         var mark = {
3583             tag: "div",
3584             cls:"x-dlg-mask"
3585         };
3586         
3587         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3588         
3589         var size = this.el.getSize();
3590         this.maskEl.setSize(size.width, size.height);
3591         this.maskEl.enableDisplayMode("block");
3592         this.maskEl.hide();
3593         
3594         if(this.loadMask){
3595             this.maskEl.show();
3596         }
3597     },
3598     
3599     
3600     getChildContainer : function()
3601     {
3602         if (this.el.select('.collapse').getCount()) {
3603             return this.el.select('.collapse',true).first();
3604         }
3605         
3606         return this.el;
3607     },
3608     
3609     mask : function()
3610     {
3611         this.maskEl.show();
3612     },
3613     
3614     unmask : function()
3615     {
3616         this.maskEl.hide();
3617     } 
3618     
3619     
3620     
3621     
3622 });
3623
3624
3625
3626  
3627
3628  /*
3629  * - LGPL
3630  *
3631  * navbar
3632  * 
3633  */
3634
3635 /**
3636  * @class Roo.bootstrap.NavSimplebar
3637  * @extends Roo.bootstrap.Navbar
3638  * Bootstrap Sidebar class
3639  *
3640  * @cfg {Boolean} inverse is inverted color
3641  * 
3642  * @cfg {String} type (nav | pills | tabs)
3643  * @cfg {Boolean} arrangement stacked | justified
3644  * @cfg {String} align (left | right) alignment
3645  * 
3646  * @cfg {Boolean} main (true|false) main nav bar? default false
3647  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3648  * 
3649  * @cfg {String} tag (header|footer|nav|div) default is nav 
3650
3651  * 
3652  * 
3653  * 
3654  * @constructor
3655  * Create a new Sidebar
3656  * @param {Object} config The config object
3657  */
3658
3659
3660 Roo.bootstrap.NavSimplebar = function(config){
3661     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3662 };
3663
3664 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3665     
3666     inverse: false,
3667     
3668     type: false,
3669     arrangement: '',
3670     align : false,
3671     
3672     
3673     
3674     main : false,
3675     
3676     
3677     tag : false,
3678     
3679     
3680     getAutoCreate : function(){
3681         
3682         
3683         var cfg = {
3684             tag : this.tag || 'div',
3685             cls : 'navbar'
3686         };
3687           
3688         
3689         cfg.cn = [
3690             {
3691                 cls: 'nav',
3692                 tag : 'ul'
3693             }
3694         ];
3695         
3696          
3697         this.type = this.type || 'nav';
3698         if (['tabs','pills'].indexOf(this.type)!==-1) {
3699             cfg.cn[0].cls += ' nav-' + this.type
3700         
3701         
3702         } else {
3703             if (this.type!=='nav') {
3704                 Roo.log('nav type must be nav/tabs/pills')
3705             }
3706             cfg.cn[0].cls += ' navbar-nav'
3707         }
3708         
3709         
3710         
3711         
3712         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3713             cfg.cn[0].cls += ' nav-' + this.arrangement;
3714         }
3715         
3716         
3717         if (this.align === 'right') {
3718             cfg.cn[0].cls += ' navbar-right';
3719         }
3720         
3721         if (this.inverse) {
3722             cfg.cls += ' navbar-inverse';
3723             
3724         }
3725         
3726         
3727         return cfg;
3728     
3729         
3730     }
3731     
3732     
3733     
3734 });
3735
3736
3737
3738  
3739
3740  
3741        /*
3742  * - LGPL
3743  *
3744  * navbar
3745  * 
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavHeaderbar
3750  * @extends Roo.bootstrap.NavSimplebar
3751  * Bootstrap Sidebar class
3752  *
3753  * @cfg {String} brand what is brand
3754  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3755  * @cfg {String} brand_href href of the brand
3756  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3757  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3758  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3759  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3760  * 
3761  * @constructor
3762  * Create a new Sidebar
3763  * @param {Object} config The config object
3764  */
3765
3766
3767 Roo.bootstrap.NavHeaderbar = function(config){
3768     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3769       
3770 };
3771
3772 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3773     
3774     position: '',
3775     brand: '',
3776     brand_href: false,
3777     srButton : true,
3778     autohide : false,
3779     desktopCenter : false,
3780    
3781     
3782     getAutoCreate : function(){
3783         
3784         var   cfg = {
3785             tag: this.nav || 'nav',
3786             cls: 'navbar',
3787             role: 'navigation',
3788             cn: []
3789         };
3790         
3791         var cn = cfg.cn;
3792         if (this.desktopCenter) {
3793             cn.push({cls : 'container', cn : []});
3794             cn = cn[0].cn;
3795         }
3796         
3797         if(this.srButton){
3798             cn.push({
3799                 tag: 'div',
3800                 cls: 'navbar-header',
3801                 cn: [
3802                     {
3803                         tag: 'button',
3804                         type: 'button',
3805                         cls: 'navbar-toggle',
3806                         'data-toggle': 'collapse',
3807                         cn: [
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'sr-only',
3811                                 html: 'Toggle navigation'
3812                             },
3813                             {
3814                                 tag: 'span',
3815                                 cls: 'icon-bar'
3816                             },
3817                             {
3818                                 tag: 'span',
3819                                 cls: 'icon-bar'
3820                             },
3821                             {
3822                                 tag: 'span',
3823                                 cls: 'icon-bar'
3824                             }
3825                         ]
3826                     }
3827                 ]
3828             });
3829         }
3830         
3831         cn.push({
3832             tag: 'div',
3833             cls: 'collapse navbar-collapse',
3834             cn : []
3835         });
3836         
3837         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3838         
3839         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3840             cfg.cls += ' navbar-' + this.position;
3841             
3842             // tag can override this..
3843             
3844             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3845         }
3846         
3847         if (this.brand !== '') {
3848             cn[0].cn.push({
3849                 tag: 'a',
3850                 href: this.brand_href ? this.brand_href : '#',
3851                 cls: 'navbar-brand',
3852                 cn: [
3853                 this.brand
3854                 ]
3855             });
3856         }
3857         
3858         if(this.main){
3859             cfg.cls += ' main-nav';
3860         }
3861         
3862         
3863         return cfg;
3864
3865         
3866     },
3867     getHeaderChildContainer : function()
3868     {
3869         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3870             return this.el.select('.navbar-header',true).first();
3871         }
3872         
3873         return this.getChildContainer();
3874     },
3875     
3876     
3877     initEvents : function()
3878     {
3879         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3880         
3881         if (this.autohide) {
3882             
3883             var prevScroll = 0;
3884             var ft = this.el;
3885             
3886             Roo.get(document).on('scroll',function(e) {
3887                 var ns = Roo.get(document).getScroll().top;
3888                 var os = prevScroll;
3889                 prevScroll = ns;
3890                 
3891                 if(ns > os){
3892                     ft.removeClass('slideDown');
3893                     ft.addClass('slideUp');
3894                     return;
3895                 }
3896                 ft.removeClass('slideUp');
3897                 ft.addClass('slideDown');
3898                  
3899               
3900           },this);
3901         }
3902     }    
3903     
3904 });
3905
3906
3907
3908  
3909
3910  /*
3911  * - LGPL
3912  *
3913  * navbar
3914  * 
3915  */
3916
3917 /**
3918  * @class Roo.bootstrap.NavSidebar
3919  * @extends Roo.bootstrap.Navbar
3920  * Bootstrap Sidebar class
3921  * 
3922  * @constructor
3923  * Create a new Sidebar
3924  * @param {Object} config The config object
3925  */
3926
3927
3928 Roo.bootstrap.NavSidebar = function(config){
3929     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3930 };
3931
3932 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3933     
3934     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3935     
3936     getAutoCreate : function(){
3937         
3938         
3939         return  {
3940             tag: 'div',
3941             cls: 'sidebar sidebar-nav'
3942         };
3943     
3944         
3945     }
3946     
3947     
3948     
3949 });
3950
3951
3952
3953  
3954
3955  /*
3956  * - LGPL
3957  *
3958  * nav group
3959  * 
3960  */
3961
3962 /**
3963  * @class Roo.bootstrap.NavGroup
3964  * @extends Roo.bootstrap.Component
3965  * Bootstrap NavGroup class
3966  * @cfg {String} align (left|right)
3967  * @cfg {Boolean} inverse
3968  * @cfg {String} type (nav|pills|tab) default nav
3969  * @cfg {String} navId - reference Id for navbar.
3970
3971  * 
3972  * @constructor
3973  * Create a new nav group
3974  * @param {Object} config The config object
3975  */
3976
3977 Roo.bootstrap.NavGroup = function(config){
3978     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3979     this.navItems = [];
3980    
3981     Roo.bootstrap.NavGroup.register(this);
3982      this.addEvents({
3983         /**
3984              * @event changed
3985              * Fires when the active item changes
3986              * @param {Roo.bootstrap.NavGroup} this
3987              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3988              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3989          */
3990         'changed': true
3991      });
3992     
3993 };
3994
3995 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3996     
3997     align: '',
3998     inverse: false,
3999     form: false,
4000     type: 'nav',
4001     navId : '',
4002     // private
4003     
4004     navItems : false, 
4005     
4006     getAutoCreate : function()
4007     {
4008         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4009         
4010         cfg = {
4011             tag : 'ul',
4012             cls: 'nav' 
4013         };
4014         
4015         if (['tabs','pills'].indexOf(this.type)!==-1) {
4016             cfg.cls += ' nav-' + this.type
4017         } else {
4018             if (this.type!=='nav') {
4019                 Roo.log('nav type must be nav/tabs/pills')
4020             }
4021             cfg.cls += ' navbar-nav'
4022         }
4023         
4024         if (this.parent().sidebar) {
4025             cfg = {
4026                 tag: 'ul',
4027                 cls: 'dashboard-menu sidebar-menu'
4028             };
4029             
4030             return cfg;
4031         }
4032         
4033         if (this.form === true) {
4034             cfg = {
4035                 tag: 'form',
4036                 cls: 'navbar-form'
4037             };
4038             
4039             if (this.align === 'right') {
4040                 cfg.cls += ' navbar-right';
4041             } else {
4042                 cfg.cls += ' navbar-left';
4043             }
4044         }
4045         
4046         if (this.align === 'right') {
4047             cfg.cls += ' navbar-right';
4048         }
4049         
4050         if (this.inverse) {
4051             cfg.cls += ' navbar-inverse';
4052             
4053         }
4054         
4055         
4056         return cfg;
4057     },
4058     /**
4059     * sets the active Navigation item
4060     * @param {Roo.bootstrap.NavItem} the new current navitem
4061     */
4062     setActiveItem : function(item)
4063     {
4064         var prev = false;
4065         Roo.each(this.navItems, function(v){
4066             if (v == item) {
4067                 return ;
4068             }
4069             if (v.isActive()) {
4070                 v.setActive(false, true);
4071                 prev = v;
4072                 
4073             }
4074             
4075         });
4076
4077         item.setActive(true, true);
4078         this.fireEvent('changed', this, item, prev);
4079         
4080         
4081     },
4082     /**
4083     * gets the active Navigation item
4084     * @return {Roo.bootstrap.NavItem} the current navitem
4085     */
4086     getActive : function()
4087     {
4088         
4089         var prev = false;
4090         Roo.each(this.navItems, function(v){
4091             
4092             if (v.isActive()) {
4093                 prev = v;
4094                 
4095             }
4096             
4097         });
4098         return prev;
4099     },
4100     
4101     indexOfNav : function()
4102     {
4103         
4104         var prev = false;
4105         Roo.each(this.navItems, function(v,i){
4106             
4107             if (v.isActive()) {
4108                 prev = i;
4109                 
4110             }
4111             
4112         });
4113         return prev;
4114     },
4115     /**
4116     * adds a Navigation item
4117     * @param {Roo.bootstrap.NavItem} the navitem to add
4118     */
4119     addItem : function(cfg)
4120     {
4121         var cn = new Roo.bootstrap.NavItem(cfg);
4122         this.register(cn);
4123         cn.parentId = this.id;
4124         cn.onRender(this.el, null);
4125         return cn;
4126     },
4127     /**
4128     * register a Navigation item
4129     * @param {Roo.bootstrap.NavItem} the navitem to add
4130     */
4131     register : function(item)
4132     {
4133         this.navItems.push( item);
4134         item.navId = this.navId;
4135     
4136     },
4137     
4138     /**
4139     * clear all the Navigation item
4140     */
4141    
4142     clearAll : function()
4143     {
4144         this.navItems = [];
4145         this.el.dom.innerHTML = '';
4146     },
4147     
4148     getNavItem: function(tabId)
4149     {
4150         var ret = false;
4151         Roo.each(this.navItems, function(e) {
4152             if (e.tabId == tabId) {
4153                ret =  e;
4154                return false;
4155             }
4156             return true;
4157             
4158         });
4159         return ret;
4160     },
4161     
4162     setActiveNext : function()
4163     {
4164         var i = this.indexOfNav(this.getActive());
4165         if (i > this.navItems.length) {
4166             return;
4167         }
4168         this.setActiveItem(this.navItems[i+1]);
4169     },
4170     setActivePrev : function()
4171     {
4172         var i = this.indexOfNav(this.getActive());
4173         if (i  < 1) {
4174             return;
4175         }
4176         this.setActiveItem(this.navItems[i-1]);
4177     },
4178     clearWasActive : function(except) {
4179         Roo.each(this.navItems, function(e) {
4180             if (e.tabId != except.tabId && e.was_active) {
4181                e.was_active = false;
4182                return false;
4183             }
4184             return true;
4185             
4186         });
4187     },
4188     getWasActive : function ()
4189     {
4190         var r = false;
4191         Roo.each(this.navItems, function(e) {
4192             if (e.was_active) {
4193                r = e;
4194                return false;
4195             }
4196             return true;
4197             
4198         });
4199         return r;
4200     }
4201     
4202     
4203 });
4204
4205  
4206 Roo.apply(Roo.bootstrap.NavGroup, {
4207     
4208     groups: {},
4209      /**
4210     * register a Navigation Group
4211     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4212     */
4213     register : function(navgrp)
4214     {
4215         this.groups[navgrp.navId] = navgrp;
4216         
4217     },
4218     /**
4219     * fetch a Navigation Group based on the navigation ID
4220     * @param {string} the navgroup to add
4221     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4222     */
4223     get: function(navId) {
4224         if (typeof(this.groups[navId]) == 'undefined') {
4225             return false;
4226             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4227         }
4228         return this.groups[navId] ;
4229     }
4230     
4231     
4232     
4233 });
4234
4235  /*
4236  * - LGPL
4237  *
4238  * row
4239  * 
4240  */
4241
4242 /**
4243  * @class Roo.bootstrap.NavItem
4244  * @extends Roo.bootstrap.Component
4245  * Bootstrap Navbar.NavItem class
4246  * @cfg {String} href  link to
4247  * @cfg {String} html content of button
4248  * @cfg {String} badge text inside badge
4249  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4250  * @cfg {String} glyphicon name of glyphicon
4251  * @cfg {String} icon name of font awesome icon
4252  * @cfg {Boolean} active Is item active
4253  * @cfg {Boolean} disabled Is item disabled
4254  
4255  * @cfg {Boolean} preventDefault (true | false) default false
4256  * @cfg {String} tabId the tab that this item activates.
4257  * @cfg {String} tagtype (a|span) render as a href or span?
4258  * @cfg {Boolean} animateRef (true|false) link to element default false  
4259   
4260  * @constructor
4261  * Create a new Navbar Item
4262  * @param {Object} config The config object
4263  */
4264 Roo.bootstrap.NavItem = function(config){
4265     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4266     this.addEvents({
4267         // raw events
4268         /**
4269          * @event click
4270          * The raw click event for the entire grid.
4271          * @param {Roo.EventObject} e
4272          */
4273         "click" : true,
4274          /**
4275             * @event changed
4276             * Fires when the active item active state changes
4277             * @param {Roo.bootstrap.NavItem} this
4278             * @param {boolean} state the new state
4279              
4280          */
4281         'changed': true,
4282         /**
4283             * @event scrollto
4284             * Fires when scroll to element
4285             * @param {Roo.bootstrap.NavItem} this
4286             * @param {Object} options
4287             * @param {Roo.EventObject} e
4288              
4289          */
4290         'scrollto': true
4291     });
4292    
4293 };
4294
4295 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4296     
4297     href: false,
4298     html: '',
4299     badge: '',
4300     icon: false,
4301     glyphicon: false,
4302     active: false,
4303     preventDefault : false,
4304     tabId : false,
4305     tagtype : 'a',
4306     disabled : false,
4307     animateRef : false,
4308     was_active : false,
4309     
4310     getAutoCreate : function(){
4311          
4312         var cfg = {
4313             tag: 'li',
4314             cls: 'nav-item'
4315             
4316         };
4317         
4318         if (this.active) {
4319             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4320         }
4321         if (this.disabled) {
4322             cfg.cls += ' disabled';
4323         }
4324         
4325         if (this.href || this.html || this.glyphicon || this.icon) {
4326             cfg.cn = [
4327                 {
4328                     tag: this.tagtype,
4329                     href : this.href || "#",
4330                     html: this.html || ''
4331                 }
4332             ];
4333             
4334             if (this.icon) {
4335                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4336             }
4337
4338             if(this.glyphicon) {
4339                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4340             }
4341             
4342             if (this.menu) {
4343                 
4344                 cfg.cn[0].html += " <span class='caret'></span>";
4345              
4346             }
4347             
4348             if (this.badge !== '') {
4349                  
4350                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4351             }
4352         }
4353         
4354         
4355         
4356         return cfg;
4357     },
4358     initEvents: function() 
4359     {
4360         if (typeof (this.menu) != 'undefined') {
4361             this.menu.parentType = this.xtype;
4362             this.menu.triggerEl = this.el;
4363             this.menu = this.addxtype(Roo.apply({}, this.menu));
4364         }
4365         
4366         this.el.select('a',true).on('click', this.onClick, this);
4367         
4368         if(this.tagtype == 'span'){
4369             this.el.select('span',true).on('click', this.onClick, this);
4370         }
4371        
4372         // at this point parent should be available..
4373         this.parent().register(this);
4374     },
4375     
4376     onClick : function(e)
4377     {
4378         if (e.getTarget('.dropdown-menu-item')) {
4379             // did you click on a menu itemm.... - then don't trigger onclick..
4380             return;
4381         }
4382         
4383         if(
4384                 this.preventDefault || 
4385                 this.href == '#' 
4386         ){
4387             Roo.log("NavItem - prevent Default?");
4388             e.preventDefault();
4389         }
4390         
4391         if (this.disabled) {
4392             return;
4393         }
4394         
4395         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4396         if (tg && tg.transition) {
4397             Roo.log("waiting for the transitionend");
4398             return;
4399         }
4400         
4401         
4402         
4403         //Roo.log("fire event clicked");
4404         if(this.fireEvent('click', this, e) === false){
4405             return;
4406         };
4407         
4408         if(this.tagtype == 'span'){
4409             return;
4410         }
4411         
4412         //Roo.log(this.href);
4413         var ael = this.el.select('a',true).first();
4414         //Roo.log(ael);
4415         
4416         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4417             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4418             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4419                 return; // ignore... - it's a 'hash' to another page.
4420             }
4421             Roo.log("NavItem - prevent Default?");
4422             e.preventDefault();
4423             this.scrollToElement(e);
4424         }
4425         
4426         
4427         var p =  this.parent();
4428    
4429         if (['tabs','pills'].indexOf(p.type)!==-1) {
4430             if (typeof(p.setActiveItem) !== 'undefined') {
4431                 p.setActiveItem(this);
4432             }
4433         }
4434         
4435         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4436         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4437             // remove the collapsed menu expand...
4438             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4439         }
4440     },
4441     
4442     isActive: function () {
4443         return this.active
4444     },
4445     setActive : function(state, fire, is_was_active)
4446     {
4447         if (this.active && !state && this.navId) {
4448             this.was_active = true;
4449             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4450             if (nv) {
4451                 nv.clearWasActive(this);
4452             }
4453             
4454         }
4455         this.active = state;
4456         
4457         if (!state ) {
4458             this.el.removeClass('active');
4459         } else if (!this.el.hasClass('active')) {
4460             this.el.addClass('active');
4461         }
4462         if (fire) {
4463             this.fireEvent('changed', this, state);
4464         }
4465         
4466         // show a panel if it's registered and related..
4467         
4468         if (!this.navId || !this.tabId || !state || is_was_active) {
4469             return;
4470         }
4471         
4472         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4473         if (!tg) {
4474             return;
4475         }
4476         var pan = tg.getPanelByName(this.tabId);
4477         if (!pan) {
4478             return;
4479         }
4480         // if we can not flip to new panel - go back to old nav highlight..
4481         if (false == tg.showPanel(pan)) {
4482             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4483             if (nv) {
4484                 var onav = nv.getWasActive();
4485                 if (onav) {
4486                     onav.setActive(true, false, true);
4487                 }
4488             }
4489             
4490         }
4491         
4492         
4493         
4494     },
4495      // this should not be here...
4496     setDisabled : function(state)
4497     {
4498         this.disabled = state;
4499         if (!state ) {
4500             this.el.removeClass('disabled');
4501         } else if (!this.el.hasClass('disabled')) {
4502             this.el.addClass('disabled');
4503         }
4504         
4505     },
4506     
4507     /**
4508      * Fetch the element to display the tooltip on.
4509      * @return {Roo.Element} defaults to this.el
4510      */
4511     tooltipEl : function()
4512     {
4513         return this.el.select('' + this.tagtype + '', true).first();
4514     },
4515     
4516     scrollToElement : function(e)
4517     {
4518         var c = document.body;
4519         
4520         /*
4521          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4522          */
4523         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4524             c = document.documentElement;
4525         }
4526         
4527         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4528         
4529         if(!target){
4530             return;
4531         }
4532
4533         var o = target.calcOffsetsTo(c);
4534         
4535         var options = {
4536             target : target,
4537             value : o[1]
4538         };
4539         
4540         this.fireEvent('scrollto', this, options, e);
4541         
4542         Roo.get(c).scrollTo('top', options.value, true);
4543         
4544         return;
4545     }
4546 });
4547  
4548
4549  /*
4550  * - LGPL
4551  *
4552  * sidebar item
4553  *
4554  *  li
4555  *    <span> icon </span>
4556  *    <span> text </span>
4557  *    <span>badge </span>
4558  */
4559
4560 /**
4561  * @class Roo.bootstrap.NavSidebarItem
4562  * @extends Roo.bootstrap.NavItem
4563  * Bootstrap Navbar.NavSidebarItem class
4564  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4565  * {bool} open is the menu open
4566  * @constructor
4567  * Create a new Navbar Button
4568  * @param {Object} config The config object
4569  */
4570 Roo.bootstrap.NavSidebarItem = function(config){
4571     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4572     this.addEvents({
4573         // raw events
4574         /**
4575          * @event click
4576          * The raw click event for the entire grid.
4577          * @param {Roo.EventObject} e
4578          */
4579         "click" : true,
4580          /**
4581             * @event changed
4582             * Fires when the active item active state changes
4583             * @param {Roo.bootstrap.NavSidebarItem} this
4584             * @param {boolean} state the new state
4585              
4586          */
4587         'changed': true
4588     });
4589    
4590 };
4591
4592 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4593     
4594     badgeWeight : 'default',
4595     
4596     open: false,
4597     
4598     getAutoCreate : function(){
4599         
4600         
4601         var a = {
4602                 tag: 'a',
4603                 href : this.href || '#',
4604                 cls: '',
4605                 html : '',
4606                 cn : []
4607         };
4608         var cfg = {
4609             tag: 'li',
4610             cls: '',
4611             cn: [ a ]
4612         };
4613         var span = {
4614             tag: 'span',
4615             html : this.html || ''
4616         };
4617         
4618         
4619         if (this.active) {
4620             cfg.cls += ' active';
4621         }
4622         
4623         if (this.disabled) {
4624             cfg.cls += ' disabled';
4625         }
4626         if (this.open) {
4627             cfg.cls += ' open x-open';
4628         }
4629         // left icon..
4630         if (this.glyphicon || this.icon) {
4631             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4632             a.cn.push({ tag : 'i', cls : c }) ;
4633         }
4634         // html..
4635         a.cn.push(span);
4636         // then badge..
4637         if (this.badge !== '') {
4638             
4639             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4640         }
4641         // fi
4642         if (this.menu) {
4643             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4644             a.cls += 'dropdown-toggle treeview' ;
4645         }
4646         
4647         return cfg;
4648          
4649            
4650     },
4651     
4652     initEvents : function()
4653     { 
4654         if (typeof (this.menu) != 'undefined') {
4655             this.menu.parentType = this.xtype;
4656             this.menu.triggerEl = this.el;
4657             this.menu = this.addxtype(Roo.apply({}, this.menu));
4658         }
4659         
4660         this.el.on('click', this.onClick, this);
4661        
4662     
4663         if(this.badge !== ''){
4664  
4665             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4666         }
4667         
4668     },
4669     
4670     onClick : function(e)
4671     {
4672         if(this.disabled){
4673             e.preventDefault();
4674             return;
4675         }
4676         
4677         if(this.preventDefault){
4678             e.preventDefault();
4679         }
4680         
4681         this.fireEvent('click', this);
4682     },
4683     
4684     disable : function()
4685     {
4686         this.setDisabled(true);
4687     },
4688     
4689     enable : function()
4690     {
4691         this.setDisabled(false);
4692     },
4693     
4694     setDisabled : function(state)
4695     {
4696         if(this.disabled == state){
4697             return;
4698         }
4699         
4700         this.disabled = state;
4701         
4702         if (state) {
4703             this.el.addClass('disabled');
4704             return;
4705         }
4706         
4707         this.el.removeClass('disabled');
4708         
4709         return;
4710     },
4711     
4712     setActive : function(state)
4713     {
4714         if(this.active == state){
4715             return;
4716         }
4717         
4718         this.active = state;
4719         
4720         if (state) {
4721             this.el.addClass('active');
4722             return;
4723         }
4724         
4725         this.el.removeClass('active');
4726         
4727         return;
4728     },
4729     
4730     isActive: function () 
4731     {
4732         return this.active;
4733     },
4734     
4735     setBadge : function(str)
4736     {
4737         if(!this.badgeEl){
4738             return;
4739         }
4740         
4741         this.badgeEl.dom.innerHTML = str;
4742     }
4743     
4744    
4745      
4746  
4747 });
4748  
4749
4750  /*
4751  * - LGPL
4752  *
4753  * row
4754  * 
4755  */
4756
4757 /**
4758  * @class Roo.bootstrap.Row
4759  * @extends Roo.bootstrap.Component
4760  * Bootstrap Row class (contains columns...)
4761  * 
4762  * @constructor
4763  * Create a new Row
4764  * @param {Object} config The config object
4765  */
4766
4767 Roo.bootstrap.Row = function(config){
4768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4769 };
4770
4771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4772     
4773     getAutoCreate : function(){
4774        return {
4775             cls: 'row clearfix'
4776        };
4777     }
4778     
4779     
4780 });
4781
4782  
4783
4784  /*
4785  * - LGPL
4786  *
4787  * element
4788  * 
4789  */
4790
4791 /**
4792  * @class Roo.bootstrap.Element
4793  * @extends Roo.bootstrap.Component
4794  * Bootstrap Element class
4795  * @cfg {String} html contents of the element
4796  * @cfg {String} tag tag of the element
4797  * @cfg {String} cls class of the element
4798  * @cfg {Boolean} preventDefault (true|false) default false
4799  * @cfg {Boolean} clickable (true|false) default false
4800  * 
4801  * @constructor
4802  * Create a new Element
4803  * @param {Object} config The config object
4804  */
4805
4806 Roo.bootstrap.Element = function(config){
4807     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4808     
4809     this.addEvents({
4810         // raw events
4811         /**
4812          * @event click
4813          * When a element is chick
4814          * @param {Roo.bootstrap.Element} this
4815          * @param {Roo.EventObject} e
4816          */
4817         "click" : true
4818     });
4819 };
4820
4821 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4822     
4823     tag: 'div',
4824     cls: '',
4825     html: '',
4826     preventDefault: false, 
4827     clickable: false,
4828     
4829     getAutoCreate : function(){
4830         
4831         var cfg = {
4832             tag: this.tag,
4833             cls: this.cls,
4834             html: this.html
4835         };
4836         
4837         return cfg;
4838     },
4839     
4840     initEvents: function() 
4841     {
4842         Roo.bootstrap.Element.superclass.initEvents.call(this);
4843         
4844         if(this.clickable){
4845             this.el.on('click', this.onClick, this);
4846         }
4847         
4848     },
4849     
4850     onClick : function(e)
4851     {
4852         if(this.preventDefault){
4853             e.preventDefault();
4854         }
4855         
4856         this.fireEvent('click', this, e);
4857     },
4858     
4859     getValue : function()
4860     {
4861         return this.el.dom.innerHTML;
4862     },
4863     
4864     setValue : function(value)
4865     {
4866         this.el.dom.innerHTML = value;
4867     }
4868    
4869 });
4870
4871  
4872
4873  /*
4874  * - LGPL
4875  *
4876  * pagination
4877  * 
4878  */
4879
4880 /**
4881  * @class Roo.bootstrap.Pagination
4882  * @extends Roo.bootstrap.Component
4883  * Bootstrap Pagination class
4884  * @cfg {String} size xs | sm | md | lg
4885  * @cfg {Boolean} inverse false | true
4886  * 
4887  * @constructor
4888  * Create a new Pagination
4889  * @param {Object} config The config object
4890  */
4891
4892 Roo.bootstrap.Pagination = function(config){
4893     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4894 };
4895
4896 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4897     
4898     cls: false,
4899     size: false,
4900     inverse: false,
4901     
4902     getAutoCreate : function(){
4903         var cfg = {
4904             tag: 'ul',
4905                 cls: 'pagination'
4906         };
4907         if (this.inverse) {
4908             cfg.cls += ' inverse';
4909         }
4910         if (this.html) {
4911             cfg.html=this.html;
4912         }
4913         if (this.cls) {
4914             cfg.cls += " " + this.cls;
4915         }
4916         return cfg;
4917     }
4918    
4919 });
4920
4921  
4922
4923  /*
4924  * - LGPL
4925  *
4926  * Pagination item
4927  * 
4928  */
4929
4930
4931 /**
4932  * @class Roo.bootstrap.PaginationItem
4933  * @extends Roo.bootstrap.Component
4934  * Bootstrap PaginationItem class
4935  * @cfg {String} html text
4936  * @cfg {String} href the link
4937  * @cfg {Boolean} preventDefault (true | false) default true
4938  * @cfg {Boolean} active (true | false) default false
4939  * @cfg {Boolean} disabled default false
4940  * 
4941  * 
4942  * @constructor
4943  * Create a new PaginationItem
4944  * @param {Object} config The config object
4945  */
4946
4947
4948 Roo.bootstrap.PaginationItem = function(config){
4949     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4950     this.addEvents({
4951         // raw events
4952         /**
4953          * @event click
4954          * The raw click event for the entire grid.
4955          * @param {Roo.EventObject} e
4956          */
4957         "click" : true
4958     });
4959 };
4960
4961 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4962     
4963     href : false,
4964     html : false,
4965     preventDefault: true,
4966     active : false,
4967     cls : false,
4968     disabled: false,
4969     
4970     getAutoCreate : function(){
4971         var cfg= {
4972             tag: 'li',
4973             cn: [
4974                 {
4975                     tag : 'a',
4976                     href : this.href ? this.href : '#',
4977                     html : this.html ? this.html : ''
4978                 }
4979             ]
4980         };
4981         
4982         if(this.cls){
4983             cfg.cls = this.cls;
4984         }
4985         
4986         if(this.disabled){
4987             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4988         }
4989         
4990         if(this.active){
4991             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4992         }
4993         
4994         return cfg;
4995     },
4996     
4997     initEvents: function() {
4998         
4999         this.el.on('click', this.onClick, this);
5000         
5001     },
5002     onClick : function(e)
5003     {
5004         Roo.log('PaginationItem on click ');
5005         if(this.preventDefault){
5006             e.preventDefault();
5007         }
5008         
5009         if(this.disabled){
5010             return;
5011         }
5012         
5013         this.fireEvent('click', this, e);
5014     }
5015    
5016 });
5017
5018  
5019
5020  /*
5021  * - LGPL
5022  *
5023  * slider
5024  * 
5025  */
5026
5027
5028 /**
5029  * @class Roo.bootstrap.Slider
5030  * @extends Roo.bootstrap.Component
5031  * Bootstrap Slider class
5032  *    
5033  * @constructor
5034  * Create a new Slider
5035  * @param {Object} config The config object
5036  */
5037
5038 Roo.bootstrap.Slider = function(config){
5039     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5040 };
5041
5042 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5043     
5044     getAutoCreate : function(){
5045         
5046         var cfg = {
5047             tag: 'div',
5048             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5049             cn: [
5050                 {
5051                     tag: 'a',
5052                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5053                 }
5054             ]
5055         };
5056         
5057         return cfg;
5058     }
5059    
5060 });
5061
5062  /*
5063  * Based on:
5064  * Ext JS Library 1.1.1
5065  * Copyright(c) 2006-2007, Ext JS, LLC.
5066  *
5067  * Originally Released Under LGPL - original licence link has changed is not relivant.
5068  *
5069  * Fork - LGPL
5070  * <script type="text/javascript">
5071  */
5072  
5073
5074 /**
5075  * @class Roo.grid.ColumnModel
5076  * @extends Roo.util.Observable
5077  * This is the default implementation of a ColumnModel used by the Grid. It defines
5078  * the columns in the grid.
5079  * <br>Usage:<br>
5080  <pre><code>
5081  var colModel = new Roo.grid.ColumnModel([
5082         {header: "Ticker", width: 60, sortable: true, locked: true},
5083         {header: "Company Name", width: 150, sortable: true},
5084         {header: "Market Cap.", width: 100, sortable: true},
5085         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5086         {header: "Employees", width: 100, sortable: true, resizable: false}
5087  ]);
5088  </code></pre>
5089  * <p>
5090  
5091  * The config options listed for this class are options which may appear in each
5092  * individual column definition.
5093  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5094  * @constructor
5095  * @param {Object} config An Array of column config objects. See this class's
5096  * config objects for details.
5097 */
5098 Roo.grid.ColumnModel = function(config){
5099         /**
5100      * The config passed into the constructor
5101      */
5102     this.config = config;
5103     this.lookup = {};
5104
5105     // if no id, create one
5106     // if the column does not have a dataIndex mapping,
5107     // map it to the order it is in the config
5108     for(var i = 0, len = config.length; i < len; i++){
5109         var c = config[i];
5110         if(typeof c.dataIndex == "undefined"){
5111             c.dataIndex = i;
5112         }
5113         if(typeof c.renderer == "string"){
5114             c.renderer = Roo.util.Format[c.renderer];
5115         }
5116         if(typeof c.id == "undefined"){
5117             c.id = Roo.id();
5118         }
5119         if(c.editor && c.editor.xtype){
5120             c.editor  = Roo.factory(c.editor, Roo.grid);
5121         }
5122         if(c.editor && c.editor.isFormField){
5123             c.editor = new Roo.grid.GridEditor(c.editor);
5124         }
5125         this.lookup[c.id] = c;
5126     }
5127
5128     /**
5129      * The width of columns which have no width specified (defaults to 100)
5130      * @type Number
5131      */
5132     this.defaultWidth = 100;
5133
5134     /**
5135      * Default sortable of columns which have no sortable specified (defaults to false)
5136      * @type Boolean
5137      */
5138     this.defaultSortable = false;
5139
5140     this.addEvents({
5141         /**
5142              * @event widthchange
5143              * Fires when the width of a column changes.
5144              * @param {ColumnModel} this
5145              * @param {Number} columnIndex The column index
5146              * @param {Number} newWidth The new width
5147              */
5148             "widthchange": true,
5149         /**
5150              * @event headerchange
5151              * Fires when the text of a header changes.
5152              * @param {ColumnModel} this
5153              * @param {Number} columnIndex The column index
5154              * @param {Number} newText The new header text
5155              */
5156             "headerchange": true,
5157         /**
5158              * @event hiddenchange
5159              * Fires when a column is hidden or "unhidden".
5160              * @param {ColumnModel} this
5161              * @param {Number} columnIndex The column index
5162              * @param {Boolean} hidden true if hidden, false otherwise
5163              */
5164             "hiddenchange": true,
5165             /**
5166          * @event columnmoved
5167          * Fires when a column is moved.
5168          * @param {ColumnModel} this
5169          * @param {Number} oldIndex
5170          * @param {Number} newIndex
5171          */
5172         "columnmoved" : true,
5173         /**
5174          * @event columlockchange
5175          * Fires when a column's locked state is changed
5176          * @param {ColumnModel} this
5177          * @param {Number} colIndex
5178          * @param {Boolean} locked true if locked
5179          */
5180         "columnlockchange" : true
5181     });
5182     Roo.grid.ColumnModel.superclass.constructor.call(this);
5183 };
5184 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5185     /**
5186      * @cfg {String} header The header text to display in the Grid view.
5187      */
5188     /**
5189      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5190      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5191      * specified, the column's index is used as an index into the Record's data Array.
5192      */
5193     /**
5194      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5195      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5196      */
5197     /**
5198      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5199      * Defaults to the value of the {@link #defaultSortable} property.
5200      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5201      */
5202     /**
5203      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5204      */
5205     /**
5206      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5207      */
5208     /**
5209      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5210      */
5211     /**
5212      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5213      */
5214     /**
5215      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5216      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5217      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5218      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5219      */
5220        /**
5221      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5222      */
5223     /**
5224      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5225      */
5226     /**
5227      * @cfg {String} cursor (Optional)
5228      */
5229     /**
5230      * @cfg {String} tooltip (Optional)
5231      */
5232     /**
5233      * @cfg {Number} xs (Optional)
5234      */
5235     /**
5236      * @cfg {Number} sm (Optional)
5237      */
5238     /**
5239      * @cfg {Number} md (Optional)
5240      */
5241     /**
5242      * @cfg {Number} lg (Optional)
5243      */
5244     /**
5245      * Returns the id of the column at the specified index.
5246      * @param {Number} index The column index
5247      * @return {String} the id
5248      */
5249     getColumnId : function(index){
5250         return this.config[index].id;
5251     },
5252
5253     /**
5254      * Returns the column for a specified id.
5255      * @param {String} id The column id
5256      * @return {Object} the column
5257      */
5258     getColumnById : function(id){
5259         return this.lookup[id];
5260     },
5261
5262     
5263     /**
5264      * Returns the column for a specified dataIndex.
5265      * @param {String} dataIndex The column dataIndex
5266      * @return {Object|Boolean} the column or false if not found
5267      */
5268     getColumnByDataIndex: function(dataIndex){
5269         var index = this.findColumnIndex(dataIndex);
5270         return index > -1 ? this.config[index] : false;
5271     },
5272     
5273     /**
5274      * Returns the index for a specified column id.
5275      * @param {String} id The column id
5276      * @return {Number} the index, or -1 if not found
5277      */
5278     getIndexById : function(id){
5279         for(var i = 0, len = this.config.length; i < len; i++){
5280             if(this.config[i].id == id){
5281                 return i;
5282             }
5283         }
5284         return -1;
5285     },
5286     
5287     /**
5288      * Returns the index for a specified column dataIndex.
5289      * @param {String} dataIndex The column dataIndex
5290      * @return {Number} the index, or -1 if not found
5291      */
5292     
5293     findColumnIndex : function(dataIndex){
5294         for(var i = 0, len = this.config.length; i < len; i++){
5295             if(this.config[i].dataIndex == dataIndex){
5296                 return i;
5297             }
5298         }
5299         return -1;
5300     },
5301     
5302     
5303     moveColumn : function(oldIndex, newIndex){
5304         var c = this.config[oldIndex];
5305         this.config.splice(oldIndex, 1);
5306         this.config.splice(newIndex, 0, c);
5307         this.dataMap = null;
5308         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5309     },
5310
5311     isLocked : function(colIndex){
5312         return this.config[colIndex].locked === true;
5313     },
5314
5315     setLocked : function(colIndex, value, suppressEvent){
5316         if(this.isLocked(colIndex) == value){
5317             return;
5318         }
5319         this.config[colIndex].locked = value;
5320         if(!suppressEvent){
5321             this.fireEvent("columnlockchange", this, colIndex, value);
5322         }
5323     },
5324
5325     getTotalLockedWidth : function(){
5326         var totalWidth = 0;
5327         for(var i = 0; i < this.config.length; i++){
5328             if(this.isLocked(i) && !this.isHidden(i)){
5329                 this.totalWidth += this.getColumnWidth(i);
5330             }
5331         }
5332         return totalWidth;
5333     },
5334
5335     getLockedCount : function(){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(!this.isLocked(i)){
5338                 return i;
5339             }
5340         }
5341         
5342         return this.config.length;
5343     },
5344
5345     /**
5346      * Returns the number of columns.
5347      * @return {Number}
5348      */
5349     getColumnCount : function(visibleOnly){
5350         if(visibleOnly === true){
5351             var c = 0;
5352             for(var i = 0, len = this.config.length; i < len; i++){
5353                 if(!this.isHidden(i)){
5354                     c++;
5355                 }
5356             }
5357             return c;
5358         }
5359         return this.config.length;
5360     },
5361
5362     /**
5363      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5364      * @param {Function} fn
5365      * @param {Object} scope (optional)
5366      * @return {Array} result
5367      */
5368     getColumnsBy : function(fn, scope){
5369         var r = [];
5370         for(var i = 0, len = this.config.length; i < len; i++){
5371             var c = this.config[i];
5372             if(fn.call(scope||this, c, i) === true){
5373                 r[r.length] = c;
5374             }
5375         }
5376         return r;
5377     },
5378
5379     /**
5380      * Returns true if the specified column is sortable.
5381      * @param {Number} col The column index
5382      * @return {Boolean}
5383      */
5384     isSortable : function(col){
5385         if(typeof this.config[col].sortable == "undefined"){
5386             return this.defaultSortable;
5387         }
5388         return this.config[col].sortable;
5389     },
5390
5391     /**
5392      * Returns the rendering (formatting) function defined for the column.
5393      * @param {Number} col The column index.
5394      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5395      */
5396     getRenderer : function(col){
5397         if(!this.config[col].renderer){
5398             return Roo.grid.ColumnModel.defaultRenderer;
5399         }
5400         return this.config[col].renderer;
5401     },
5402
5403     /**
5404      * Sets the rendering (formatting) function for a column.
5405      * @param {Number} col The column index
5406      * @param {Function} fn The function to use to process the cell's raw data
5407      * to return HTML markup for the grid view. The render function is called with
5408      * the following parameters:<ul>
5409      * <li>Data value.</li>
5410      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5411      * <li>css A CSS style string to apply to the table cell.</li>
5412      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5413      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5414      * <li>Row index</li>
5415      * <li>Column index</li>
5416      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5417      */
5418     setRenderer : function(col, fn){
5419         this.config[col].renderer = fn;
5420     },
5421
5422     /**
5423      * Returns the width for the specified column.
5424      * @param {Number} col The column index
5425      * @return {Number}
5426      */
5427     getColumnWidth : function(col){
5428         return this.config[col].width * 1 || this.defaultWidth;
5429     },
5430
5431     /**
5432      * Sets the width for a column.
5433      * @param {Number} col The column index
5434      * @param {Number} width The new width
5435      */
5436     setColumnWidth : function(col, width, suppressEvent){
5437         this.config[col].width = width;
5438         this.totalWidth = null;
5439         if(!suppressEvent){
5440              this.fireEvent("widthchange", this, col, width);
5441         }
5442     },
5443
5444     /**
5445      * Returns the total width of all columns.
5446      * @param {Boolean} includeHidden True to include hidden column widths
5447      * @return {Number}
5448      */
5449     getTotalWidth : function(includeHidden){
5450         if(!this.totalWidth){
5451             this.totalWidth = 0;
5452             for(var i = 0, len = this.config.length; i < len; i++){
5453                 if(includeHidden || !this.isHidden(i)){
5454                     this.totalWidth += this.getColumnWidth(i);
5455                 }
5456             }
5457         }
5458         return this.totalWidth;
5459     },
5460
5461     /**
5462      * Returns the header for the specified column.
5463      * @param {Number} col The column index
5464      * @return {String}
5465      */
5466     getColumnHeader : function(col){
5467         return this.config[col].header;
5468     },
5469
5470     /**
5471      * Sets the header for a column.
5472      * @param {Number} col The column index
5473      * @param {String} header The new header
5474      */
5475     setColumnHeader : function(col, header){
5476         this.config[col].header = header;
5477         this.fireEvent("headerchange", this, col, header);
5478     },
5479
5480     /**
5481      * Returns the tooltip for the specified column.
5482      * @param {Number} col The column index
5483      * @return {String}
5484      */
5485     getColumnTooltip : function(col){
5486             return this.config[col].tooltip;
5487     },
5488     /**
5489      * Sets the tooltip for a column.
5490      * @param {Number} col The column index
5491      * @param {String} tooltip The new tooltip
5492      */
5493     setColumnTooltip : function(col, tooltip){
5494             this.config[col].tooltip = tooltip;
5495     },
5496
5497     /**
5498      * Returns the dataIndex for the specified column.
5499      * @param {Number} col The column index
5500      * @return {Number}
5501      */
5502     getDataIndex : function(col){
5503         return this.config[col].dataIndex;
5504     },
5505
5506     /**
5507      * Sets the dataIndex for a column.
5508      * @param {Number} col The column index
5509      * @param {Number} dataIndex The new dataIndex
5510      */
5511     setDataIndex : function(col, dataIndex){
5512         this.config[col].dataIndex = dataIndex;
5513     },
5514
5515     
5516     
5517     /**
5518      * Returns true if the cell is editable.
5519      * @param {Number} colIndex The column index
5520      * @param {Number} rowIndex The row index - this is nto actually used..?
5521      * @return {Boolean}
5522      */
5523     isCellEditable : function(colIndex, rowIndex){
5524         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5525     },
5526
5527     /**
5528      * Returns the editor defined for the cell/column.
5529      * return false or null to disable editing.
5530      * @param {Number} colIndex The column index
5531      * @param {Number} rowIndex The row index
5532      * @return {Object}
5533      */
5534     getCellEditor : function(colIndex, rowIndex){
5535         return this.config[colIndex].editor;
5536     },
5537
5538     /**
5539      * Sets if a column is editable.
5540      * @param {Number} col The column index
5541      * @param {Boolean} editable True if the column is editable
5542      */
5543     setEditable : function(col, editable){
5544         this.config[col].editable = editable;
5545     },
5546
5547
5548     /**
5549      * Returns true if the column is hidden.
5550      * @param {Number} colIndex The column index
5551      * @return {Boolean}
5552      */
5553     isHidden : function(colIndex){
5554         return this.config[colIndex].hidden;
5555     },
5556
5557
5558     /**
5559      * Returns true if the column width cannot be changed
5560      */
5561     isFixed : function(colIndex){
5562         return this.config[colIndex].fixed;
5563     },
5564
5565     /**
5566      * Returns true if the column can be resized
5567      * @return {Boolean}
5568      */
5569     isResizable : function(colIndex){
5570         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5571     },
5572     /**
5573      * Sets if a column is hidden.
5574      * @param {Number} colIndex The column index
5575      * @param {Boolean} hidden True if the column is hidden
5576      */
5577     setHidden : function(colIndex, hidden){
5578         this.config[colIndex].hidden = hidden;
5579         this.totalWidth = null;
5580         this.fireEvent("hiddenchange", this, colIndex, hidden);
5581     },
5582
5583     /**
5584      * Sets the editor for a column.
5585      * @param {Number} col The column index
5586      * @param {Object} editor The editor object
5587      */
5588     setEditor : function(col, editor){
5589         this.config[col].editor = editor;
5590     }
5591 });
5592
5593 Roo.grid.ColumnModel.defaultRenderer = function(value)
5594 {
5595     if(typeof value == "object") {
5596         return value;
5597     }
5598         if(typeof value == "string" && value.length < 1){
5599             return "&#160;";
5600         }
5601     
5602         return String.format("{0}", value);
5603 };
5604
5605 // Alias for backwards compatibility
5606 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5607 /*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 /**
5619  * @class Roo.LoadMask
5620  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5621  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5622  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5623  * element's UpdateManager load indicator and will be destroyed after the initial load.
5624  * @constructor
5625  * Create a new LoadMask
5626  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5627  * @param {Object} config The config object
5628  */
5629 Roo.LoadMask = function(el, config){
5630     this.el = Roo.get(el);
5631     Roo.apply(this, config);
5632     if(this.store){
5633         this.store.on('beforeload', this.onBeforeLoad, this);
5634         this.store.on('load', this.onLoad, this);
5635         this.store.on('loadexception', this.onLoadException, this);
5636         this.removeMask = false;
5637     }else{
5638         var um = this.el.getUpdateManager();
5639         um.showLoadIndicator = false; // disable the default indicator
5640         um.on('beforeupdate', this.onBeforeLoad, this);
5641         um.on('update', this.onLoad, this);
5642         um.on('failure', this.onLoad, this);
5643         this.removeMask = true;
5644     }
5645 };
5646
5647 Roo.LoadMask.prototype = {
5648     /**
5649      * @cfg {Boolean} removeMask
5650      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5651      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5652      */
5653     /**
5654      * @cfg {String} msg
5655      * The text to display in a centered loading message box (defaults to 'Loading...')
5656      */
5657     msg : 'Loading...',
5658     /**
5659      * @cfg {String} msgCls
5660      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5661      */
5662     msgCls : 'x-mask-loading',
5663
5664     /**
5665      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5666      * @type Boolean
5667      */
5668     disabled: false,
5669
5670     /**
5671      * Disables the mask to prevent it from being displayed
5672      */
5673     disable : function(){
5674        this.disabled = true;
5675     },
5676
5677     /**
5678      * Enables the mask so that it can be displayed
5679      */
5680     enable : function(){
5681         this.disabled = false;
5682     },
5683     
5684     onLoadException : function()
5685     {
5686         Roo.log(arguments);
5687         
5688         if (typeof(arguments[3]) != 'undefined') {
5689             Roo.MessageBox.alert("Error loading",arguments[3]);
5690         } 
5691         /*
5692         try {
5693             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5694                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5695             }   
5696         } catch(e) {
5697             
5698         }
5699         */
5700     
5701         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5702     },
5703     // private
5704     onLoad : function()
5705     {
5706         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5707     },
5708
5709     // private
5710     onBeforeLoad : function(){
5711         if(!this.disabled){
5712             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5713         }
5714     },
5715
5716     // private
5717     destroy : function(){
5718         if(this.store){
5719             this.store.un('beforeload', this.onBeforeLoad, this);
5720             this.store.un('load', this.onLoad, this);
5721             this.store.un('loadexception', this.onLoadException, this);
5722         }else{
5723             var um = this.el.getUpdateManager();
5724             um.un('beforeupdate', this.onBeforeLoad, this);
5725             um.un('update', this.onLoad, this);
5726             um.un('failure', this.onLoad, this);
5727         }
5728     }
5729 };/*
5730  * - LGPL
5731  *
5732  * table
5733  * 
5734  */
5735
5736 /**
5737  * @class Roo.bootstrap.Table
5738  * @extends Roo.bootstrap.Component
5739  * Bootstrap Table class
5740  * @cfg {String} cls table class
5741  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5742  * @cfg {String} bgcolor Specifies the background color for a table
5743  * @cfg {Number} border Specifies whether the table cells should have borders or not
5744  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5745  * @cfg {Number} cellspacing Specifies the space between cells
5746  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5747  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5748  * @cfg {String} sortable Specifies that the table should be sortable
5749  * @cfg {String} summary Specifies a summary of the content of a table
5750  * @cfg {Number} width Specifies the width of a table
5751  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5752  * 
5753  * @cfg {boolean} striped Should the rows be alternative striped
5754  * @cfg {boolean} bordered Add borders to the table
5755  * @cfg {boolean} hover Add hover highlighting
5756  * @cfg {boolean} condensed Format condensed
5757  * @cfg {boolean} responsive Format condensed
5758  * @cfg {Boolean} loadMask (true|false) default false
5759  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5760  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5761  * @cfg {Boolean} rowSelection (true|false) default false
5762  * @cfg {Boolean} cellSelection (true|false) default false
5763  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5764  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5765  
5766  * 
5767  * @constructor
5768  * Create a new Table
5769  * @param {Object} config The config object
5770  */
5771
5772 Roo.bootstrap.Table = function(config){
5773     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5774     
5775   
5776     
5777     // BC...
5778     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5779     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5780     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5781     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5782     
5783     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5784     if (this.sm) {
5785         this.sm.grid = this;
5786         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5787         this.sm = this.selModel;
5788         this.sm.xmodule = this.xmodule || false;
5789     }
5790     
5791     if (this.cm && typeof(this.cm.config) == 'undefined') {
5792         this.colModel = new Roo.grid.ColumnModel(this.cm);
5793         this.cm = this.colModel;
5794         this.cm.xmodule = this.xmodule || false;
5795     }
5796     if (this.store) {
5797         this.store= Roo.factory(this.store, Roo.data);
5798         this.ds = this.store;
5799         this.ds.xmodule = this.xmodule || false;
5800          
5801     }
5802     if (this.footer && this.store) {
5803         this.footer.dataSource = this.ds;
5804         this.footer = Roo.factory(this.footer);
5805     }
5806     
5807     /** @private */
5808     this.addEvents({
5809         /**
5810          * @event cellclick
5811          * Fires when a cell is clicked
5812          * @param {Roo.bootstrap.Table} this
5813          * @param {Roo.Element} el
5814          * @param {Number} rowIndex
5815          * @param {Number} columnIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "cellclick" : true,
5819         /**
5820          * @event celldblclick
5821          * Fires when a cell is double clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "celldblclick" : true,
5829         /**
5830          * @event rowclick
5831          * Fires when a row is clicked
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Roo.EventObject} e
5836          */
5837         "rowclick" : true,
5838         /**
5839          * @event rowdblclick
5840          * Fires when a row is double clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Roo.EventObject} e
5845          */
5846         "rowdblclick" : true,
5847         /**
5848          * @event mouseover
5849          * Fires when a mouseover occur
5850          * @param {Roo.bootstrap.Table} this
5851          * @param {Roo.Element} el
5852          * @param {Number} rowIndex
5853          * @param {Number} columnIndex
5854          * @param {Roo.EventObject} e
5855          */
5856         "mouseover" : true,
5857         /**
5858          * @event mouseout
5859          * Fires when a mouseout occur
5860          * @param {Roo.bootstrap.Table} this
5861          * @param {Roo.Element} el
5862          * @param {Number} rowIndex
5863          * @param {Number} columnIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "mouseout" : true,
5867         /**
5868          * @event rowclass
5869          * Fires when a row is rendered, so you can change add a style to it.
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5872          */
5873         'rowclass' : true,
5874           /**
5875          * @event rowsrendered
5876          * Fires when all the  rows have been rendered
5877          * @param {Roo.bootstrap.Table} this
5878          */
5879         'rowsrendered' : true,
5880         /**
5881          * @event contextmenu
5882          * The raw contextmenu event for the entire grid.
5883          * @param {Roo.EventObject} e
5884          */
5885         "contextmenu" : true,
5886         /**
5887          * @event rowcontextmenu
5888          * Fires when a row is right clicked
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Number} rowIndex
5891          * @param {Roo.EventObject} e
5892          */
5893         "rowcontextmenu" : true,
5894         /**
5895          * @event cellcontextmenu
5896          * Fires when a cell is right clicked
5897          * @param {Roo.bootstrap.Table} this
5898          * @param {Number} rowIndex
5899          * @param {Number} cellIndex
5900          * @param {Roo.EventObject} e
5901          */
5902          "cellcontextmenu" : true,
5903          /**
5904          * @event headercontextmenu
5905          * Fires when a header is right clicked
5906          * @param {Roo.bootstrap.Table} this
5907          * @param {Number} columnIndex
5908          * @param {Roo.EventObject} e
5909          */
5910         "headercontextmenu" : true
5911     });
5912 };
5913
5914 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5915     
5916     cls: false,
5917     align: false,
5918     bgcolor: false,
5919     border: false,
5920     cellpadding: false,
5921     cellspacing: false,
5922     frame: false,
5923     rules: false,
5924     sortable: false,
5925     summary: false,
5926     width: false,
5927     striped : false,
5928     scrollBody : false,
5929     bordered: false,
5930     hover:  false,
5931     condensed : false,
5932     responsive : false,
5933     sm : false,
5934     cm : false,
5935     store : false,
5936     loadMask : false,
5937     footerShow : true,
5938     headerShow : true,
5939   
5940     rowSelection : false,
5941     cellSelection : false,
5942     layout : false,
5943     
5944     // Roo.Element - the tbody
5945     mainBody: false,
5946     // Roo.Element - thead element
5947     mainHead: false,
5948     
5949     container: false, // used by gridpanel...
5950     
5951     getAutoCreate : function()
5952     {
5953         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5954         
5955         cfg = {
5956             tag: 'table',
5957             cls : 'table',
5958             cn : []
5959         };
5960         if (this.scrollBody) {
5961             cfg.cls += ' table-body-fixed';
5962         }    
5963         if (this.striped) {
5964             cfg.cls += ' table-striped';
5965         }
5966         
5967         if (this.hover) {
5968             cfg.cls += ' table-hover';
5969         }
5970         if (this.bordered) {
5971             cfg.cls += ' table-bordered';
5972         }
5973         if (this.condensed) {
5974             cfg.cls += ' table-condensed';
5975         }
5976         if (this.responsive) {
5977             cfg.cls += ' table-responsive';
5978         }
5979         
5980         if (this.cls) {
5981             cfg.cls+=  ' ' +this.cls;
5982         }
5983         
5984         // this lot should be simplifed...
5985         
5986         if (this.align) {
5987             cfg.align=this.align;
5988         }
5989         if (this.bgcolor) {
5990             cfg.bgcolor=this.bgcolor;
5991         }
5992         if (this.border) {
5993             cfg.border=this.border;
5994         }
5995         if (this.cellpadding) {
5996             cfg.cellpadding=this.cellpadding;
5997         }
5998         if (this.cellspacing) {
5999             cfg.cellspacing=this.cellspacing;
6000         }
6001         if (this.frame) {
6002             cfg.frame=this.frame;
6003         }
6004         if (this.rules) {
6005             cfg.rules=this.rules;
6006         }
6007         if (this.sortable) {
6008             cfg.sortable=this.sortable;
6009         }
6010         if (this.summary) {
6011             cfg.summary=this.summary;
6012         }
6013         if (this.width) {
6014             cfg.width=this.width;
6015         }
6016         if (this.layout) {
6017             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6018         }
6019         
6020         if(this.store || this.cm){
6021             if(this.headerShow){
6022                 cfg.cn.push(this.renderHeader());
6023             }
6024             
6025             cfg.cn.push(this.renderBody());
6026             
6027             if(this.footerShow){
6028                 cfg.cn.push(this.renderFooter());
6029             }
6030             // where does this come from?
6031             //cfg.cls+=  ' TableGrid';
6032         }
6033         
6034         return { cn : [ cfg ] };
6035     },
6036     
6037     initEvents : function()
6038     {   
6039         if(!this.store || !this.cm){
6040             return;
6041         }
6042         if (this.selModel) {
6043             this.selModel.initEvents();
6044         }
6045         
6046         
6047         //Roo.log('initEvents with ds!!!!');
6048         
6049         this.mainBody = this.el.select('tbody', true).first();
6050         this.mainHead = this.el.select('thead', true).first();
6051         
6052         
6053         
6054         
6055         var _this = this;
6056         
6057         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6058             e.on('click', _this.sort, _this);
6059         });
6060         
6061         this.mainBody.on("click", this.onClick, this);
6062         this.mainBody.on("dblclick", this.onDblClick, this);
6063         
6064         // why is this done????? = it breaks dialogs??
6065         //this.parent().el.setStyle('position', 'relative');
6066         
6067         
6068         if (this.footer) {
6069             this.footer.parentId = this.id;
6070             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6071         } 
6072         
6073         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6074         
6075         this.store.on('load', this.onLoad, this);
6076         this.store.on('beforeload', this.onBeforeLoad, this);
6077         this.store.on('update', this.onUpdate, this);
6078         this.store.on('add', this.onAdd, this);
6079         this.store.on("clear", this.clear, this);
6080         
6081         this.el.on("contextmenu", this.onContextMenu, this);
6082         
6083         this.mainBody.on('scroll', this.onBodyScroll, this);
6084         
6085         
6086     },
6087     
6088     onContextMenu : function(e, t)
6089     {
6090         this.processEvent("contextmenu", e);
6091     },
6092     
6093     processEvent : function(name, e)
6094     {
6095         if (name != 'touchstart' ) {
6096             this.fireEvent(name, e);    
6097         }
6098         
6099         var t = e.getTarget();
6100         
6101         var cell = Roo.get(t);
6102         
6103         if(!cell){
6104             return;
6105         }
6106         
6107         if(cell.findParent('tfoot', false, true)){
6108             return;
6109         }
6110         
6111         if(cell.findParent('thead', false, true)){
6112             
6113             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6114                 cell = Roo.get(t).findParent('th', false, true);
6115                 if (!cell) {
6116                     Roo.log("failed to find th in thead?");
6117                     Roo.log(e.getTarget());
6118                     return;
6119                 }
6120             }
6121             
6122             var cellIndex = cell.dom.cellIndex;
6123             
6124             var ename = name == 'touchstart' ? 'click' : name;
6125             this.fireEvent("header" + ename, this, cellIndex, e);
6126             
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = Roo.get(t).findParent('td', false, true);
6132             if (!cell) {
6133                 Roo.log("failed to find th in tbody?");
6134                 Roo.log(e.getTarget());
6135                 return;
6136             }
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1;
6142         
6143         if(row !== false){
6144             
6145             this.fireEvent("row" + name, this, rowIndex, e);
6146             
6147             if(cell !== false){
6148             
6149                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6150             }
6151         }
6152         
6153     },
6154     
6155     onMouseover : function(e, el)
6156     {
6157         var cell = Roo.get(el);
6158         
6159         if(!cell){
6160             return;
6161         }
6162         
6163         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6164             cell = cell.findParent('td', false, true);
6165         }
6166         
6167         var row = cell.findParent('tr', false, true);
6168         var cellIndex = cell.dom.cellIndex;
6169         var rowIndex = row.dom.rowIndex - 1; // start from 0
6170         
6171         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6172         
6173     },
6174     
6175     onMouseout : function(e, el)
6176     {
6177         var cell = Roo.get(el);
6178         
6179         if(!cell){
6180             return;
6181         }
6182         
6183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6184             cell = cell.findParent('td', false, true);
6185         }
6186         
6187         var row = cell.findParent('tr', false, true);
6188         var cellIndex = cell.dom.cellIndex;
6189         var rowIndex = row.dom.rowIndex - 1; // start from 0
6190         
6191         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6192         
6193     },
6194     
6195     onClick : function(e, el)
6196     {
6197         var cell = Roo.get(el);
6198         
6199         if(!cell || (!this.cellSelection && !this.rowSelection)){
6200             return;
6201         }
6202         
6203         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6204             cell = cell.findParent('td', false, true);
6205         }
6206         
6207         if(!cell || typeof(cell) == 'undefined'){
6208             return;
6209         }
6210         
6211         var row = cell.findParent('tr', false, true);
6212         
6213         if(!row || typeof(row) == 'undefined'){
6214             return;
6215         }
6216         
6217         var cellIndex = cell.dom.cellIndex;
6218         var rowIndex = this.getRowIndex(row);
6219         
6220         // why??? - should these not be based on SelectionModel?
6221         if(this.cellSelection){
6222             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6223         }
6224         
6225         if(this.rowSelection){
6226             this.fireEvent('rowclick', this, row, rowIndex, e);
6227         }
6228         
6229         
6230     },
6231         
6232     onDblClick : function(e,el)
6233     {
6234         var cell = Roo.get(el);
6235         
6236         if(!cell || (!this.cellSelection && !this.rowSelection)){
6237             return;
6238         }
6239         
6240         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6241             cell = cell.findParent('td', false, true);
6242         }
6243         
6244         if(!cell || typeof(cell) == 'undefined'){
6245             return;
6246         }
6247         
6248         var row = cell.findParent('tr', false, true);
6249         
6250         if(!row || typeof(row) == 'undefined'){
6251             return;
6252         }
6253         
6254         var cellIndex = cell.dom.cellIndex;
6255         var rowIndex = this.getRowIndex(row);
6256         
6257         if(this.cellSelection){
6258             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6259         }
6260         
6261         if(this.rowSelection){
6262             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6263         }
6264     },
6265     
6266     sort : function(e,el)
6267     {
6268         var col = Roo.get(el);
6269         
6270         if(!col.hasClass('sortable')){
6271             return;
6272         }
6273         
6274         var sort = col.attr('sort');
6275         var dir = 'ASC';
6276         
6277         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6278             dir = 'DESC';
6279         }
6280         
6281         this.store.sortInfo = {field : sort, direction : dir};
6282         
6283         if (this.footer) {
6284             Roo.log("calling footer first");
6285             this.footer.onClick('first');
6286         } else {
6287         
6288             this.store.load({ params : { start : 0 } });
6289         }
6290     },
6291     
6292     renderHeader : function()
6293     {
6294         var header = {
6295             tag: 'thead',
6296             cn : []
6297         };
6298         
6299         var cm = this.cm;
6300         this.totalWidth = 0;
6301         
6302         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6303             
6304             var config = cm.config[i];
6305             
6306             var c = {
6307                 tag: 'th',
6308                 style : '',
6309                 html: cm.getColumnHeader(i)
6310             };
6311             
6312             var hh = '';
6313             
6314             if(typeof(config.sortable) != 'undefined' && config.sortable){
6315                 c.cls = 'sortable';
6316                 c.html = '<i class="glyphicon"></i>' + c.html;
6317             }
6318             
6319             if(typeof(config.lgHeader) != 'undefined'){
6320                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6321             }
6322             
6323             if(typeof(config.mdHeader) != 'undefined'){
6324                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6325             }
6326             
6327             if(typeof(config.smHeader) != 'undefined'){
6328                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6329             }
6330             
6331             if(typeof(config.xsHeader) != 'undefined'){
6332                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6333             }
6334             
6335             if(hh.length){
6336                 c.html = hh;
6337             }
6338             
6339             if(typeof(config.tooltip) != 'undefined'){
6340                 c.tooltip = config.tooltip;
6341             }
6342             
6343             if(typeof(config.colspan) != 'undefined'){
6344                 c.colspan = config.colspan;
6345             }
6346             
6347             if(typeof(config.hidden) != 'undefined' && config.hidden){
6348                 c.style += ' display:none;';
6349             }
6350             
6351             if(typeof(config.dataIndex) != 'undefined'){
6352                 c.sort = config.dataIndex;
6353             }
6354             
6355            
6356             
6357             if(typeof(config.align) != 'undefined' && config.align.length){
6358                 c.style += ' text-align:' + config.align + ';';
6359             }
6360             
6361             if(typeof(config.width) != 'undefined'){
6362                 c.style += ' width:' + config.width + 'px;';
6363                 this.totalWidth += config.width;
6364             } else {
6365                 this.totalWidth += 100; // assume minimum of 100 per column?
6366             }
6367             
6368             if(typeof(config.cls) != 'undefined'){
6369                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6370             }
6371             
6372             ['xs','sm','md','lg'].map(function(size){
6373                 
6374                 if(typeof(config[size]) == 'undefined'){
6375                     return;
6376                 }
6377                 
6378                 if (!config[size]) { // 0 = hidden
6379                     c.cls += ' hidden-' + size;
6380                     return;
6381                 }
6382                 
6383                 c.cls += ' col-' + size + '-' + config[size];
6384
6385             });
6386             
6387             header.cn.push(c)
6388         }
6389         
6390         return header;
6391     },
6392     
6393     renderBody : function()
6394     {
6395         var body = {
6396             tag: 'tbody',
6397             cn : [
6398                 {
6399                     tag: 'tr',
6400                     cn : [
6401                         {
6402                             tag : 'td',
6403                             colspan :  this.cm.getColumnCount()
6404                         }
6405                     ]
6406                 }
6407             ]
6408         };
6409         
6410         return body;
6411     },
6412     
6413     renderFooter : function()
6414     {
6415         var footer = {
6416             tag: 'tfoot',
6417             cn : [
6418                 {
6419                     tag: 'tr',
6420                     cn : [
6421                         {
6422                             tag : 'td',
6423                             colspan :  this.cm.getColumnCount()
6424                         }
6425                     ]
6426                 }
6427             ]
6428         };
6429         
6430         return footer;
6431     },
6432     
6433     
6434     
6435     onLoad : function()
6436     {
6437 //        Roo.log('ds onload');
6438         this.clear();
6439         
6440         var _this = this;
6441         var cm = this.cm;
6442         var ds = this.store;
6443         
6444         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6445             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6446             if (_this.store.sortInfo) {
6447                     
6448                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6449                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6450                 }
6451                 
6452                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6453                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6454                 }
6455             }
6456         });
6457         
6458         var tbody =  this.mainBody;
6459               
6460         if(ds.getCount() > 0){
6461             ds.data.each(function(d,rowIndex){
6462                 var row =  this.renderRow(cm, ds, rowIndex);
6463                 
6464                 tbody.createChild(row);
6465                 
6466                 var _this = this;
6467                 
6468                 if(row.cellObjects.length){
6469                     Roo.each(row.cellObjects, function(r){
6470                         _this.renderCellObject(r);
6471                     })
6472                 }
6473                 
6474             }, this);
6475         }
6476         
6477         Roo.each(this.el.select('tbody td', true).elements, function(e){
6478             e.on('mouseover', _this.onMouseover, _this);
6479         });
6480         
6481         Roo.each(this.el.select('tbody td', true).elements, function(e){
6482             e.on('mouseout', _this.onMouseout, _this);
6483         });
6484         this.fireEvent('rowsrendered', this);
6485         //if(this.loadMask){
6486         //    this.maskEl.hide();
6487         //}
6488         
6489         this.autoSize();
6490     },
6491     
6492     
6493     onUpdate : function(ds,record)
6494     {
6495         this.refreshRow(record);
6496         this.autoSize();
6497     },
6498     
6499     onRemove : function(ds, record, index, isUpdate){
6500         if(isUpdate !== true){
6501             this.fireEvent("beforerowremoved", this, index, record);
6502         }
6503         var bt = this.mainBody.dom;
6504         
6505         var rows = this.el.select('tbody > tr', true).elements;
6506         
6507         if(typeof(rows[index]) != 'undefined'){
6508             bt.removeChild(rows[index].dom);
6509         }
6510         
6511 //        if(bt.rows[index]){
6512 //            bt.removeChild(bt.rows[index]);
6513 //        }
6514         
6515         if(isUpdate !== true){
6516             //this.stripeRows(index);
6517             //this.syncRowHeights(index, index);
6518             //this.layout();
6519             this.fireEvent("rowremoved", this, index, record);
6520         }
6521     },
6522     
6523     onAdd : function(ds, records, rowIndex)
6524     {
6525         //Roo.log('on Add called');
6526         // - note this does not handle multiple adding very well..
6527         var bt = this.mainBody.dom;
6528         for (var i =0 ; i < records.length;i++) {
6529             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6530             //Roo.log(records[i]);
6531             //Roo.log(this.store.getAt(rowIndex+i));
6532             this.insertRow(this.store, rowIndex + i, false);
6533             return;
6534         }
6535         
6536     },
6537     
6538     
6539     refreshRow : function(record){
6540         var ds = this.store, index;
6541         if(typeof record == 'number'){
6542             index = record;
6543             record = ds.getAt(index);
6544         }else{
6545             index = ds.indexOf(record);
6546         }
6547         this.insertRow(ds, index, true);
6548         this.autoSize();
6549         this.onRemove(ds, record, index+1, true);
6550         this.autoSize();
6551         //this.syncRowHeights(index, index);
6552         //this.layout();
6553         this.fireEvent("rowupdated", this, index, record);
6554     },
6555     
6556     insertRow : function(dm, rowIndex, isUpdate){
6557         
6558         if(!isUpdate){
6559             this.fireEvent("beforerowsinserted", this, rowIndex);
6560         }
6561             //var s = this.getScrollState();
6562         var row = this.renderRow(this.cm, this.store, rowIndex);
6563         // insert before rowIndex..
6564         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6565         
6566         var _this = this;
6567                 
6568         if(row.cellObjects.length){
6569             Roo.each(row.cellObjects, function(r){
6570                 _this.renderCellObject(r);
6571             })
6572         }
6573             
6574         if(!isUpdate){
6575             this.fireEvent("rowsinserted", this, rowIndex);
6576             //this.syncRowHeights(firstRow, lastRow);
6577             //this.stripeRows(firstRow);
6578             //this.layout();
6579         }
6580         
6581     },
6582     
6583     
6584     getRowDom : function(rowIndex)
6585     {
6586         var rows = this.el.select('tbody > tr', true).elements;
6587         
6588         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6589         
6590     },
6591     // returns the object tree for a tr..
6592   
6593     
6594     renderRow : function(cm, ds, rowIndex) 
6595     {
6596         
6597         var d = ds.getAt(rowIndex);
6598         
6599         var row = {
6600             tag : 'tr',
6601             cn : []
6602         };
6603             
6604         var cellObjects = [];
6605         
6606         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6607             var config = cm.config[i];
6608             
6609             var renderer = cm.getRenderer(i);
6610             var value = '';
6611             var id = false;
6612             
6613             if(typeof(renderer) !== 'undefined'){
6614                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6615             }
6616             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6617             // and are rendered into the cells after the row is rendered - using the id for the element.
6618             
6619             if(typeof(value) === 'object'){
6620                 id = Roo.id();
6621                 cellObjects.push({
6622                     container : id,
6623                     cfg : value 
6624                 })
6625             }
6626             
6627             var rowcfg = {
6628                 record: d,
6629                 rowIndex : rowIndex,
6630                 colIndex : i,
6631                 rowClass : ''
6632             };
6633
6634             this.fireEvent('rowclass', this, rowcfg);
6635             
6636             var td = {
6637                 tag: 'td',
6638                 cls : rowcfg.rowClass,
6639                 style: '',
6640                 html: (typeof(value) === 'object') ? '' : value
6641             };
6642             
6643             if (id) {
6644                 td.id = id;
6645             }
6646             
6647             if(typeof(config.colspan) != 'undefined'){
6648                 td.colspan = config.colspan;
6649             }
6650             
6651             if(typeof(config.hidden) != 'undefined' && config.hidden){
6652                 td.style += ' display:none;';
6653             }
6654             
6655             if(typeof(config.align) != 'undefined' && config.align.length){
6656                 td.style += ' text-align:' + config.align + ';';
6657             }
6658             
6659             if(typeof(config.width) != 'undefined'){
6660                 td.style += ' width:' +  config.width + 'px;';
6661             }
6662             
6663             if(typeof(config.cursor) != 'undefined'){
6664                 td.style += ' cursor:' +  config.cursor + ';';
6665             }
6666             
6667             if(typeof(config.cls) != 'undefined'){
6668                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6669             }
6670             
6671             ['xs','sm','md','lg'].map(function(size){
6672                 
6673                 if(typeof(config[size]) == 'undefined'){
6674                     return;
6675                 }
6676                 
6677                 if (!config[size]) { // 0 = hidden
6678                     td.cls += ' hidden-' + size;
6679                     return;
6680                 }
6681                 
6682                 td.cls += ' col-' + size + '-' + config[size];
6683
6684             });
6685              
6686             row.cn.push(td);
6687            
6688         }
6689         
6690         row.cellObjects = cellObjects;
6691         
6692         return row;
6693           
6694     },
6695     
6696     
6697     
6698     onBeforeLoad : function()
6699     {
6700         //Roo.log('ds onBeforeLoad');
6701         
6702         //this.clear();
6703         
6704         //if(this.loadMask){
6705         //    this.maskEl.show();
6706         //}
6707     },
6708      /**
6709      * Remove all rows
6710      */
6711     clear : function()
6712     {
6713         this.el.select('tbody', true).first().dom.innerHTML = '';
6714     },
6715     /**
6716      * Show or hide a row.
6717      * @param {Number} rowIndex to show or hide
6718      * @param {Boolean} state hide
6719      */
6720     setRowVisibility : function(rowIndex, state)
6721     {
6722         var bt = this.mainBody.dom;
6723         
6724         var rows = this.el.select('tbody > tr', true).elements;
6725         
6726         if(typeof(rows[rowIndex]) == 'undefined'){
6727             return;
6728         }
6729         rows[rowIndex].dom.style.display = state ? '' : 'none';
6730     },
6731     
6732     
6733     getSelectionModel : function(){
6734         if(!this.selModel){
6735             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6736         }
6737         return this.selModel;
6738     },
6739     /*
6740      * Render the Roo.bootstrap object from renderder
6741      */
6742     renderCellObject : function(r)
6743     {
6744         var _this = this;
6745         
6746         var t = r.cfg.render(r.container);
6747         
6748         if(r.cfg.cn){
6749             Roo.each(r.cfg.cn, function(c){
6750                 var child = {
6751                     container: t.getChildContainer(),
6752                     cfg: c
6753                 };
6754                 _this.renderCellObject(child);
6755             })
6756         }
6757     },
6758     
6759     getRowIndex : function(row)
6760     {
6761         var rowIndex = -1;
6762         
6763         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6764             if(el != row){
6765                 return;
6766             }
6767             
6768             rowIndex = index;
6769         });
6770         
6771         return rowIndex;
6772     },
6773      /**
6774      * Returns the grid's underlying element = used by panel.Grid
6775      * @return {Element} The element
6776      */
6777     getGridEl : function(){
6778         return this.el;
6779     },
6780      /**
6781      * Forces a resize - used by panel.Grid
6782      * @return {Element} The element
6783      */
6784     autoSize : function()
6785     {
6786         //var ctr = Roo.get(this.container.dom.parentElement);
6787         var ctr = Roo.get(this.el.dom);
6788         
6789         var thd = this.getGridEl().select('thead',true).first();
6790         var tbd = this.getGridEl().select('tbody', true).first();
6791         var tfd = this.getGridEl().select('tfoot', true).first();
6792         
6793         var cw = ctr.getWidth();
6794         
6795         if (tbd) {
6796             
6797             tbd.setSize(ctr.getWidth(),
6798                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6799             );
6800             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6801             cw -= barsize;
6802         }
6803         cw = Math.max(cw, this.totalWidth);
6804         this.getGridEl().select('tr',true).setWidth(cw);
6805         // resize 'expandable coloumn?
6806         
6807         return; // we doe not have a view in this design..
6808         
6809     },
6810     onBodyScroll: function()
6811     {
6812         
6813         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6814         this.mainHead.setStyle({
6815                     'position' : 'relative',
6816                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6817         });
6818         
6819         
6820     }
6821 });
6822
6823  
6824
6825  /*
6826  * - LGPL
6827  *
6828  * table cell
6829  * 
6830  */
6831
6832 /**
6833  * @class Roo.bootstrap.TableCell
6834  * @extends Roo.bootstrap.Component
6835  * Bootstrap TableCell class
6836  * @cfg {String} html cell contain text
6837  * @cfg {String} cls cell class
6838  * @cfg {String} tag cell tag (td|th) default td
6839  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6840  * @cfg {String} align Aligns the content in a cell
6841  * @cfg {String} axis Categorizes cells
6842  * @cfg {String} bgcolor Specifies the background color of a cell
6843  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6844  * @cfg {Number} colspan Specifies the number of columns a cell should span
6845  * @cfg {String} headers Specifies one or more header cells a cell is related to
6846  * @cfg {Number} height Sets the height of a cell
6847  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6848  * @cfg {Number} rowspan Sets the number of rows a cell should span
6849  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6850  * @cfg {String} valign Vertical aligns the content in a cell
6851  * @cfg {Number} width Specifies the width of a cell
6852  * 
6853  * @constructor
6854  * Create a new TableCell
6855  * @param {Object} config The config object
6856  */
6857
6858 Roo.bootstrap.TableCell = function(config){
6859     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6860 };
6861
6862 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6863     
6864     html: false,
6865     cls: false,
6866     tag: false,
6867     abbr: false,
6868     align: false,
6869     axis: false,
6870     bgcolor: false,
6871     charoff: false,
6872     colspan: false,
6873     headers: false,
6874     height: false,
6875     nowrap: false,
6876     rowspan: false,
6877     scope: false,
6878     valign: false,
6879     width: false,
6880     
6881     
6882     getAutoCreate : function(){
6883         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6884         
6885         cfg = {
6886             tag: 'td'
6887         };
6888         
6889         if(this.tag){
6890             cfg.tag = this.tag;
6891         }
6892         
6893         if (this.html) {
6894             cfg.html=this.html
6895         }
6896         if (this.cls) {
6897             cfg.cls=this.cls
6898         }
6899         if (this.abbr) {
6900             cfg.abbr=this.abbr
6901         }
6902         if (this.align) {
6903             cfg.align=this.align
6904         }
6905         if (this.axis) {
6906             cfg.axis=this.axis
6907         }
6908         if (this.bgcolor) {
6909             cfg.bgcolor=this.bgcolor
6910         }
6911         if (this.charoff) {
6912             cfg.charoff=this.charoff
6913         }
6914         if (this.colspan) {
6915             cfg.colspan=this.colspan
6916         }
6917         if (this.headers) {
6918             cfg.headers=this.headers
6919         }
6920         if (this.height) {
6921             cfg.height=this.height
6922         }
6923         if (this.nowrap) {
6924             cfg.nowrap=this.nowrap
6925         }
6926         if (this.rowspan) {
6927             cfg.rowspan=this.rowspan
6928         }
6929         if (this.scope) {
6930             cfg.scope=this.scope
6931         }
6932         if (this.valign) {
6933             cfg.valign=this.valign
6934         }
6935         if (this.width) {
6936             cfg.width=this.width
6937         }
6938         
6939         
6940         return cfg;
6941     }
6942    
6943 });
6944
6945  
6946
6947  /*
6948  * - LGPL
6949  *
6950  * table row
6951  * 
6952  */
6953
6954 /**
6955  * @class Roo.bootstrap.TableRow
6956  * @extends Roo.bootstrap.Component
6957  * Bootstrap TableRow class
6958  * @cfg {String} cls row class
6959  * @cfg {String} align Aligns the content in a table row
6960  * @cfg {String} bgcolor Specifies a background color for a table row
6961  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6962  * @cfg {String} valign Vertical aligns the content in a table row
6963  * 
6964  * @constructor
6965  * Create a new TableRow
6966  * @param {Object} config The config object
6967  */
6968
6969 Roo.bootstrap.TableRow = function(config){
6970     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6971 };
6972
6973 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6974     
6975     cls: false,
6976     align: false,
6977     bgcolor: false,
6978     charoff: false,
6979     valign: false,
6980     
6981     getAutoCreate : function(){
6982         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6983         
6984         cfg = {
6985             tag: 'tr'
6986         };
6987             
6988         if(this.cls){
6989             cfg.cls = this.cls;
6990         }
6991         if(this.align){
6992             cfg.align = this.align;
6993         }
6994         if(this.bgcolor){
6995             cfg.bgcolor = this.bgcolor;
6996         }
6997         if(this.charoff){
6998             cfg.charoff = this.charoff;
6999         }
7000         if(this.valign){
7001             cfg.valign = this.valign;
7002         }
7003         
7004         return cfg;
7005     }
7006    
7007 });
7008
7009  
7010
7011  /*
7012  * - LGPL
7013  *
7014  * table body
7015  * 
7016  */
7017
7018 /**
7019  * @class Roo.bootstrap.TableBody
7020  * @extends Roo.bootstrap.Component
7021  * Bootstrap TableBody class
7022  * @cfg {String} cls element class
7023  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7024  * @cfg {String} align Aligns the content inside the element
7025  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7026  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7027  * 
7028  * @constructor
7029  * Create a new TableBody
7030  * @param {Object} config The config object
7031  */
7032
7033 Roo.bootstrap.TableBody = function(config){
7034     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7035 };
7036
7037 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7038     
7039     cls: false,
7040     tag: false,
7041     align: false,
7042     charoff: false,
7043     valign: false,
7044     
7045     getAutoCreate : function(){
7046         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7047         
7048         cfg = {
7049             tag: 'tbody'
7050         };
7051             
7052         if (this.cls) {
7053             cfg.cls=this.cls
7054         }
7055         if(this.tag){
7056             cfg.tag = this.tag;
7057         }
7058         
7059         if(this.align){
7060             cfg.align = this.align;
7061         }
7062         if(this.charoff){
7063             cfg.charoff = this.charoff;
7064         }
7065         if(this.valign){
7066             cfg.valign = this.valign;
7067         }
7068         
7069         return cfg;
7070     }
7071     
7072     
7073 //    initEvents : function()
7074 //    {
7075 //        
7076 //        if(!this.store){
7077 //            return;
7078 //        }
7079 //        
7080 //        this.store = Roo.factory(this.store, Roo.data);
7081 //        this.store.on('load', this.onLoad, this);
7082 //        
7083 //        this.store.load();
7084 //        
7085 //    },
7086 //    
7087 //    onLoad: function () 
7088 //    {   
7089 //        this.fireEvent('load', this);
7090 //    }
7091 //    
7092 //   
7093 });
7094
7095  
7096
7097  /*
7098  * Based on:
7099  * Ext JS Library 1.1.1
7100  * Copyright(c) 2006-2007, Ext JS, LLC.
7101  *
7102  * Originally Released Under LGPL - original licence link has changed is not relivant.
7103  *
7104  * Fork - LGPL
7105  * <script type="text/javascript">
7106  */
7107
7108 // as we use this in bootstrap.
7109 Roo.namespace('Roo.form');
7110  /**
7111  * @class Roo.form.Action
7112  * Internal Class used to handle form actions
7113  * @constructor
7114  * @param {Roo.form.BasicForm} el The form element or its id
7115  * @param {Object} config Configuration options
7116  */
7117
7118  
7119  
7120 // define the action interface
7121 Roo.form.Action = function(form, options){
7122     this.form = form;
7123     this.options = options || {};
7124 };
7125 /**
7126  * Client Validation Failed
7127  * @const 
7128  */
7129 Roo.form.Action.CLIENT_INVALID = 'client';
7130 /**
7131  * Server Validation Failed
7132  * @const 
7133  */
7134 Roo.form.Action.SERVER_INVALID = 'server';
7135  /**
7136  * Connect to Server Failed
7137  * @const 
7138  */
7139 Roo.form.Action.CONNECT_FAILURE = 'connect';
7140 /**
7141  * Reading Data from Server Failed
7142  * @const 
7143  */
7144 Roo.form.Action.LOAD_FAILURE = 'load';
7145
7146 Roo.form.Action.prototype = {
7147     type : 'default',
7148     failureType : undefined,
7149     response : undefined,
7150     result : undefined,
7151
7152     // interface method
7153     run : function(options){
7154
7155     },
7156
7157     // interface method
7158     success : function(response){
7159
7160     },
7161
7162     // interface method
7163     handleResponse : function(response){
7164
7165     },
7166
7167     // default connection failure
7168     failure : function(response){
7169         
7170         this.response = response;
7171         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7172         this.form.afterAction(this, false);
7173     },
7174
7175     processResponse : function(response){
7176         this.response = response;
7177         if(!response.responseText){
7178             return true;
7179         }
7180         this.result = this.handleResponse(response);
7181         return this.result;
7182     },
7183
7184     // utility functions used internally
7185     getUrl : function(appendParams){
7186         var url = this.options.url || this.form.url || this.form.el.dom.action;
7187         if(appendParams){
7188             var p = this.getParams();
7189             if(p){
7190                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7191             }
7192         }
7193         return url;
7194     },
7195
7196     getMethod : function(){
7197         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7198     },
7199
7200     getParams : function(){
7201         var bp = this.form.baseParams;
7202         var p = this.options.params;
7203         if(p){
7204             if(typeof p == "object"){
7205                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7206             }else if(typeof p == 'string' && bp){
7207                 p += '&' + Roo.urlEncode(bp);
7208             }
7209         }else if(bp){
7210             p = Roo.urlEncode(bp);
7211         }
7212         return p;
7213     },
7214
7215     createCallback : function(){
7216         return {
7217             success: this.success,
7218             failure: this.failure,
7219             scope: this,
7220             timeout: (this.form.timeout*1000),
7221             upload: this.form.fileUpload ? this.success : undefined
7222         };
7223     }
7224 };
7225
7226 Roo.form.Action.Submit = function(form, options){
7227     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7228 };
7229
7230 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7231     type : 'submit',
7232
7233     haveProgress : false,
7234     uploadComplete : false,
7235     
7236     // uploadProgress indicator.
7237     uploadProgress : function()
7238     {
7239         if (!this.form.progressUrl) {
7240             return;
7241         }
7242         
7243         if (!this.haveProgress) {
7244             Roo.MessageBox.progress("Uploading", "Uploading");
7245         }
7246         if (this.uploadComplete) {
7247            Roo.MessageBox.hide();
7248            return;
7249         }
7250         
7251         this.haveProgress = true;
7252    
7253         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7254         
7255         var c = new Roo.data.Connection();
7256         c.request({
7257             url : this.form.progressUrl,
7258             params: {
7259                 id : uid
7260             },
7261             method: 'GET',
7262             success : function(req){
7263                //console.log(data);
7264                 var rdata = false;
7265                 var edata;
7266                 try  {
7267                    rdata = Roo.decode(req.responseText)
7268                 } catch (e) {
7269                     Roo.log("Invalid data from server..");
7270                     Roo.log(edata);
7271                     return;
7272                 }
7273                 if (!rdata || !rdata.success) {
7274                     Roo.log(rdata);
7275                     Roo.MessageBox.alert(Roo.encode(rdata));
7276                     return;
7277                 }
7278                 var data = rdata.data;
7279                 
7280                 if (this.uploadComplete) {
7281                    Roo.MessageBox.hide();
7282                    return;
7283                 }
7284                    
7285                 if (data){
7286                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7287                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7288                     );
7289                 }
7290                 this.uploadProgress.defer(2000,this);
7291             },
7292        
7293             failure: function(data) {
7294                 Roo.log('progress url failed ');
7295                 Roo.log(data);
7296             },
7297             scope : this
7298         });
7299            
7300     },
7301     
7302     
7303     run : function()
7304     {
7305         // run get Values on the form, so it syncs any secondary forms.
7306         this.form.getValues();
7307         
7308         var o = this.options;
7309         var method = this.getMethod();
7310         var isPost = method == 'POST';
7311         if(o.clientValidation === false || this.form.isValid()){
7312             
7313             if (this.form.progressUrl) {
7314                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7315                     (new Date() * 1) + '' + Math.random());
7316                     
7317             } 
7318             
7319             
7320             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7321                 form:this.form.el.dom,
7322                 url:this.getUrl(!isPost),
7323                 method: method,
7324                 params:isPost ? this.getParams() : null,
7325                 isUpload: this.form.fileUpload
7326             }));
7327             
7328             this.uploadProgress();
7329
7330         }else if (o.clientValidation !== false){ // client validation failed
7331             this.failureType = Roo.form.Action.CLIENT_INVALID;
7332             this.form.afterAction(this, false);
7333         }
7334     },
7335
7336     success : function(response)
7337     {
7338         this.uploadComplete= true;
7339         if (this.haveProgress) {
7340             Roo.MessageBox.hide();
7341         }
7342         
7343         
7344         var result = this.processResponse(response);
7345         if(result === true || result.success){
7346             this.form.afterAction(this, true);
7347             return;
7348         }
7349         if(result.errors){
7350             this.form.markInvalid(result.errors);
7351             this.failureType = Roo.form.Action.SERVER_INVALID;
7352         }
7353         this.form.afterAction(this, false);
7354     },
7355     failure : function(response)
7356     {
7357         this.uploadComplete= true;
7358         if (this.haveProgress) {
7359             Roo.MessageBox.hide();
7360         }
7361         
7362         this.response = response;
7363         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7364         this.form.afterAction(this, false);
7365     },
7366     
7367     handleResponse : function(response){
7368         if(this.form.errorReader){
7369             var rs = this.form.errorReader.read(response);
7370             var errors = [];
7371             if(rs.records){
7372                 for(var i = 0, len = rs.records.length; i < len; i++) {
7373                     var r = rs.records[i];
7374                     errors[i] = r.data;
7375                 }
7376             }
7377             if(errors.length < 1){
7378                 errors = null;
7379             }
7380             return {
7381                 success : rs.success,
7382                 errors : errors
7383             };
7384         }
7385         var ret = false;
7386         try {
7387             ret = Roo.decode(response.responseText);
7388         } catch (e) {
7389             ret = {
7390                 success: false,
7391                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7392                 errors : []
7393             };
7394         }
7395         return ret;
7396         
7397     }
7398 });
7399
7400
7401 Roo.form.Action.Load = function(form, options){
7402     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7403     this.reader = this.form.reader;
7404 };
7405
7406 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7407     type : 'load',
7408
7409     run : function(){
7410         
7411         Roo.Ajax.request(Roo.apply(
7412                 this.createCallback(), {
7413                     method:this.getMethod(),
7414                     url:this.getUrl(false),
7415                     params:this.getParams()
7416         }));
7417     },
7418
7419     success : function(response){
7420         
7421         var result = this.processResponse(response);
7422         if(result === true || !result.success || !result.data){
7423             this.failureType = Roo.form.Action.LOAD_FAILURE;
7424             this.form.afterAction(this, false);
7425             return;
7426         }
7427         this.form.clearInvalid();
7428         this.form.setValues(result.data);
7429         this.form.afterAction(this, true);
7430     },
7431
7432     handleResponse : function(response){
7433         if(this.form.reader){
7434             var rs = this.form.reader.read(response);
7435             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7436             return {
7437                 success : rs.success,
7438                 data : data
7439             };
7440         }
7441         return Roo.decode(response.responseText);
7442     }
7443 });
7444
7445 Roo.form.Action.ACTION_TYPES = {
7446     'load' : Roo.form.Action.Load,
7447     'submit' : Roo.form.Action.Submit
7448 };/*
7449  * - LGPL
7450  *
7451  * form
7452  *
7453  */
7454
7455 /**
7456  * @class Roo.bootstrap.Form
7457  * @extends Roo.bootstrap.Component
7458  * Bootstrap Form class
7459  * @cfg {String} method  GET | POST (default POST)
7460  * @cfg {String} labelAlign top | left (default top)
7461  * @cfg {String} align left  | right - for navbars
7462  * @cfg {Boolean} loadMask load mask when submit (default true)
7463
7464  *
7465  * @constructor
7466  * Create a new Form
7467  * @param {Object} config The config object
7468  */
7469
7470
7471 Roo.bootstrap.Form = function(config){
7472     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7473     
7474     Roo.bootstrap.Form.popover.apply();
7475     
7476     this.addEvents({
7477         /**
7478          * @event clientvalidation
7479          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7480          * @param {Form} this
7481          * @param {Boolean} valid true if the form has passed client-side validation
7482          */
7483         clientvalidation: true,
7484         /**
7485          * @event beforeaction
7486          * Fires before any action is performed. Return false to cancel the action.
7487          * @param {Form} this
7488          * @param {Action} action The action to be performed
7489          */
7490         beforeaction: true,
7491         /**
7492          * @event actionfailed
7493          * Fires when an action fails.
7494          * @param {Form} this
7495          * @param {Action} action The action that failed
7496          */
7497         actionfailed : true,
7498         /**
7499          * @event actioncomplete
7500          * Fires when an action is completed.
7501          * @param {Form} this
7502          * @param {Action} action The action that completed
7503          */
7504         actioncomplete : true
7505     });
7506
7507 };
7508
7509 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7510
7511      /**
7512      * @cfg {String} method
7513      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7514      */
7515     method : 'POST',
7516     /**
7517      * @cfg {String} url
7518      * The URL to use for form actions if one isn't supplied in the action options.
7519      */
7520     /**
7521      * @cfg {Boolean} fileUpload
7522      * Set to true if this form is a file upload.
7523      */
7524
7525     /**
7526      * @cfg {Object} baseParams
7527      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7528      */
7529
7530     /**
7531      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7532      */
7533     timeout: 30,
7534     /**
7535      * @cfg {Sting} align (left|right) for navbar forms
7536      */
7537     align : 'left',
7538
7539     // private
7540     activeAction : null,
7541
7542     /**
7543      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7544      * element by passing it or its id or mask the form itself by passing in true.
7545      * @type Mixed
7546      */
7547     waitMsgTarget : false,
7548
7549     loadMask : true,
7550     
7551     /**
7552      * @cfg {Boolean} errorMask (true|false) default false
7553      */
7554     errorMask : false,
7555
7556     getAutoCreate : function(){
7557
7558         var cfg = {
7559             tag: 'form',
7560             method : this.method || 'POST',
7561             id : this.id || Roo.id(),
7562             cls : ''
7563         };
7564         if (this.parent().xtype.match(/^Nav/)) {
7565             cfg.cls = 'navbar-form navbar-' + this.align;
7566
7567         }
7568
7569         if (this.labelAlign == 'left' ) {
7570             cfg.cls += ' form-horizontal';
7571         }
7572
7573
7574         return cfg;
7575     },
7576     initEvents : function()
7577     {
7578         this.el.on('submit', this.onSubmit, this);
7579         // this was added as random key presses on the form where triggering form submit.
7580         this.el.on('keypress', function(e) {
7581             if (e.getCharCode() != 13) {
7582                 return true;
7583             }
7584             // we might need to allow it for textareas.. and some other items.
7585             // check e.getTarget().
7586
7587             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7588                 return true;
7589             }
7590
7591             Roo.log("keypress blocked");
7592
7593             e.preventDefault();
7594             return false;
7595         });
7596         
7597     },
7598     // private
7599     onSubmit : function(e){
7600         e.stopEvent();
7601     },
7602
7603      /**
7604      * Returns true if client-side validation on the form is successful.
7605      * @return Boolean
7606      */
7607     isValid : function(){
7608         var items = this.getItems();
7609         var valid = true;
7610         var target = false;
7611         
7612         items.each(function(f){
7613             
7614             if(f.validate()){
7615                 return;
7616             }
7617             valid = false;
7618
7619             if(!target && f.el.isVisible(true)){
7620                 target = f;
7621             }
7622            
7623         });
7624         
7625         if(this.errorMask && !valid){
7626             Roo.bootstrap.Form.popover.mask(this, target);
7627         }
7628         
7629         return valid;
7630     },
7631     
7632     /**
7633      * Returns true if any fields in this form have changed since their original load.
7634      * @return Boolean
7635      */
7636     isDirty : function(){
7637         var dirty = false;
7638         var items = this.getItems();
7639         items.each(function(f){
7640            if(f.isDirty()){
7641                dirty = true;
7642                return false;
7643            }
7644            return true;
7645         });
7646         return dirty;
7647     },
7648      /**
7649      * Performs a predefined action (submit or load) or custom actions you define on this form.
7650      * @param {String} actionName The name of the action type
7651      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7652      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7653      * accept other config options):
7654      * <pre>
7655 Property          Type             Description
7656 ----------------  ---------------  ----------------------------------------------------------------------------------
7657 url               String           The url for the action (defaults to the form's url)
7658 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7659 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7660 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7661                                    validate the form on the client (defaults to false)
7662      * </pre>
7663      * @return {BasicForm} this
7664      */
7665     doAction : function(action, options){
7666         if(typeof action == 'string'){
7667             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7668         }
7669         if(this.fireEvent('beforeaction', this, action) !== false){
7670             this.beforeAction(action);
7671             action.run.defer(100, action);
7672         }
7673         return this;
7674     },
7675
7676     // private
7677     beforeAction : function(action){
7678         var o = action.options;
7679
7680         if(this.loadMask){
7681             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7682         }
7683         // not really supported yet.. ??
7684
7685         //if(this.waitMsgTarget === true){
7686         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7687         //}else if(this.waitMsgTarget){
7688         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7689         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7690         //}else {
7691         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7692        // }
7693
7694     },
7695
7696     // private
7697     afterAction : function(action, success){
7698         this.activeAction = null;
7699         var o = action.options;
7700
7701         //if(this.waitMsgTarget === true){
7702             this.el.unmask();
7703         //}else if(this.waitMsgTarget){
7704         //    this.waitMsgTarget.unmask();
7705         //}else{
7706         //    Roo.MessageBox.updateProgress(1);
7707         //    Roo.MessageBox.hide();
7708        // }
7709         //
7710         if(success){
7711             if(o.reset){
7712                 this.reset();
7713             }
7714             Roo.callback(o.success, o.scope, [this, action]);
7715             this.fireEvent('actioncomplete', this, action);
7716
7717         }else{
7718
7719             // failure condition..
7720             // we have a scenario where updates need confirming.
7721             // eg. if a locking scenario exists..
7722             // we look for { errors : { needs_confirm : true }} in the response.
7723             if (
7724                 (typeof(action.result) != 'undefined')  &&
7725                 (typeof(action.result.errors) != 'undefined')  &&
7726                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7727            ){
7728                 var _t = this;
7729                 Roo.log("not supported yet");
7730                  /*
7731
7732                 Roo.MessageBox.confirm(
7733                     "Change requires confirmation",
7734                     action.result.errorMsg,
7735                     function(r) {
7736                         if (r != 'yes') {
7737                             return;
7738                         }
7739                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7740                     }
7741
7742                 );
7743                 */
7744
7745
7746                 return;
7747             }
7748
7749             Roo.callback(o.failure, o.scope, [this, action]);
7750             // show an error message if no failed handler is set..
7751             if (!this.hasListener('actionfailed')) {
7752                 Roo.log("need to add dialog support");
7753                 /*
7754                 Roo.MessageBox.alert("Error",
7755                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7756                         action.result.errorMsg :
7757                         "Saving Failed, please check your entries or try again"
7758                 );
7759                 */
7760             }
7761
7762             this.fireEvent('actionfailed', this, action);
7763         }
7764
7765     },
7766     /**
7767      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7768      * @param {String} id The value to search for
7769      * @return Field
7770      */
7771     findField : function(id){
7772         var items = this.getItems();
7773         var field = items.get(id);
7774         if(!field){
7775              items.each(function(f){
7776                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7777                     field = f;
7778                     return false;
7779                 }
7780                 return true;
7781             });
7782         }
7783         return field || null;
7784     },
7785      /**
7786      * Mark fields in this form invalid in bulk.
7787      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7788      * @return {BasicForm} this
7789      */
7790     markInvalid : function(errors){
7791         if(errors instanceof Array){
7792             for(var i = 0, len = errors.length; i < len; i++){
7793                 var fieldError = errors[i];
7794                 var f = this.findField(fieldError.id);
7795                 if(f){
7796                     f.markInvalid(fieldError.msg);
7797                 }
7798             }
7799         }else{
7800             var field, id;
7801             for(id in errors){
7802                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7803                     field.markInvalid(errors[id]);
7804                 }
7805             }
7806         }
7807         //Roo.each(this.childForms || [], function (f) {
7808         //    f.markInvalid(errors);
7809         //});
7810
7811         return this;
7812     },
7813
7814     /**
7815      * Set values for fields in this form in bulk.
7816      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7817      * @return {BasicForm} this
7818      */
7819     setValues : function(values){
7820         if(values instanceof Array){ // array of objects
7821             for(var i = 0, len = values.length; i < len; i++){
7822                 var v = values[i];
7823                 var f = this.findField(v.id);
7824                 if(f){
7825                     f.setValue(v.value);
7826                     if(this.trackResetOnLoad){
7827                         f.originalValue = f.getValue();
7828                     }
7829                 }
7830             }
7831         }else{ // object hash
7832             var field, id;
7833             for(id in values){
7834                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7835
7836                     if (field.setFromData &&
7837                         field.valueField &&
7838                         field.displayField &&
7839                         // combos' with local stores can
7840                         // be queried via setValue()
7841                         // to set their value..
7842                         (field.store && !field.store.isLocal)
7843                         ) {
7844                         // it's a combo
7845                         var sd = { };
7846                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7847                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7848                         field.setFromData(sd);
7849
7850                     } else {
7851                         field.setValue(values[id]);
7852                     }
7853
7854
7855                     if(this.trackResetOnLoad){
7856                         field.originalValue = field.getValue();
7857                     }
7858                 }
7859             }
7860         }
7861
7862         //Roo.each(this.childForms || [], function (f) {
7863         //    f.setValues(values);
7864         //});
7865
7866         return this;
7867     },
7868
7869     /**
7870      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7871      * they are returned as an array.
7872      * @param {Boolean} asString
7873      * @return {Object}
7874      */
7875     getValues : function(asString){
7876         //if (this.childForms) {
7877             // copy values from the child forms
7878         //    Roo.each(this.childForms, function (f) {
7879         //        this.setValues(f.getValues());
7880         //    }, this);
7881         //}
7882
7883
7884
7885         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7886         if(asString === true){
7887             return fs;
7888         }
7889         return Roo.urlDecode(fs);
7890     },
7891
7892     /**
7893      * Returns the fields in this form as an object with key/value pairs.
7894      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7895      * @return {Object}
7896      */
7897     getFieldValues : function(with_hidden)
7898     {
7899         var items = this.getItems();
7900         var ret = {};
7901         items.each(function(f){
7902             if (!f.getName()) {
7903                 return;
7904             }
7905             var v = f.getValue();
7906             if (f.inputType =='radio') {
7907                 if (typeof(ret[f.getName()]) == 'undefined') {
7908                     ret[f.getName()] = ''; // empty..
7909                 }
7910
7911                 if (!f.el.dom.checked) {
7912                     return;
7913
7914                 }
7915                 v = f.el.dom.value;
7916
7917             }
7918
7919             // not sure if this supported any more..
7920             if ((typeof(v) == 'object') && f.getRawValue) {
7921                 v = f.getRawValue() ; // dates..
7922             }
7923             // combo boxes where name != hiddenName...
7924             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7925                 ret[f.name] = f.getRawValue();
7926             }
7927             ret[f.getName()] = v;
7928         });
7929
7930         return ret;
7931     },
7932
7933     /**
7934      * Clears all invalid messages in this form.
7935      * @return {BasicForm} this
7936      */
7937     clearInvalid : function(){
7938         var items = this.getItems();
7939
7940         items.each(function(f){
7941            f.clearInvalid();
7942         });
7943
7944
7945
7946         return this;
7947     },
7948
7949     /**
7950      * Resets this form.
7951      * @return {BasicForm} this
7952      */
7953     reset : function(){
7954         var items = this.getItems();
7955         items.each(function(f){
7956             f.reset();
7957         });
7958
7959         Roo.each(this.childForms || [], function (f) {
7960             f.reset();
7961         });
7962
7963
7964         return this;
7965     },
7966     getItems : function()
7967     {
7968         var r=new Roo.util.MixedCollection(false, function(o){
7969             return o.id || (o.id = Roo.id());
7970         });
7971         var iter = function(el) {
7972             if (el.inputEl) {
7973                 r.add(el);
7974             }
7975             if (!el.items) {
7976                 return;
7977             }
7978             Roo.each(el.items,function(e) {
7979                 iter(e);
7980             });
7981
7982
7983         };
7984
7985         iter(this);
7986         return r;
7987
7988
7989
7990
7991     }
7992
7993 });
7994
7995 Roo.apply(Roo.bootstrap.Form, {
7996     
7997     popover : {
7998         
7999         padding : 5,
8000         
8001         isApplied : false,
8002         
8003         isMasked : false,
8004         
8005         form : false,
8006         
8007         target : false,
8008         
8009         toolTip : false,
8010         
8011         intervalID : false,
8012         
8013         maskEl : false,
8014         
8015         apply : function()
8016         {
8017             if(this.isApplied){
8018                 return;
8019             }
8020             
8021             this.maskEl = {
8022                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8023                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8024                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8025                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8026             };
8027             
8028             this.maskEl.top.enableDisplayMode("block");
8029             this.maskEl.left.enableDisplayMode("block");
8030             this.maskEl.bottom.enableDisplayMode("block");
8031             this.maskEl.right.enableDisplayMode("block");
8032             
8033             this.toolTip = new Roo.bootstrap.Tooltip({
8034                 cls : 'roo-form-error-popover',
8035                 alignment : {
8036                     'left' : ['r-l', [-2,0], 'right'],
8037                     'right' : ['l-r', [2,0], 'left'],
8038                     'bottom' : ['tl-bl', [0,2], 'top'],
8039                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8040                 }
8041             });
8042             
8043             this.toolTip.render(Roo.get(document.body));
8044
8045             this.toolTip.el.enableDisplayMode("block");
8046             
8047             Roo.get(document.body).on('click', function(){
8048                 this.unmask();
8049             }, this);
8050             
8051             this.isApplied = true
8052         },
8053         
8054         mask : function(form, target)
8055         {
8056             this.form = form;
8057             
8058             this.target = target;
8059             
8060             if(!this.form.errorMask || !target.el){
8061                 return;
8062             }
8063             
8064             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8065             
8066             var ot = this.target.el.calcOffsetsTo(scrollable);
8067             
8068             var scrollTo = ot[1] - 100;
8069             
8070             var maxScroll = Roo.lib.Dom.getDocumentHeight() - Roo.lib.Dom.getViewportHeight();
8071             
8072             scrollTo = Math.min(scrollTo, maxScroll);
8073             
8074             scrollable.scrollTo('top', scrollTo);
8075             
8076             var box = this.target.el.getBox();
8077
8078             var zIndex = Roo.bootstrap.Modal.zIndex++;
8079
8080             this.maskEl.top.setStyle('position', 'fixed');
8081             this.maskEl.top.setStyle('z-index', zIndex);
8082             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8083             this.maskEl.top.setXY([0, 0]);
8084             this.maskEl.top.show();
8085
8086             this.maskEl.left.setStyle('position', 'fixed');
8087             this.maskEl.left.setStyle('z-index', zIndex);
8088             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8089             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8090             this.maskEl.left.show();
8091
8092             this.maskEl.bottom.setStyle('position', 'fixed');
8093             this.maskEl.bottom.setStyle('z-index', zIndex);
8094             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8095             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8096             this.maskEl.bottom.show();
8097
8098             this.maskEl.right.setStyle('position', 'fixed');
8099             this.maskEl.right.setStyle('z-index', zIndex);
8100             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8101             this.maskEl.right.setXY([0, box.y - this.padding]);
8102             this.maskEl.right.show();
8103
8104
8105             this.toolTip.bindEl = this.target.el;
8106
8107             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8108
8109             var tip = this.target.blankText;
8110
8111             if(this.target.getValue() !== '' && this.target.regexText.length){
8112                 tip = this.target.regexText;
8113             }
8114
8115             this.toolTip.show(tip);
8116
8117             this.intervalID = window.setInterval(function() {
8118                 Roo.bootstrap.Form.popover.unmask();
8119             }, 10000);
8120
8121             window.onwheel = function(){ return false;};
8122             
8123             (function(){ this.isMasked = true; }).defer(500, this);
8124                 
8125             
8126             
8127         },
8128         
8129         unmask : function()
8130         {
8131             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8132                 return;
8133             }
8134             
8135             this.maskEl.top.setStyle('position', 'absolute');
8136             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8137             this.maskEl.top.hide();
8138
8139             this.maskEl.left.setStyle('position', 'absolute');
8140             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8141             this.maskEl.left.hide();
8142
8143             this.maskEl.bottom.setStyle('position', 'absolute');
8144             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8145             this.maskEl.bottom.hide();
8146
8147             this.maskEl.right.setStyle('position', 'absolute');
8148             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8149             this.maskEl.right.hide();
8150             
8151             this.toolTip.hide();
8152             
8153             this.toolTip.el.hide();
8154             
8155             window.onwheel = function(){ return true;};
8156             
8157             if(this.intervalID){
8158                 window.clearInterval(this.intervalID);
8159                 this.intervalID = false;
8160             }
8161             
8162             this.isMasked = false;
8163             
8164         }
8165         
8166     }
8167     
8168 });
8169
8170 /*
8171  * Based on:
8172  * Ext JS Library 1.1.1
8173  * Copyright(c) 2006-2007, Ext JS, LLC.
8174  *
8175  * Originally Released Under LGPL - original licence link has changed is not relivant.
8176  *
8177  * Fork - LGPL
8178  * <script type="text/javascript">
8179  */
8180 /**
8181  * @class Roo.form.VTypes
8182  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8183  * @singleton
8184  */
8185 Roo.form.VTypes = function(){
8186     // closure these in so they are only created once.
8187     var alpha = /^[a-zA-Z_]+$/;
8188     var alphanum = /^[a-zA-Z0-9_]+$/;
8189     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8190     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8191
8192     // All these messages and functions are configurable
8193     return {
8194         /**
8195          * The function used to validate email addresses
8196          * @param {String} value The email address
8197          */
8198         'email' : function(v){
8199             return email.test(v);
8200         },
8201         /**
8202          * The error text to display when the email validation function returns false
8203          * @type String
8204          */
8205         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8206         /**
8207          * The keystroke filter mask to be applied on email input
8208          * @type RegExp
8209          */
8210         'emailMask' : /[a-z0-9_\.\-@]/i,
8211
8212         /**
8213          * The function used to validate URLs
8214          * @param {String} value The URL
8215          */
8216         'url' : function(v){
8217             return url.test(v);
8218         },
8219         /**
8220          * The error text to display when the url validation function returns false
8221          * @type String
8222          */
8223         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8224         
8225         /**
8226          * The function used to validate alpha values
8227          * @param {String} value The value
8228          */
8229         'alpha' : function(v){
8230             return alpha.test(v);
8231         },
8232         /**
8233          * The error text to display when the alpha validation function returns false
8234          * @type String
8235          */
8236         'alphaText' : 'This field should only contain letters and _',
8237         /**
8238          * The keystroke filter mask to be applied on alpha input
8239          * @type RegExp
8240          */
8241         'alphaMask' : /[a-z_]/i,
8242
8243         /**
8244          * The function used to validate alphanumeric values
8245          * @param {String} value The value
8246          */
8247         'alphanum' : function(v){
8248             return alphanum.test(v);
8249         },
8250         /**
8251          * The error text to display when the alphanumeric validation function returns false
8252          * @type String
8253          */
8254         'alphanumText' : 'This field should only contain letters, numbers and _',
8255         /**
8256          * The keystroke filter mask to be applied on alphanumeric input
8257          * @type RegExp
8258          */
8259         'alphanumMask' : /[a-z0-9_]/i
8260     };
8261 }();/*
8262  * - LGPL
8263  *
8264  * Input
8265  * 
8266  */
8267
8268 /**
8269  * @class Roo.bootstrap.Input
8270  * @extends Roo.bootstrap.Component
8271  * Bootstrap Input class
8272  * @cfg {Boolean} disabled is it disabled
8273  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8274  * @cfg {String} name name of the input
8275  * @cfg {string} fieldLabel - the label associated
8276  * @cfg {string} placeholder - placeholder to put in text.
8277  * @cfg {string}  before - input group add on before
8278  * @cfg {string} after - input group add on after
8279  * @cfg {string} size - (lg|sm) or leave empty..
8280  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8281  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8282  * @cfg {Number} md colspan out of 12 for computer-sized screens
8283  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8284  * @cfg {string} value default value of the input
8285  * @cfg {Number} labelWidth set the width of label 
8286  * @cfg {Number} labellg set the width of label (1-12)
8287  * @cfg {Number} labelmd set the width of label (1-12)
8288  * @cfg {Number} labelsm set the width of label (1-12)
8289  * @cfg {Number} labelxs set the width of label (1-12)
8290  * @cfg {String} labelAlign (top|left)
8291  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8292  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8293  * @cfg {String} indicatorpos (left|right) default left
8294
8295  * @cfg {String} align (left|center|right) Default left
8296  * @cfg {Boolean} forceFeedback (true|false) Default false
8297  * 
8298  * 
8299  * 
8300  * 
8301  * @constructor
8302  * Create a new Input
8303  * @param {Object} config The config object
8304  */
8305
8306 Roo.bootstrap.Input = function(config){
8307     
8308     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8309     
8310     this.addEvents({
8311         /**
8312          * @event focus
8313          * Fires when this field receives input focus.
8314          * @param {Roo.form.Field} this
8315          */
8316         focus : true,
8317         /**
8318          * @event blur
8319          * Fires when this field loses input focus.
8320          * @param {Roo.form.Field} this
8321          */
8322         blur : true,
8323         /**
8324          * @event specialkey
8325          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8326          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8327          * @param {Roo.form.Field} this
8328          * @param {Roo.EventObject} e The event object
8329          */
8330         specialkey : true,
8331         /**
8332          * @event change
8333          * Fires just before the field blurs if the field value has changed.
8334          * @param {Roo.form.Field} this
8335          * @param {Mixed} newValue The new value
8336          * @param {Mixed} oldValue The original value
8337          */
8338         change : true,
8339         /**
8340          * @event invalid
8341          * Fires after the field has been marked as invalid.
8342          * @param {Roo.form.Field} this
8343          * @param {String} msg The validation message
8344          */
8345         invalid : true,
8346         /**
8347          * @event valid
8348          * Fires after the field has been validated with no errors.
8349          * @param {Roo.form.Field} this
8350          */
8351         valid : true,
8352          /**
8353          * @event keyup
8354          * Fires after the key up
8355          * @param {Roo.form.Field} this
8356          * @param {Roo.EventObject}  e The event Object
8357          */
8358         keyup : true
8359     });
8360 };
8361
8362 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8363      /**
8364      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8365       automatic validation (defaults to "keyup").
8366      */
8367     validationEvent : "keyup",
8368      /**
8369      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8370      */
8371     validateOnBlur : true,
8372     /**
8373      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8374      */
8375     validationDelay : 250,
8376      /**
8377      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8378      */
8379     focusClass : "x-form-focus",  // not needed???
8380     
8381        
8382     /**
8383      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8384      */
8385     invalidClass : "has-warning",
8386     
8387     /**
8388      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8389      */
8390     validClass : "has-success",
8391     
8392     /**
8393      * @cfg {Boolean} hasFeedback (true|false) default true
8394      */
8395     hasFeedback : true,
8396     
8397     /**
8398      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8399      */
8400     invalidFeedbackClass : "glyphicon-warning-sign",
8401     
8402     /**
8403      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8404      */
8405     validFeedbackClass : "glyphicon-ok",
8406     
8407     /**
8408      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8409      */
8410     selectOnFocus : false,
8411     
8412      /**
8413      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8414      */
8415     maskRe : null,
8416        /**
8417      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8418      */
8419     vtype : null,
8420     
8421       /**
8422      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8423      */
8424     disableKeyFilter : false,
8425     
8426        /**
8427      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8428      */
8429     disabled : false,
8430      /**
8431      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8432      */
8433     allowBlank : true,
8434     /**
8435      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8436      */
8437     blankText : "Please complete this mandatory field",
8438     
8439      /**
8440      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8441      */
8442     minLength : 0,
8443     /**
8444      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8445      */
8446     maxLength : Number.MAX_VALUE,
8447     /**
8448      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8449      */
8450     minLengthText : "The minimum length for this field is {0}",
8451     /**
8452      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8453      */
8454     maxLengthText : "The maximum length for this field is {0}",
8455   
8456     
8457     /**
8458      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8459      * If available, this function will be called only after the basic validators all return true, and will be passed the
8460      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8461      */
8462     validator : null,
8463     /**
8464      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8465      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8466      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8467      */
8468     regex : null,
8469     /**
8470      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8471      */
8472     regexText : "",
8473     
8474     autocomplete: false,
8475     
8476     
8477     fieldLabel : '',
8478     inputType : 'text',
8479     
8480     name : false,
8481     placeholder: false,
8482     before : false,
8483     after : false,
8484     size : false,
8485     hasFocus : false,
8486     preventMark: false,
8487     isFormField : true,
8488     value : '',
8489     labelWidth : 2,
8490     labelAlign : false,
8491     readOnly : false,
8492     align : false,
8493     formatedValue : false,
8494     forceFeedback : false,
8495     
8496     indicatorpos : 'left',
8497     
8498     labellg : 0,
8499     labelmd : 0,
8500     labelsm : 0,
8501     labelxs : 0,
8502     
8503     parentLabelAlign : function()
8504     {
8505         var parent = this;
8506         while (parent.parent()) {
8507             parent = parent.parent();
8508             if (typeof(parent.labelAlign) !='undefined') {
8509                 return parent.labelAlign;
8510             }
8511         }
8512         return 'left';
8513         
8514     },
8515     
8516     getAutoCreate : function()
8517     {
8518         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8519         
8520         var id = Roo.id();
8521         
8522         var cfg = {};
8523         
8524         if(this.inputType != 'hidden'){
8525             cfg.cls = 'form-group' //input-group
8526         }
8527         
8528         var input =  {
8529             tag: 'input',
8530             id : id,
8531             type : this.inputType,
8532             value : this.value,
8533             cls : 'form-control',
8534             placeholder : this.placeholder || '',
8535             autocomplete : this.autocomplete || 'new-password'
8536         };
8537         
8538         if(this.align){
8539             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8540         }
8541         
8542         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8543             input.maxLength = this.maxLength;
8544         }
8545         
8546         if (this.disabled) {
8547             input.disabled=true;
8548         }
8549         
8550         if (this.readOnly) {
8551             input.readonly=true;
8552         }
8553         
8554         if (this.name) {
8555             input.name = this.name;
8556         }
8557         
8558         if (this.size) {
8559             input.cls += ' input-' + this.size;
8560         }
8561         
8562         var settings=this;
8563         ['xs','sm','md','lg'].map(function(size){
8564             if (settings[size]) {
8565                 cfg.cls += ' col-' + size + '-' + settings[size];
8566             }
8567         });
8568         
8569         var inputblock = input;
8570         
8571         var feedback = {
8572             tag: 'span',
8573             cls: 'glyphicon form-control-feedback'
8574         };
8575             
8576         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8577             
8578             inputblock = {
8579                 cls : 'has-feedback',
8580                 cn :  [
8581                     input,
8582                     feedback
8583                 ] 
8584             };  
8585         }
8586         
8587         if (this.before || this.after) {
8588             
8589             inputblock = {
8590                 cls : 'input-group',
8591                 cn :  [] 
8592             };
8593             
8594             if (this.before && typeof(this.before) == 'string') {
8595                 
8596                 inputblock.cn.push({
8597                     tag :'span',
8598                     cls : 'roo-input-before input-group-addon',
8599                     html : this.before
8600                 });
8601             }
8602             if (this.before && typeof(this.before) == 'object') {
8603                 this.before = Roo.factory(this.before);
8604                 
8605                 inputblock.cn.push({
8606                     tag :'span',
8607                     cls : 'roo-input-before input-group-' +
8608                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8609                 });
8610             }
8611             
8612             inputblock.cn.push(input);
8613             
8614             if (this.after && typeof(this.after) == 'string') {
8615                 inputblock.cn.push({
8616                     tag :'span',
8617                     cls : 'roo-input-after input-group-addon',
8618                     html : this.after
8619                 });
8620             }
8621             if (this.after && typeof(this.after) == 'object') {
8622                 this.after = Roo.factory(this.after);
8623                 
8624                 inputblock.cn.push({
8625                     tag :'span',
8626                     cls : 'roo-input-after input-group-' +
8627                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8628                 });
8629             }
8630             
8631             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8632                 inputblock.cls += ' has-feedback';
8633                 inputblock.cn.push(feedback);
8634             }
8635         };
8636         
8637         if (align ==='left' && this.fieldLabel.length) {
8638             
8639             cfg.cls += ' roo-form-group-label-left';
8640             
8641             cfg.cn = [
8642                 {
8643                     tag : 'i',
8644                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8645                     tooltip : 'This field is required'
8646                 },
8647                 {
8648                     tag: 'label',
8649                     'for' :  id,
8650                     cls : 'control-label',
8651                     html : this.fieldLabel
8652
8653                 },
8654                 {
8655                     cls : "", 
8656                     cn: [
8657                         inputblock
8658                     ]
8659                 }
8660             ];
8661             
8662             var labelCfg = cfg.cn[1];
8663             var contentCfg = cfg.cn[2];
8664             
8665             if(this.indicatorpos == 'right'){
8666                 cfg.cn = [
8667                     {
8668                         tag: 'label',
8669                         'for' :  id,
8670                         cls : 'control-label',
8671                         html : this.fieldLabel
8672
8673                     },
8674                     {
8675                         tag : 'i',
8676                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8677                         tooltip : 'This field is required'
8678                     },
8679                     {
8680                         cls : "",
8681                         cn: [
8682                             inputblock
8683                         ]
8684                     }
8685
8686                 ];
8687                 
8688                 labelCfg = cfg.cn[0];
8689                 contentCfg = cfg.cn[2];
8690             
8691             }
8692             
8693             if(this.labelWidth > 12){
8694                 labelCfg.style = "width: " + this.labelWidth + 'px';
8695             }
8696             
8697             if(this.labelWidth < 13 && this.labelmd == 0){
8698                 this.labelmd = this.labelWidth;
8699             }
8700             
8701             if(this.labellg > 0){
8702                 labelCfg.cls += ' col-lg-' + this.labellg;
8703                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8704             }
8705             
8706             if(this.labelmd > 0){
8707                 labelCfg.cls += ' col-md-' + this.labelmd;
8708                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8709             }
8710             
8711             if(this.labelsm > 0){
8712                 labelCfg.cls += ' col-sm-' + this.labelsm;
8713                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8714             }
8715             
8716             if(this.labelxs > 0){
8717                 labelCfg.cls += ' col-xs-' + this.labelxs;
8718                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8719             }
8720             
8721             
8722         } else if ( this.fieldLabel.length) {
8723                 
8724             cfg.cn = [
8725                 {
8726                     tag : 'i',
8727                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8728                     tooltip : 'This field is required'
8729                 },
8730                 {
8731                     tag: 'label',
8732                    //cls : 'input-group-addon',
8733                     html : this.fieldLabel
8734
8735                 },
8736
8737                inputblock
8738
8739            ];
8740            
8741            if(this.indicatorpos == 'right'){
8742                 
8743                 cfg.cn = [
8744                     {
8745                         tag: 'label',
8746                        //cls : 'input-group-addon',
8747                         html : this.fieldLabel
8748
8749                     },
8750                     {
8751                         tag : 'i',
8752                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8753                         tooltip : 'This field is required'
8754                     },
8755
8756                    inputblock
8757
8758                ];
8759
8760             }
8761
8762         } else {
8763             
8764             cfg.cn = [
8765
8766                     inputblock
8767
8768             ];
8769                 
8770                 
8771         };
8772         
8773         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8774            cfg.cls += ' navbar-form';
8775         }
8776         
8777         if (this.parentType === 'NavGroup') {
8778            cfg.cls += ' navbar-form';
8779            cfg.tag = 'li';
8780         }
8781         
8782         return cfg;
8783         
8784     },
8785     /**
8786      * return the real input element.
8787      */
8788     inputEl: function ()
8789     {
8790         return this.el.select('input.form-control',true).first();
8791     },
8792     
8793     tooltipEl : function()
8794     {
8795         return this.inputEl();
8796     },
8797     
8798     indicatorEl : function()
8799     {
8800         var indicator = this.el.select('i.roo-required-indicator',true).first();
8801         
8802         if(!indicator){
8803             return false;
8804         }
8805         
8806         return indicator;
8807         
8808     },
8809     
8810     setDisabled : function(v)
8811     {
8812         var i  = this.inputEl().dom;
8813         if (!v) {
8814             i.removeAttribute('disabled');
8815             return;
8816             
8817         }
8818         i.setAttribute('disabled','true');
8819     },
8820     initEvents : function()
8821     {
8822           
8823         this.inputEl().on("keydown" , this.fireKey,  this);
8824         this.inputEl().on("focus", this.onFocus,  this);
8825         this.inputEl().on("blur", this.onBlur,  this);
8826         
8827         this.inputEl().relayEvent('keyup', this);
8828         
8829         this.indicator = this.indicatorEl();
8830         
8831         if(this.indicator){
8832             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8833             this.indicator.hide();
8834         }
8835  
8836         // reference to original value for reset
8837         this.originalValue = this.getValue();
8838         //Roo.form.TextField.superclass.initEvents.call(this);
8839         if(this.validationEvent == 'keyup'){
8840             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8841             this.inputEl().on('keyup', this.filterValidation, this);
8842         }
8843         else if(this.validationEvent !== false){
8844             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8845         }
8846         
8847         if(this.selectOnFocus){
8848             this.on("focus", this.preFocus, this);
8849             
8850         }
8851         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8852             this.inputEl().on("keypress", this.filterKeys, this);
8853         } else {
8854             this.inputEl().relayEvent('keypress', this);
8855         }
8856        /* if(this.grow){
8857             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8858             this.el.on("click", this.autoSize,  this);
8859         }
8860         */
8861         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8862             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8863         }
8864         
8865         if (typeof(this.before) == 'object') {
8866             this.before.render(this.el.select('.roo-input-before',true).first());
8867         }
8868         if (typeof(this.after) == 'object') {
8869             this.after.render(this.el.select('.roo-input-after',true).first());
8870         }
8871         
8872         
8873     },
8874     filterValidation : function(e){
8875         if(!e.isNavKeyPress()){
8876             this.validationTask.delay(this.validationDelay);
8877         }
8878     },
8879      /**
8880      * Validates the field value
8881      * @return {Boolean} True if the value is valid, else false
8882      */
8883     validate : function(){
8884         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8885         if(this.disabled || this.validateValue(this.getRawValue())){
8886             this.markValid();
8887             return true;
8888         }
8889         
8890         this.markInvalid();
8891         return false;
8892     },
8893     
8894     
8895     /**
8896      * Validates a value according to the field's validation rules and marks the field as invalid
8897      * if the validation fails
8898      * @param {Mixed} value The value to validate
8899      * @return {Boolean} True if the value is valid, else false
8900      */
8901     validateValue : function(value){
8902         if(value.length < 1)  { // if it's blank
8903             if(this.allowBlank){
8904                 return true;
8905             }
8906             return false;
8907         }
8908         
8909         if(value.length < this.minLength){
8910             return false;
8911         }
8912         if(value.length > this.maxLength){
8913             return false;
8914         }
8915         if(this.vtype){
8916             var vt = Roo.form.VTypes;
8917             if(!vt[this.vtype](value, this)){
8918                 return false;
8919             }
8920         }
8921         if(typeof this.validator == "function"){
8922             var msg = this.validator(value);
8923             if(msg !== true){
8924                 return false;
8925             }
8926         }
8927         
8928         if(this.regex && !this.regex.test(value)){
8929             return false;
8930         }
8931         
8932         return true;
8933     },
8934
8935     
8936     
8937      // private
8938     fireKey : function(e){
8939         //Roo.log('field ' + e.getKey());
8940         if(e.isNavKeyPress()){
8941             this.fireEvent("specialkey", this, e);
8942         }
8943     },
8944     focus : function (selectText){
8945         if(this.rendered){
8946             this.inputEl().focus();
8947             if(selectText === true){
8948                 this.inputEl().dom.select();
8949             }
8950         }
8951         return this;
8952     } ,
8953     
8954     onFocus : function(){
8955         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8956            // this.el.addClass(this.focusClass);
8957         }
8958         if(!this.hasFocus){
8959             this.hasFocus = true;
8960             this.startValue = this.getValue();
8961             this.fireEvent("focus", this);
8962         }
8963     },
8964     
8965     beforeBlur : Roo.emptyFn,
8966
8967     
8968     // private
8969     onBlur : function(){
8970         this.beforeBlur();
8971         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8972             //this.el.removeClass(this.focusClass);
8973         }
8974         this.hasFocus = false;
8975         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8976             this.validate();
8977         }
8978         var v = this.getValue();
8979         if(String(v) !== String(this.startValue)){
8980             this.fireEvent('change', this, v, this.startValue);
8981         }
8982         this.fireEvent("blur", this);
8983     },
8984     
8985     /**
8986      * Resets the current field value to the originally loaded value and clears any validation messages
8987      */
8988     reset : function(){
8989         this.setValue(this.originalValue);
8990         this.validate();
8991     },
8992      /**
8993      * Returns the name of the field
8994      * @return {Mixed} name The name field
8995      */
8996     getName: function(){
8997         return this.name;
8998     },
8999      /**
9000      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9001      * @return {Mixed} value The field value
9002      */
9003     getValue : function(){
9004         
9005         var v = this.inputEl().getValue();
9006         
9007         return v;
9008     },
9009     /**
9010      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9011      * @return {Mixed} value The field value
9012      */
9013     getRawValue : function(){
9014         var v = this.inputEl().getValue();
9015         
9016         return v;
9017     },
9018     
9019     /**
9020      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9021      * @param {Mixed} value The value to set
9022      */
9023     setRawValue : function(v){
9024         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9025     },
9026     
9027     selectText : function(start, end){
9028         var v = this.getRawValue();
9029         if(v.length > 0){
9030             start = start === undefined ? 0 : start;
9031             end = end === undefined ? v.length : end;
9032             var d = this.inputEl().dom;
9033             if(d.setSelectionRange){
9034                 d.setSelectionRange(start, end);
9035             }else if(d.createTextRange){
9036                 var range = d.createTextRange();
9037                 range.moveStart("character", start);
9038                 range.moveEnd("character", v.length-end);
9039                 range.select();
9040             }
9041         }
9042     },
9043     
9044     /**
9045      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9046      * @param {Mixed} value The value to set
9047      */
9048     setValue : function(v){
9049         this.value = v;
9050         if(this.rendered){
9051             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9052             this.validate();
9053         }
9054     },
9055     
9056     /*
9057     processValue : function(value){
9058         if(this.stripCharsRe){
9059             var newValue = value.replace(this.stripCharsRe, '');
9060             if(newValue !== value){
9061                 this.setRawValue(newValue);
9062                 return newValue;
9063             }
9064         }
9065         return value;
9066     },
9067   */
9068     preFocus : function(){
9069         
9070         if(this.selectOnFocus){
9071             this.inputEl().dom.select();
9072         }
9073     },
9074     filterKeys : function(e){
9075         var k = e.getKey();
9076         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9077             return;
9078         }
9079         var c = e.getCharCode(), cc = String.fromCharCode(c);
9080         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9081             return;
9082         }
9083         if(!this.maskRe.test(cc)){
9084             e.stopEvent();
9085         }
9086     },
9087      /**
9088      * Clear any invalid styles/messages for this field
9089      */
9090     clearInvalid : function(){
9091         
9092         if(!this.el || this.preventMark){ // not rendered
9093             return;
9094         }
9095         
9096         /*
9097         if(this.indicator){
9098             this.indicator.hide();
9099         }
9100         */
9101         
9102         this.el.removeClass(this.invalidClass);
9103         
9104         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9105             
9106             var feedback = this.el.select('.form-control-feedback', true).first();
9107             
9108             if(feedback){
9109                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9110             }
9111             
9112         }
9113         
9114         this.fireEvent('valid', this);
9115     },
9116     
9117      /**
9118      * Mark this field as valid
9119      */
9120     markValid : function()
9121     {
9122         if(!this.el  || this.preventMark){ // not rendered...
9123             return;
9124         }
9125         
9126         this.el.removeClass([this.invalidClass, this.validClass]);
9127         
9128         var feedback = this.el.select('.form-control-feedback', true).first();
9129             
9130         if(feedback){
9131             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9132         }
9133
9134         if(this.disabled){
9135             return;
9136         }
9137         
9138         if(this.allowBlank && !this.getRawValue().length){
9139             return;
9140         }
9141         
9142         if(this.indicator){
9143             this.indicator.hide();
9144         }
9145         
9146         this.el.addClass(this.validClass);
9147         
9148         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9149             
9150             var feedback = this.el.select('.form-control-feedback', true).first();
9151             
9152             if(feedback){
9153                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9154                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9155             }
9156             
9157         }
9158         
9159         this.fireEvent('valid', this);
9160     },
9161     
9162      /**
9163      * Mark this field as invalid
9164      * @param {String} msg The validation message
9165      */
9166     markInvalid : function(msg)
9167     {
9168         if(!this.el  || this.preventMark){ // not rendered
9169             return;
9170         }
9171         
9172         this.el.removeClass([this.invalidClass, this.validClass]);
9173         
9174         var feedback = this.el.select('.form-control-feedback', true).first();
9175             
9176         if(feedback){
9177             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9178         }
9179
9180         if(this.disabled){
9181             return;
9182         }
9183         
9184         if(this.allowBlank && !this.getRawValue().length){
9185             return;
9186         }
9187         
9188         if(this.indicator){
9189             this.indicator.show();
9190         }
9191         
9192         this.el.addClass(this.invalidClass);
9193         
9194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9195             
9196             var feedback = this.el.select('.form-control-feedback', true).first();
9197             
9198             if(feedback){
9199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9200                 
9201                 if(this.getValue().length || this.forceFeedback){
9202                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9203                 }
9204                 
9205             }
9206             
9207         }
9208         
9209         this.fireEvent('invalid', this, msg);
9210     },
9211     // private
9212     SafariOnKeyDown : function(event)
9213     {
9214         // this is a workaround for a password hang bug on chrome/ webkit.
9215         if (this.inputEl().dom.type != 'password') {
9216             return;
9217         }
9218         
9219         var isSelectAll = false;
9220         
9221         if(this.inputEl().dom.selectionEnd > 0){
9222             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9223         }
9224         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9225             event.preventDefault();
9226             this.setValue('');
9227             return;
9228         }
9229         
9230         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9231             
9232             event.preventDefault();
9233             // this is very hacky as keydown always get's upper case.
9234             //
9235             var cc = String.fromCharCode(event.getCharCode());
9236             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9237             
9238         }
9239     },
9240     adjustWidth : function(tag, w){
9241         tag = tag.toLowerCase();
9242         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9243             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9244                 if(tag == 'input'){
9245                     return w + 2;
9246                 }
9247                 if(tag == 'textarea'){
9248                     return w-2;
9249                 }
9250             }else if(Roo.isOpera){
9251                 if(tag == 'input'){
9252                     return w + 2;
9253                 }
9254                 if(tag == 'textarea'){
9255                     return w-2;
9256                 }
9257             }
9258         }
9259         return w;
9260     }
9261     
9262 });
9263
9264  
9265 /*
9266  * - LGPL
9267  *
9268  * Input
9269  * 
9270  */
9271
9272 /**
9273  * @class Roo.bootstrap.TextArea
9274  * @extends Roo.bootstrap.Input
9275  * Bootstrap TextArea class
9276  * @cfg {Number} cols Specifies the visible width of a text area
9277  * @cfg {Number} rows Specifies the visible number of lines in a text area
9278  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9279  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9280  * @cfg {string} html text
9281  * 
9282  * @constructor
9283  * Create a new TextArea
9284  * @param {Object} config The config object
9285  */
9286
9287 Roo.bootstrap.TextArea = function(config){
9288     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9289    
9290 };
9291
9292 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9293      
9294     cols : false,
9295     rows : 5,
9296     readOnly : false,
9297     warp : 'soft',
9298     resize : false,
9299     value: false,
9300     html: false,
9301     
9302     getAutoCreate : function(){
9303         
9304         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9305         
9306         var id = Roo.id();
9307         
9308         var cfg = {};
9309         
9310         var input =  {
9311             tag: 'textarea',
9312             id : id,
9313             warp : this.warp,
9314             rows : this.rows,
9315             value : this.value || '',
9316             html: this.html || '',
9317             cls : 'form-control',
9318             placeholder : this.placeholder || '' 
9319             
9320         };
9321         
9322         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9323             input.maxLength = this.maxLength;
9324         }
9325         
9326         if(this.resize){
9327             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9328         }
9329         
9330         if(this.cols){
9331             input.cols = this.cols;
9332         }
9333         
9334         if (this.readOnly) {
9335             input.readonly = true;
9336         }
9337         
9338         if (this.name) {
9339             input.name = this.name;
9340         }
9341         
9342         if (this.size) {
9343             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9344         }
9345         
9346         var settings=this;
9347         ['xs','sm','md','lg'].map(function(size){
9348             if (settings[size]) {
9349                 cfg.cls += ' col-' + size + '-' + settings[size];
9350             }
9351         });
9352         
9353         var inputblock = input;
9354         
9355         if(this.hasFeedback && !this.allowBlank){
9356             
9357             var feedback = {
9358                 tag: 'span',
9359                 cls: 'glyphicon form-control-feedback'
9360             };
9361
9362             inputblock = {
9363                 cls : 'has-feedback',
9364                 cn :  [
9365                     input,
9366                     feedback
9367                 ] 
9368             };  
9369         }
9370         
9371         
9372         if (this.before || this.after) {
9373             
9374             inputblock = {
9375                 cls : 'input-group',
9376                 cn :  [] 
9377             };
9378             if (this.before) {
9379                 inputblock.cn.push({
9380                     tag :'span',
9381                     cls : 'input-group-addon',
9382                     html : this.before
9383                 });
9384             }
9385             
9386             inputblock.cn.push(input);
9387             
9388             if(this.hasFeedback && !this.allowBlank){
9389                 inputblock.cls += ' has-feedback';
9390                 inputblock.cn.push(feedback);
9391             }
9392             
9393             if (this.after) {
9394                 inputblock.cn.push({
9395                     tag :'span',
9396                     cls : 'input-group-addon',
9397                     html : this.after
9398                 });
9399             }
9400             
9401         }
9402         
9403         if (align ==='left' && this.fieldLabel.length) {
9404             cfg.cn = [
9405                 {
9406                     tag: 'label',
9407                     'for' :  id,
9408                     cls : 'control-label',
9409                     html : this.fieldLabel
9410                 },
9411                 {
9412                     cls : "",
9413                     cn: [
9414                         inputblock
9415                     ]
9416                 }
9417
9418             ];
9419             
9420             if(this.labelWidth > 12){
9421                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9422             }
9423
9424             if(this.labelWidth < 13 && this.labelmd == 0){
9425                 this.labelmd = this.labelWidth;
9426             }
9427
9428             if(this.labellg > 0){
9429                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9430                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9431             }
9432
9433             if(this.labelmd > 0){
9434                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9435                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9436             }
9437
9438             if(this.labelsm > 0){
9439                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9440                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9441             }
9442
9443             if(this.labelxs > 0){
9444                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9445                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9446             }
9447             
9448         } else if ( this.fieldLabel.length) {
9449             cfg.cn = [
9450
9451                {
9452                    tag: 'label',
9453                    //cls : 'input-group-addon',
9454                    html : this.fieldLabel
9455
9456                },
9457
9458                inputblock
9459
9460            ];
9461
9462         } else {
9463
9464             cfg.cn = [
9465
9466                 inputblock
9467
9468             ];
9469                 
9470         }
9471         
9472         if (this.disabled) {
9473             input.disabled=true;
9474         }
9475         
9476         return cfg;
9477         
9478     },
9479     /**
9480      * return the real textarea element.
9481      */
9482     inputEl: function ()
9483     {
9484         return this.el.select('textarea.form-control',true).first();
9485     },
9486     
9487     /**
9488      * Clear any invalid styles/messages for this field
9489      */
9490     clearInvalid : function()
9491     {
9492         
9493         if(!this.el || this.preventMark){ // not rendered
9494             return;
9495         }
9496         
9497         var label = this.el.select('label', true).first();
9498         var icon = this.el.select('i.fa-star', true).first();
9499         
9500         if(label && icon){
9501             icon.remove();
9502         }
9503         
9504         this.el.removeClass(this.invalidClass);
9505         
9506         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9507             
9508             var feedback = this.el.select('.form-control-feedback', true).first();
9509             
9510             if(feedback){
9511                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9512             }
9513             
9514         }
9515         
9516         this.fireEvent('valid', this);
9517     },
9518     
9519      /**
9520      * Mark this field as valid
9521      */
9522     markValid : function()
9523     {
9524         if(!this.el  || this.preventMark){ // not rendered
9525             return;
9526         }
9527         
9528         this.el.removeClass([this.invalidClass, this.validClass]);
9529         
9530         var feedback = this.el.select('.form-control-feedback', true).first();
9531             
9532         if(feedback){
9533             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9534         }
9535
9536         if(this.disabled || this.allowBlank){
9537             return;
9538         }
9539         
9540         var label = this.el.select('label', true).first();
9541         var icon = this.el.select('i.fa-star', true).first();
9542         
9543         if(label && icon){
9544             icon.remove();
9545         }
9546         
9547         this.el.addClass(this.validClass);
9548         
9549         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9550             
9551             var feedback = this.el.select('.form-control-feedback', true).first();
9552             
9553             if(feedback){
9554                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9555                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9556             }
9557             
9558         }
9559         
9560         this.fireEvent('valid', this);
9561     },
9562     
9563      /**
9564      * Mark this field as invalid
9565      * @param {String} msg The validation message
9566      */
9567     markInvalid : function(msg)
9568     {
9569         if(!this.el  || this.preventMark){ // not rendered
9570             return;
9571         }
9572         
9573         this.el.removeClass([this.invalidClass, this.validClass]);
9574         
9575         var feedback = this.el.select('.form-control-feedback', true).first();
9576             
9577         if(feedback){
9578             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9579         }
9580
9581         if(this.disabled || this.allowBlank){
9582             return;
9583         }
9584         
9585         var label = this.el.select('label', true).first();
9586         var icon = this.el.select('i.fa-star', true).first();
9587         
9588         if(!this.getValue().length && label && !icon){
9589             this.el.createChild({
9590                 tag : 'i',
9591                 cls : 'text-danger fa fa-lg fa-star',
9592                 tooltip : 'This field is required',
9593                 style : 'margin-right:5px;'
9594             }, label, true);
9595         }
9596
9597         this.el.addClass(this.invalidClass);
9598         
9599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9600             
9601             var feedback = this.el.select('.form-control-feedback', true).first();
9602             
9603             if(feedback){
9604                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605                 
9606                 if(this.getValue().length || this.forceFeedback){
9607                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9608                 }
9609                 
9610             }
9611             
9612         }
9613         
9614         this.fireEvent('invalid', this, msg);
9615     }
9616 });
9617
9618  
9619 /*
9620  * - LGPL
9621  *
9622  * trigger field - base class for combo..
9623  * 
9624  */
9625  
9626 /**
9627  * @class Roo.bootstrap.TriggerField
9628  * @extends Roo.bootstrap.Input
9629  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9630  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9631  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9632  * for which you can provide a custom implementation.  For example:
9633  * <pre><code>
9634 var trigger = new Roo.bootstrap.TriggerField();
9635 trigger.onTriggerClick = myTriggerFn;
9636 trigger.applyTo('my-field');
9637 </code></pre>
9638  *
9639  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9640  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9641  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9642  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9643  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9644
9645  * @constructor
9646  * Create a new TriggerField.
9647  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9648  * to the base TextField)
9649  */
9650 Roo.bootstrap.TriggerField = function(config){
9651     this.mimicing = false;
9652     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9653 };
9654
9655 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9656     /**
9657      * @cfg {String} triggerClass A CSS class to apply to the trigger
9658      */
9659      /**
9660      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9661      */
9662     hideTrigger:false,
9663
9664     /**
9665      * @cfg {Boolean} removable (true|false) special filter default false
9666      */
9667     removable : false,
9668     
9669     /** @cfg {Boolean} grow @hide */
9670     /** @cfg {Number} growMin @hide */
9671     /** @cfg {Number} growMax @hide */
9672
9673     /**
9674      * @hide 
9675      * @method
9676      */
9677     autoSize: Roo.emptyFn,
9678     // private
9679     monitorTab : true,
9680     // private
9681     deferHeight : true,
9682
9683     
9684     actionMode : 'wrap',
9685     
9686     caret : false,
9687     
9688     
9689     getAutoCreate : function(){
9690        
9691         var align = this.labelAlign || this.parentLabelAlign();
9692         
9693         var id = Roo.id();
9694         
9695         var cfg = {
9696             cls: 'form-group' //input-group
9697         };
9698         
9699         
9700         var input =  {
9701             tag: 'input',
9702             id : id,
9703             type : this.inputType,
9704             cls : 'form-control',
9705             autocomplete: 'new-password',
9706             placeholder : this.placeholder || '' 
9707             
9708         };
9709         if (this.name) {
9710             input.name = this.name;
9711         }
9712         if (this.size) {
9713             input.cls += ' input-' + this.size;
9714         }
9715         
9716         if (this.disabled) {
9717             input.disabled=true;
9718         }
9719         
9720         var inputblock = input;
9721         
9722         if(this.hasFeedback && !this.allowBlank){
9723             
9724             var feedback = {
9725                 tag: 'span',
9726                 cls: 'glyphicon form-control-feedback'
9727             };
9728             
9729             if(this.removable && !this.editable && !this.tickable){
9730                 inputblock = {
9731                     cls : 'has-feedback',
9732                     cn :  [
9733                         inputblock,
9734                         {
9735                             tag: 'button',
9736                             html : 'x',
9737                             cls : 'roo-combo-removable-btn close'
9738                         },
9739                         feedback
9740                     ] 
9741                 };
9742             } else {
9743                 inputblock = {
9744                     cls : 'has-feedback',
9745                     cn :  [
9746                         inputblock,
9747                         feedback
9748                     ] 
9749                 };
9750             }
9751
9752         } else {
9753             if(this.removable && !this.editable && !this.tickable){
9754                 inputblock = {
9755                     cls : 'roo-removable',
9756                     cn :  [
9757                         inputblock,
9758                         {
9759                             tag: 'button',
9760                             html : 'x',
9761                             cls : 'roo-combo-removable-btn close'
9762                         }
9763                     ] 
9764                 };
9765             }
9766         }
9767         
9768         if (this.before || this.after) {
9769             
9770             inputblock = {
9771                 cls : 'input-group',
9772                 cn :  [] 
9773             };
9774             if (this.before) {
9775                 inputblock.cn.push({
9776                     tag :'span',
9777                     cls : 'input-group-addon',
9778                     html : this.before
9779                 });
9780             }
9781             
9782             inputblock.cn.push(input);
9783             
9784             if(this.hasFeedback && !this.allowBlank){
9785                 inputblock.cls += ' has-feedback';
9786                 inputblock.cn.push(feedback);
9787             }
9788             
9789             if (this.after) {
9790                 inputblock.cn.push({
9791                     tag :'span',
9792                     cls : 'input-group-addon',
9793                     html : this.after
9794                 });
9795             }
9796             
9797         };
9798         
9799         var box = {
9800             tag: 'div',
9801             cn: [
9802                 {
9803                     tag: 'input',
9804                     type : 'hidden',
9805                     cls: 'form-hidden-field'
9806                 },
9807                 inputblock
9808             ]
9809             
9810         };
9811         
9812         if(this.multiple){
9813             box = {
9814                 tag: 'div',
9815                 cn: [
9816                     {
9817                         tag: 'input',
9818                         type : 'hidden',
9819                         cls: 'form-hidden-field'
9820                     },
9821                     {
9822                         tag: 'ul',
9823                         cls: 'roo-select2-choices',
9824                         cn:[
9825                             {
9826                                 tag: 'li',
9827                                 cls: 'roo-select2-search-field',
9828                                 cn: [
9829
9830                                     inputblock
9831                                 ]
9832                             }
9833                         ]
9834                     }
9835                 ]
9836             }
9837         };
9838         
9839         var combobox = {
9840             cls: 'roo-select2-container input-group',
9841             cn: [
9842                 box
9843 //                {
9844 //                    tag: 'ul',
9845 //                    cls: 'typeahead typeahead-long dropdown-menu',
9846 //                    style: 'display:none'
9847 //                }
9848             ]
9849         };
9850         
9851         if(!this.multiple && this.showToggleBtn){
9852             
9853             var caret = {
9854                         tag: 'span',
9855                         cls: 'caret'
9856              };
9857             if (this.caret != false) {
9858                 caret = {
9859                      tag: 'i',
9860                      cls: 'fa fa-' + this.caret
9861                 };
9862                 
9863             }
9864             
9865             combobox.cn.push({
9866                 tag :'span',
9867                 cls : 'input-group-addon btn dropdown-toggle',
9868                 cn : [
9869                     caret,
9870                     {
9871                         tag: 'span',
9872                         cls: 'combobox-clear',
9873                         cn  : [
9874                             {
9875                                 tag : 'i',
9876                                 cls: 'icon-remove'
9877                             }
9878                         ]
9879                     }
9880                 ]
9881
9882             })
9883         }
9884         
9885         if(this.multiple){
9886             combobox.cls += ' roo-select2-container-multi';
9887         }
9888         
9889         if (align ==='left' && this.fieldLabel.length) {
9890             
9891             cfg.cls += ' roo-form-group-label-left';
9892
9893             cfg.cn = [
9894                 {
9895                     tag : 'i',
9896                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9897                     tooltip : 'This field is required'
9898                 },
9899                 {
9900                     tag: 'label',
9901                     'for' :  id,
9902                     cls : 'control-label',
9903                     html : this.fieldLabel
9904
9905                 },
9906                 {
9907                     cls : "", 
9908                     cn: [
9909                         combobox
9910                     ]
9911                 }
9912
9913             ];
9914             
9915             var labelCfg = cfg.cn[1];
9916             var contentCfg = cfg.cn[2];
9917             
9918             if(this.indicatorpos == 'right'){
9919                 cfg.cn = [
9920                     {
9921                         tag: 'label',
9922                         'for' :  id,
9923                         cls : 'control-label',
9924                         cn : [
9925                             {
9926                                 tag : 'span',
9927                                 html : this.fieldLabel
9928                             },
9929                             {
9930                                 tag : 'i',
9931                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9932                                 tooltip : 'This field is required'
9933                             }
9934                         ]
9935                     },
9936                     {
9937                         cls : "", 
9938                         cn: [
9939                             combobox
9940                         ]
9941                     }
9942
9943                 ];
9944                 
9945                 labelCfg = cfg.cn[0];
9946                 contentCfg = cfg.cn[1];
9947             }
9948             
9949             if(this.labelWidth > 12){
9950                 labelCfg.style = "width: " + this.labelWidth + 'px';
9951             }
9952             
9953             if(this.labelWidth < 13 && this.labelmd == 0){
9954                 this.labelmd = this.labelWidth;
9955             }
9956             
9957             if(this.labellg > 0){
9958                 labelCfg.cls += ' col-lg-' + this.labellg;
9959                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9960             }
9961             
9962             if(this.labelmd > 0){
9963                 labelCfg.cls += ' col-md-' + this.labelmd;
9964                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9965             }
9966             
9967             if(this.labelsm > 0){
9968                 labelCfg.cls += ' col-sm-' + this.labelsm;
9969                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9970             }
9971             
9972             if(this.labelxs > 0){
9973                 labelCfg.cls += ' col-xs-' + this.labelxs;
9974                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9975             }
9976             
9977         } else if ( this.fieldLabel.length) {
9978 //                Roo.log(" label");
9979             cfg.cn = [
9980                 {
9981                    tag : 'i',
9982                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9983                    tooltip : 'This field is required'
9984                },
9985                {
9986                    tag: 'label',
9987                    //cls : 'input-group-addon',
9988                    html : this.fieldLabel
9989
9990                },
9991
9992                combobox
9993
9994             ];
9995             
9996             if(this.indicatorpos == 'right'){
9997                 
9998                 cfg.cn = [
9999                     {
10000                        tag: 'label',
10001                        cn : [
10002                            {
10003                                tag : 'span',
10004                                html : this.fieldLabel
10005                            },
10006                            {
10007                               tag : 'i',
10008                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10009                               tooltip : 'This field is required'
10010                            }
10011                        ]
10012
10013                     },
10014                     combobox
10015
10016                 ];
10017
10018             }
10019
10020         } else {
10021             
10022 //                Roo.log(" no label && no align");
10023                 cfg = combobox
10024                      
10025                 
10026         }
10027         
10028         var settings=this;
10029         ['xs','sm','md','lg'].map(function(size){
10030             if (settings[size]) {
10031                 cfg.cls += ' col-' + size + '-' + settings[size];
10032             }
10033         });
10034         
10035         return cfg;
10036         
10037     },
10038     
10039     
10040     
10041     // private
10042     onResize : function(w, h){
10043 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10044 //        if(typeof w == 'number'){
10045 //            var x = w - this.trigger.getWidth();
10046 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10047 //            this.trigger.setStyle('left', x+'px');
10048 //        }
10049     },
10050
10051     // private
10052     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10053
10054     // private
10055     getResizeEl : function(){
10056         return this.inputEl();
10057     },
10058
10059     // private
10060     getPositionEl : function(){
10061         return this.inputEl();
10062     },
10063
10064     // private
10065     alignErrorIcon : function(){
10066         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10067     },
10068
10069     // private
10070     initEvents : function(){
10071         
10072         this.createList();
10073         
10074         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10075         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10076         if(!this.multiple && this.showToggleBtn){
10077             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10078             if(this.hideTrigger){
10079                 this.trigger.setDisplayed(false);
10080             }
10081             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10082         }
10083         
10084         if(this.multiple){
10085             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10086         }
10087         
10088         if(this.removable && !this.editable && !this.tickable){
10089             var close = this.closeTriggerEl();
10090             
10091             if(close){
10092                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10093                 close.on('click', this.removeBtnClick, this, close);
10094             }
10095         }
10096         
10097         //this.trigger.addClassOnOver('x-form-trigger-over');
10098         //this.trigger.addClassOnClick('x-form-trigger-click');
10099         
10100         //if(!this.width){
10101         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10102         //}
10103     },
10104     
10105     closeTriggerEl : function()
10106     {
10107         var close = this.el.select('.roo-combo-removable-btn', true).first();
10108         return close ? close : false;
10109     },
10110     
10111     removeBtnClick : function(e, h, el)
10112     {
10113         e.preventDefault();
10114         
10115         if(this.fireEvent("remove", this) !== false){
10116             this.reset();
10117             this.fireEvent("afterremove", this)
10118         }
10119     },
10120     
10121     createList : function()
10122     {
10123         this.list = Roo.get(document.body).createChild({
10124             tag: 'ul',
10125             cls: 'typeahead typeahead-long dropdown-menu',
10126             style: 'display:none'
10127         });
10128         
10129         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10130         
10131     },
10132
10133     // private
10134     initTrigger : function(){
10135        
10136     },
10137
10138     // private
10139     onDestroy : function(){
10140         if(this.trigger){
10141             this.trigger.removeAllListeners();
10142           //  this.trigger.remove();
10143         }
10144         //if(this.wrap){
10145         //    this.wrap.remove();
10146         //}
10147         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10148     },
10149
10150     // private
10151     onFocus : function(){
10152         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10153         /*
10154         if(!this.mimicing){
10155             this.wrap.addClass('x-trigger-wrap-focus');
10156             this.mimicing = true;
10157             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10158             if(this.monitorTab){
10159                 this.el.on("keydown", this.checkTab, this);
10160             }
10161         }
10162         */
10163     },
10164
10165     // private
10166     checkTab : function(e){
10167         if(e.getKey() == e.TAB){
10168             this.triggerBlur();
10169         }
10170     },
10171
10172     // private
10173     onBlur : function(){
10174         // do nothing
10175     },
10176
10177     // private
10178     mimicBlur : function(e, t){
10179         /*
10180         if(!this.wrap.contains(t) && this.validateBlur()){
10181             this.triggerBlur();
10182         }
10183         */
10184     },
10185
10186     // private
10187     triggerBlur : function(){
10188         this.mimicing = false;
10189         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10190         if(this.monitorTab){
10191             this.el.un("keydown", this.checkTab, this);
10192         }
10193         //this.wrap.removeClass('x-trigger-wrap-focus');
10194         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10195     },
10196
10197     // private
10198     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10199     validateBlur : function(e, t){
10200         return true;
10201     },
10202
10203     // private
10204     onDisable : function(){
10205         this.inputEl().dom.disabled = true;
10206         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10207         //if(this.wrap){
10208         //    this.wrap.addClass('x-item-disabled');
10209         //}
10210     },
10211
10212     // private
10213     onEnable : function(){
10214         this.inputEl().dom.disabled = false;
10215         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10216         //if(this.wrap){
10217         //    this.el.removeClass('x-item-disabled');
10218         //}
10219     },
10220
10221     // private
10222     onShow : function(){
10223         var ae = this.getActionEl();
10224         
10225         if(ae){
10226             ae.dom.style.display = '';
10227             ae.dom.style.visibility = 'visible';
10228         }
10229     },
10230
10231     // private
10232     
10233     onHide : function(){
10234         var ae = this.getActionEl();
10235         ae.dom.style.display = 'none';
10236     },
10237
10238     /**
10239      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10240      * by an implementing function.
10241      * @method
10242      * @param {EventObject} e
10243      */
10244     onTriggerClick : Roo.emptyFn
10245 });
10246  /*
10247  * Based on:
10248  * Ext JS Library 1.1.1
10249  * Copyright(c) 2006-2007, Ext JS, LLC.
10250  *
10251  * Originally Released Under LGPL - original licence link has changed is not relivant.
10252  *
10253  * Fork - LGPL
10254  * <script type="text/javascript">
10255  */
10256
10257
10258 /**
10259  * @class Roo.data.SortTypes
10260  * @singleton
10261  * Defines the default sorting (casting?) comparison functions used when sorting data.
10262  */
10263 Roo.data.SortTypes = {
10264     /**
10265      * Default sort that does nothing
10266      * @param {Mixed} s The value being converted
10267      * @return {Mixed} The comparison value
10268      */
10269     none : function(s){
10270         return s;
10271     },
10272     
10273     /**
10274      * The regular expression used to strip tags
10275      * @type {RegExp}
10276      * @property
10277      */
10278     stripTagsRE : /<\/?[^>]+>/gi,
10279     
10280     /**
10281      * Strips all HTML tags to sort on text only
10282      * @param {Mixed} s The value being converted
10283      * @return {String} The comparison value
10284      */
10285     asText : function(s){
10286         return String(s).replace(this.stripTagsRE, "");
10287     },
10288     
10289     /**
10290      * Strips all HTML tags to sort on text only - Case insensitive
10291      * @param {Mixed} s The value being converted
10292      * @return {String} The comparison value
10293      */
10294     asUCText : function(s){
10295         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10296     },
10297     
10298     /**
10299      * Case insensitive string
10300      * @param {Mixed} s The value being converted
10301      * @return {String} The comparison value
10302      */
10303     asUCString : function(s) {
10304         return String(s).toUpperCase();
10305     },
10306     
10307     /**
10308      * Date sorting
10309      * @param {Mixed} s The value being converted
10310      * @return {Number} The comparison value
10311      */
10312     asDate : function(s) {
10313         if(!s){
10314             return 0;
10315         }
10316         if(s instanceof Date){
10317             return s.getTime();
10318         }
10319         return Date.parse(String(s));
10320     },
10321     
10322     /**
10323      * Float sorting
10324      * @param {Mixed} s The value being converted
10325      * @return {Float} The comparison value
10326      */
10327     asFloat : function(s) {
10328         var val = parseFloat(String(s).replace(/,/g, ""));
10329         if(isNaN(val)) {
10330             val = 0;
10331         }
10332         return val;
10333     },
10334     
10335     /**
10336      * Integer sorting
10337      * @param {Mixed} s The value being converted
10338      * @return {Number} The comparison value
10339      */
10340     asInt : function(s) {
10341         var val = parseInt(String(s).replace(/,/g, ""));
10342         if(isNaN(val)) {
10343             val = 0;
10344         }
10345         return val;
10346     }
10347 };/*
10348  * Based on:
10349  * Ext JS Library 1.1.1
10350  * Copyright(c) 2006-2007, Ext JS, LLC.
10351  *
10352  * Originally Released Under LGPL - original licence link has changed is not relivant.
10353  *
10354  * Fork - LGPL
10355  * <script type="text/javascript">
10356  */
10357
10358 /**
10359 * @class Roo.data.Record
10360  * Instances of this class encapsulate both record <em>definition</em> information, and record
10361  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10362  * to access Records cached in an {@link Roo.data.Store} object.<br>
10363  * <p>
10364  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10365  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10366  * objects.<br>
10367  * <p>
10368  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10369  * @constructor
10370  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10371  * {@link #create}. The parameters are the same.
10372  * @param {Array} data An associative Array of data values keyed by the field name.
10373  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10374  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10375  * not specified an integer id is generated.
10376  */
10377 Roo.data.Record = function(data, id){
10378     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10379     this.data = data;
10380 };
10381
10382 /**
10383  * Generate a constructor for a specific record layout.
10384  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10385  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10386  * Each field definition object may contain the following properties: <ul>
10387  * <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,
10388  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10389  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10390  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10391  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10392  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10393  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10394  * this may be omitted.</p></li>
10395  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10396  * <ul><li>auto (Default, implies no conversion)</li>
10397  * <li>string</li>
10398  * <li>int</li>
10399  * <li>float</li>
10400  * <li>boolean</li>
10401  * <li>date</li></ul></p></li>
10402  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10403  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10404  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10405  * by the Reader into an object that will be stored in the Record. It is passed the
10406  * following parameters:<ul>
10407  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10408  * </ul></p></li>
10409  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10410  * </ul>
10411  * <br>usage:<br><pre><code>
10412 var TopicRecord = Roo.data.Record.create(
10413     {name: 'title', mapping: 'topic_title'},
10414     {name: 'author', mapping: 'username'},
10415     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10416     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10417     {name: 'lastPoster', mapping: 'user2'},
10418     {name: 'excerpt', mapping: 'post_text'}
10419 );
10420
10421 var myNewRecord = new TopicRecord({
10422     title: 'Do my job please',
10423     author: 'noobie',
10424     totalPosts: 1,
10425     lastPost: new Date(),
10426     lastPoster: 'Animal',
10427     excerpt: 'No way dude!'
10428 });
10429 myStore.add(myNewRecord);
10430 </code></pre>
10431  * @method create
10432  * @static
10433  */
10434 Roo.data.Record.create = function(o){
10435     var f = function(){
10436         f.superclass.constructor.apply(this, arguments);
10437     };
10438     Roo.extend(f, Roo.data.Record);
10439     var p = f.prototype;
10440     p.fields = new Roo.util.MixedCollection(false, function(field){
10441         return field.name;
10442     });
10443     for(var i = 0, len = o.length; i < len; i++){
10444         p.fields.add(new Roo.data.Field(o[i]));
10445     }
10446     f.getField = function(name){
10447         return p.fields.get(name);  
10448     };
10449     return f;
10450 };
10451
10452 Roo.data.Record.AUTO_ID = 1000;
10453 Roo.data.Record.EDIT = 'edit';
10454 Roo.data.Record.REJECT = 'reject';
10455 Roo.data.Record.COMMIT = 'commit';
10456
10457 Roo.data.Record.prototype = {
10458     /**
10459      * Readonly flag - true if this record has been modified.
10460      * @type Boolean
10461      */
10462     dirty : false,
10463     editing : false,
10464     error: null,
10465     modified: null,
10466
10467     // private
10468     join : function(store){
10469         this.store = store;
10470     },
10471
10472     /**
10473      * Set the named field to the specified value.
10474      * @param {String} name The name of the field to set.
10475      * @param {Object} value The value to set the field to.
10476      */
10477     set : function(name, value){
10478         if(this.data[name] == value){
10479             return;
10480         }
10481         this.dirty = true;
10482         if(!this.modified){
10483             this.modified = {};
10484         }
10485         if(typeof this.modified[name] == 'undefined'){
10486             this.modified[name] = this.data[name];
10487         }
10488         this.data[name] = value;
10489         if(!this.editing && this.store){
10490             this.store.afterEdit(this);
10491         }       
10492     },
10493
10494     /**
10495      * Get the value of the named field.
10496      * @param {String} name The name of the field to get the value of.
10497      * @return {Object} The value of the field.
10498      */
10499     get : function(name){
10500         return this.data[name]; 
10501     },
10502
10503     // private
10504     beginEdit : function(){
10505         this.editing = true;
10506         this.modified = {}; 
10507     },
10508
10509     // private
10510     cancelEdit : function(){
10511         this.editing = false;
10512         delete this.modified;
10513     },
10514
10515     // private
10516     endEdit : function(){
10517         this.editing = false;
10518         if(this.dirty && this.store){
10519             this.store.afterEdit(this);
10520         }
10521     },
10522
10523     /**
10524      * Usually called by the {@link Roo.data.Store} which owns the Record.
10525      * Rejects all changes made to the Record since either creation, or the last commit operation.
10526      * Modified fields are reverted to their original values.
10527      * <p>
10528      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10529      * of reject operations.
10530      */
10531     reject : function(){
10532         var m = this.modified;
10533         for(var n in m){
10534             if(typeof m[n] != "function"){
10535                 this.data[n] = m[n];
10536             }
10537         }
10538         this.dirty = false;
10539         delete this.modified;
10540         this.editing = false;
10541         if(this.store){
10542             this.store.afterReject(this);
10543         }
10544     },
10545
10546     /**
10547      * Usually called by the {@link Roo.data.Store} which owns the Record.
10548      * Commits all changes made to the Record since either creation, or the last commit operation.
10549      * <p>
10550      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10551      * of commit operations.
10552      */
10553     commit : function(){
10554         this.dirty = false;
10555         delete this.modified;
10556         this.editing = false;
10557         if(this.store){
10558             this.store.afterCommit(this);
10559         }
10560     },
10561
10562     // private
10563     hasError : function(){
10564         return this.error != null;
10565     },
10566
10567     // private
10568     clearError : function(){
10569         this.error = null;
10570     },
10571
10572     /**
10573      * Creates a copy of this record.
10574      * @param {String} id (optional) A new record id if you don't want to use this record's id
10575      * @return {Record}
10576      */
10577     copy : function(newId) {
10578         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10579     }
10580 };/*
10581  * Based on:
10582  * Ext JS Library 1.1.1
10583  * Copyright(c) 2006-2007, Ext JS, LLC.
10584  *
10585  * Originally Released Under LGPL - original licence link has changed is not relivant.
10586  *
10587  * Fork - LGPL
10588  * <script type="text/javascript">
10589  */
10590
10591
10592
10593 /**
10594  * @class Roo.data.Store
10595  * @extends Roo.util.Observable
10596  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10597  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10598  * <p>
10599  * 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
10600  * has no knowledge of the format of the data returned by the Proxy.<br>
10601  * <p>
10602  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10603  * instances from the data object. These records are cached and made available through accessor functions.
10604  * @constructor
10605  * Creates a new Store.
10606  * @param {Object} config A config object containing the objects needed for the Store to access data,
10607  * and read the data into Records.
10608  */
10609 Roo.data.Store = function(config){
10610     this.data = new Roo.util.MixedCollection(false);
10611     this.data.getKey = function(o){
10612         return o.id;
10613     };
10614     this.baseParams = {};
10615     // private
10616     this.paramNames = {
10617         "start" : "start",
10618         "limit" : "limit",
10619         "sort" : "sort",
10620         "dir" : "dir",
10621         "multisort" : "_multisort"
10622     };
10623
10624     if(config && config.data){
10625         this.inlineData = config.data;
10626         delete config.data;
10627     }
10628
10629     Roo.apply(this, config);
10630     
10631     if(this.reader){ // reader passed
10632         this.reader = Roo.factory(this.reader, Roo.data);
10633         this.reader.xmodule = this.xmodule || false;
10634         if(!this.recordType){
10635             this.recordType = this.reader.recordType;
10636         }
10637         if(this.reader.onMetaChange){
10638             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10639         }
10640     }
10641
10642     if(this.recordType){
10643         this.fields = this.recordType.prototype.fields;
10644     }
10645     this.modified = [];
10646
10647     this.addEvents({
10648         /**
10649          * @event datachanged
10650          * Fires when the data cache has changed, and a widget which is using this Store
10651          * as a Record cache should refresh its view.
10652          * @param {Store} this
10653          */
10654         datachanged : true,
10655         /**
10656          * @event metachange
10657          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10658          * @param {Store} this
10659          * @param {Object} meta The JSON metadata
10660          */
10661         metachange : true,
10662         /**
10663          * @event add
10664          * Fires when Records have been added to the Store
10665          * @param {Store} this
10666          * @param {Roo.data.Record[]} records The array of Records added
10667          * @param {Number} index The index at which the record(s) were added
10668          */
10669         add : true,
10670         /**
10671          * @event remove
10672          * Fires when a Record has been removed from the Store
10673          * @param {Store} this
10674          * @param {Roo.data.Record} record The Record that was removed
10675          * @param {Number} index The index at which the record was removed
10676          */
10677         remove : true,
10678         /**
10679          * @event update
10680          * Fires when a Record has been updated
10681          * @param {Store} this
10682          * @param {Roo.data.Record} record The Record that was updated
10683          * @param {String} operation The update operation being performed.  Value may be one of:
10684          * <pre><code>
10685  Roo.data.Record.EDIT
10686  Roo.data.Record.REJECT
10687  Roo.data.Record.COMMIT
10688          * </code></pre>
10689          */
10690         update : true,
10691         /**
10692          * @event clear
10693          * Fires when the data cache has been cleared.
10694          * @param {Store} this
10695          */
10696         clear : true,
10697         /**
10698          * @event beforeload
10699          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10700          * the load action will be canceled.
10701          * @param {Store} this
10702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10703          */
10704         beforeload : true,
10705         /**
10706          * @event beforeloadadd
10707          * Fires after a new set of Records has been loaded.
10708          * @param {Store} this
10709          * @param {Roo.data.Record[]} records The Records that were loaded
10710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10711          */
10712         beforeloadadd : true,
10713         /**
10714          * @event load
10715          * Fires after a new set of Records has been loaded, before they are added to the store.
10716          * @param {Store} this
10717          * @param {Roo.data.Record[]} records The Records that were loaded
10718          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10719          * @params {Object} return from reader
10720          */
10721         load : true,
10722         /**
10723          * @event loadexception
10724          * Fires if an exception occurs in the Proxy during loading.
10725          * Called with the signature of the Proxy's "loadexception" event.
10726          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10727          * 
10728          * @param {Proxy} 
10729          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10730          * @param {Object} load options 
10731          * @param {Object} jsonData from your request (normally this contains the Exception)
10732          */
10733         loadexception : true
10734     });
10735     
10736     if(this.proxy){
10737         this.proxy = Roo.factory(this.proxy, Roo.data);
10738         this.proxy.xmodule = this.xmodule || false;
10739         this.relayEvents(this.proxy,  ["loadexception"]);
10740     }
10741     this.sortToggle = {};
10742     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10743
10744     Roo.data.Store.superclass.constructor.call(this);
10745
10746     if(this.inlineData){
10747         this.loadData(this.inlineData);
10748         delete this.inlineData;
10749     }
10750 };
10751
10752 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10753      /**
10754     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10755     * without a remote query - used by combo/forms at present.
10756     */
10757     
10758     /**
10759     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10760     */
10761     /**
10762     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10763     */
10764     /**
10765     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10766     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10767     */
10768     /**
10769     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10770     * on any HTTP request
10771     */
10772     /**
10773     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10774     */
10775     /**
10776     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10777     */
10778     multiSort: false,
10779     /**
10780     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10781     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10782     */
10783     remoteSort : false,
10784
10785     /**
10786     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10787      * loaded or when a record is removed. (defaults to false).
10788     */
10789     pruneModifiedRecords : false,
10790
10791     // private
10792     lastOptions : null,
10793
10794     /**
10795      * Add Records to the Store and fires the add event.
10796      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10797      */
10798     add : function(records){
10799         records = [].concat(records);
10800         for(var i = 0, len = records.length; i < len; i++){
10801             records[i].join(this);
10802         }
10803         var index = this.data.length;
10804         this.data.addAll(records);
10805         this.fireEvent("add", this, records, index);
10806     },
10807
10808     /**
10809      * Remove a Record from the Store and fires the remove event.
10810      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10811      */
10812     remove : function(record){
10813         var index = this.data.indexOf(record);
10814         this.data.removeAt(index);
10815         if(this.pruneModifiedRecords){
10816             this.modified.remove(record);
10817         }
10818         this.fireEvent("remove", this, record, index);
10819     },
10820
10821     /**
10822      * Remove all Records from the Store and fires the clear event.
10823      */
10824     removeAll : function(){
10825         this.data.clear();
10826         if(this.pruneModifiedRecords){
10827             this.modified = [];
10828         }
10829         this.fireEvent("clear", this);
10830     },
10831
10832     /**
10833      * Inserts Records to the Store at the given index and fires the add event.
10834      * @param {Number} index The start index at which to insert the passed Records.
10835      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10836      */
10837     insert : function(index, records){
10838         records = [].concat(records);
10839         for(var i = 0, len = records.length; i < len; i++){
10840             this.data.insert(index, records[i]);
10841             records[i].join(this);
10842         }
10843         this.fireEvent("add", this, records, index);
10844     },
10845
10846     /**
10847      * Get the index within the cache of the passed Record.
10848      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10849      * @return {Number} The index of the passed Record. Returns -1 if not found.
10850      */
10851     indexOf : function(record){
10852         return this.data.indexOf(record);
10853     },
10854
10855     /**
10856      * Get the index within the cache of the Record with the passed id.
10857      * @param {String} id The id of the Record to find.
10858      * @return {Number} The index of the Record. Returns -1 if not found.
10859      */
10860     indexOfId : function(id){
10861         return this.data.indexOfKey(id);
10862     },
10863
10864     /**
10865      * Get the Record with the specified id.
10866      * @param {String} id The id of the Record to find.
10867      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10868      */
10869     getById : function(id){
10870         return this.data.key(id);
10871     },
10872
10873     /**
10874      * Get the Record at the specified index.
10875      * @param {Number} index The index of the Record to find.
10876      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10877      */
10878     getAt : function(index){
10879         return this.data.itemAt(index);
10880     },
10881
10882     /**
10883      * Returns a range of Records between specified indices.
10884      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10885      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10886      * @return {Roo.data.Record[]} An array of Records
10887      */
10888     getRange : function(start, end){
10889         return this.data.getRange(start, end);
10890     },
10891
10892     // private
10893     storeOptions : function(o){
10894         o = Roo.apply({}, o);
10895         delete o.callback;
10896         delete o.scope;
10897         this.lastOptions = o;
10898     },
10899
10900     /**
10901      * Loads the Record cache from the configured Proxy using the configured Reader.
10902      * <p>
10903      * If using remote paging, then the first load call must specify the <em>start</em>
10904      * and <em>limit</em> properties in the options.params property to establish the initial
10905      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10906      * <p>
10907      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10908      * and this call will return before the new data has been loaded. Perform any post-processing
10909      * in a callback function, or in a "load" event handler.</strong>
10910      * <p>
10911      * @param {Object} options An object containing properties which control loading options:<ul>
10912      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10913      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10914      * passed the following arguments:<ul>
10915      * <li>r : Roo.data.Record[]</li>
10916      * <li>options: Options object from the load call</li>
10917      * <li>success: Boolean success indicator</li></ul></li>
10918      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10919      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10920      * </ul>
10921      */
10922     load : function(options){
10923         options = options || {};
10924         if(this.fireEvent("beforeload", this, options) !== false){
10925             this.storeOptions(options);
10926             var p = Roo.apply(options.params || {}, this.baseParams);
10927             // if meta was not loaded from remote source.. try requesting it.
10928             if (!this.reader.metaFromRemote) {
10929                 p._requestMeta = 1;
10930             }
10931             if(this.sortInfo && this.remoteSort){
10932                 var pn = this.paramNames;
10933                 p[pn["sort"]] = this.sortInfo.field;
10934                 p[pn["dir"]] = this.sortInfo.direction;
10935             }
10936             if (this.multiSort) {
10937                 var pn = this.paramNames;
10938                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10939             }
10940             
10941             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10942         }
10943     },
10944
10945     /**
10946      * Reloads the Record cache from the configured Proxy using the configured Reader and
10947      * the options from the last load operation performed.
10948      * @param {Object} options (optional) An object containing properties which may override the options
10949      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10950      * the most recently used options are reused).
10951      */
10952     reload : function(options){
10953         this.load(Roo.applyIf(options||{}, this.lastOptions));
10954     },
10955
10956     // private
10957     // Called as a callback by the Reader during a load operation.
10958     loadRecords : function(o, options, success){
10959         if(!o || success === false){
10960             if(success !== false){
10961                 this.fireEvent("load", this, [], options, o);
10962             }
10963             if(options.callback){
10964                 options.callback.call(options.scope || this, [], options, false);
10965             }
10966             return;
10967         }
10968         // if data returned failure - throw an exception.
10969         if (o.success === false) {
10970             // show a message if no listener is registered.
10971             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10972                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10973             }
10974             // loadmask wil be hooked into this..
10975             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10976             return;
10977         }
10978         var r = o.records, t = o.totalRecords || r.length;
10979         
10980         this.fireEvent("beforeloadadd", this, r, options, o);
10981         
10982         if(!options || options.add !== true){
10983             if(this.pruneModifiedRecords){
10984                 this.modified = [];
10985             }
10986             for(var i = 0, len = r.length; i < len; i++){
10987                 r[i].join(this);
10988             }
10989             if(this.snapshot){
10990                 this.data = this.snapshot;
10991                 delete this.snapshot;
10992             }
10993             this.data.clear();
10994             this.data.addAll(r);
10995             this.totalLength = t;
10996             this.applySort();
10997             this.fireEvent("datachanged", this);
10998         }else{
10999             this.totalLength = Math.max(t, this.data.length+r.length);
11000             this.add(r);
11001         }
11002         this.fireEvent("load", this, r, options, o);
11003         if(options.callback){
11004             options.callback.call(options.scope || this, r, options, true);
11005         }
11006     },
11007
11008
11009     /**
11010      * Loads data from a passed data block. A Reader which understands the format of the data
11011      * must have been configured in the constructor.
11012      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11013      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11014      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11015      */
11016     loadData : function(o, append){
11017         var r = this.reader.readRecords(o);
11018         this.loadRecords(r, {add: append}, true);
11019     },
11020
11021     /**
11022      * Gets the number of cached records.
11023      * <p>
11024      * <em>If using paging, this may not be the total size of the dataset. If the data object
11025      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11026      * the data set size</em>
11027      */
11028     getCount : function(){
11029         return this.data.length || 0;
11030     },
11031
11032     /**
11033      * Gets the total number of records in the dataset as returned by the server.
11034      * <p>
11035      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11036      * the dataset size</em>
11037      */
11038     getTotalCount : function(){
11039         return this.totalLength || 0;
11040     },
11041
11042     /**
11043      * Returns the sort state of the Store as an object with two properties:
11044      * <pre><code>
11045  field {String} The name of the field by which the Records are sorted
11046  direction {String} The sort order, "ASC" or "DESC"
11047      * </code></pre>
11048      */
11049     getSortState : function(){
11050         return this.sortInfo;
11051     },
11052
11053     // private
11054     applySort : function(){
11055         if(this.sortInfo && !this.remoteSort){
11056             var s = this.sortInfo, f = s.field;
11057             var st = this.fields.get(f).sortType;
11058             var fn = function(r1, r2){
11059                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11060                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11061             };
11062             this.data.sort(s.direction, fn);
11063             if(this.snapshot && this.snapshot != this.data){
11064                 this.snapshot.sort(s.direction, fn);
11065             }
11066         }
11067     },
11068
11069     /**
11070      * Sets the default sort column and order to be used by the next load operation.
11071      * @param {String} fieldName The name of the field to sort by.
11072      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11073      */
11074     setDefaultSort : function(field, dir){
11075         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11076     },
11077
11078     /**
11079      * Sort the Records.
11080      * If remote sorting is used, the sort is performed on the server, and the cache is
11081      * reloaded. If local sorting is used, the cache is sorted internally.
11082      * @param {String} fieldName The name of the field to sort by.
11083      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11084      */
11085     sort : function(fieldName, dir){
11086         var f = this.fields.get(fieldName);
11087         if(!dir){
11088             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11089             
11090             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11091                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11092             }else{
11093                 dir = f.sortDir;
11094             }
11095         }
11096         this.sortToggle[f.name] = dir;
11097         this.sortInfo = {field: f.name, direction: dir};
11098         if(!this.remoteSort){
11099             this.applySort();
11100             this.fireEvent("datachanged", this);
11101         }else{
11102             this.load(this.lastOptions);
11103         }
11104     },
11105
11106     /**
11107      * Calls the specified function for each of the Records in the cache.
11108      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11109      * Returning <em>false</em> aborts and exits the iteration.
11110      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11111      */
11112     each : function(fn, scope){
11113         this.data.each(fn, scope);
11114     },
11115
11116     /**
11117      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11118      * (e.g., during paging).
11119      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11120      */
11121     getModifiedRecords : function(){
11122         return this.modified;
11123     },
11124
11125     // private
11126     createFilterFn : function(property, value, anyMatch){
11127         if(!value.exec){ // not a regex
11128             value = String(value);
11129             if(value.length == 0){
11130                 return false;
11131             }
11132             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11133         }
11134         return function(r){
11135             return value.test(r.data[property]);
11136         };
11137     },
11138
11139     /**
11140      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11141      * @param {String} property A field on your records
11142      * @param {Number} start The record index to start at (defaults to 0)
11143      * @param {Number} end The last record index to include (defaults to length - 1)
11144      * @return {Number} The sum
11145      */
11146     sum : function(property, start, end){
11147         var rs = this.data.items, v = 0;
11148         start = start || 0;
11149         end = (end || end === 0) ? end : rs.length-1;
11150
11151         for(var i = start; i <= end; i++){
11152             v += (rs[i].data[property] || 0);
11153         }
11154         return v;
11155     },
11156
11157     /**
11158      * Filter the records by a specified property.
11159      * @param {String} field A field on your records
11160      * @param {String/RegExp} value Either a string that the field
11161      * should start with or a RegExp to test against the field
11162      * @param {Boolean} anyMatch True to match any part not just the beginning
11163      */
11164     filter : function(property, value, anyMatch){
11165         var fn = this.createFilterFn(property, value, anyMatch);
11166         return fn ? this.filterBy(fn) : this.clearFilter();
11167     },
11168
11169     /**
11170      * Filter by a function. The specified function will be called with each
11171      * record in this data source. If the function returns true the record is included,
11172      * otherwise it is filtered.
11173      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11174      * @param {Object} scope (optional) The scope of the function (defaults to this)
11175      */
11176     filterBy : function(fn, scope){
11177         this.snapshot = this.snapshot || this.data;
11178         this.data = this.queryBy(fn, scope||this);
11179         this.fireEvent("datachanged", this);
11180     },
11181
11182     /**
11183      * Query the records by a specified property.
11184      * @param {String} field A field on your records
11185      * @param {String/RegExp} value Either a string that the field
11186      * should start with or a RegExp to test against the field
11187      * @param {Boolean} anyMatch True to match any part not just the beginning
11188      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11189      */
11190     query : function(property, value, anyMatch){
11191         var fn = this.createFilterFn(property, value, anyMatch);
11192         return fn ? this.queryBy(fn) : this.data.clone();
11193     },
11194
11195     /**
11196      * Query by a function. The specified function will be called with each
11197      * record in this data source. If the function returns true the record is included
11198      * in the results.
11199      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11200      * @param {Object} scope (optional) The scope of the function (defaults to this)
11201       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11202      **/
11203     queryBy : function(fn, scope){
11204         var data = this.snapshot || this.data;
11205         return data.filterBy(fn, scope||this);
11206     },
11207
11208     /**
11209      * Collects unique values for a particular dataIndex from this store.
11210      * @param {String} dataIndex The property to collect
11211      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11212      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11213      * @return {Array} An array of the unique values
11214      **/
11215     collect : function(dataIndex, allowNull, bypassFilter){
11216         var d = (bypassFilter === true && this.snapshot) ?
11217                 this.snapshot.items : this.data.items;
11218         var v, sv, r = [], l = {};
11219         for(var i = 0, len = d.length; i < len; i++){
11220             v = d[i].data[dataIndex];
11221             sv = String(v);
11222             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11223                 l[sv] = true;
11224                 r[r.length] = v;
11225             }
11226         }
11227         return r;
11228     },
11229
11230     /**
11231      * Revert to a view of the Record cache with no filtering applied.
11232      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11233      */
11234     clearFilter : function(suppressEvent){
11235         if(this.snapshot && this.snapshot != this.data){
11236             this.data = this.snapshot;
11237             delete this.snapshot;
11238             if(suppressEvent !== true){
11239                 this.fireEvent("datachanged", this);
11240             }
11241         }
11242     },
11243
11244     // private
11245     afterEdit : function(record){
11246         if(this.modified.indexOf(record) == -1){
11247             this.modified.push(record);
11248         }
11249         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11250     },
11251     
11252     // private
11253     afterReject : function(record){
11254         this.modified.remove(record);
11255         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11256     },
11257
11258     // private
11259     afterCommit : function(record){
11260         this.modified.remove(record);
11261         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11262     },
11263
11264     /**
11265      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11266      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11267      */
11268     commitChanges : function(){
11269         var m = this.modified.slice(0);
11270         this.modified = [];
11271         for(var i = 0, len = m.length; i < len; i++){
11272             m[i].commit();
11273         }
11274     },
11275
11276     /**
11277      * Cancel outstanding changes on all changed records.
11278      */
11279     rejectChanges : function(){
11280         var m = this.modified.slice(0);
11281         this.modified = [];
11282         for(var i = 0, len = m.length; i < len; i++){
11283             m[i].reject();
11284         }
11285     },
11286
11287     onMetaChange : function(meta, rtype, o){
11288         this.recordType = rtype;
11289         this.fields = rtype.prototype.fields;
11290         delete this.snapshot;
11291         this.sortInfo = meta.sortInfo || this.sortInfo;
11292         this.modified = [];
11293         this.fireEvent('metachange', this, this.reader.meta);
11294     },
11295     
11296     moveIndex : function(data, type)
11297     {
11298         var index = this.indexOf(data);
11299         
11300         var newIndex = index + type;
11301         
11302         this.remove(data);
11303         
11304         this.insert(newIndex, data);
11305         
11306     }
11307 });/*
11308  * Based on:
11309  * Ext JS Library 1.1.1
11310  * Copyright(c) 2006-2007, Ext JS, LLC.
11311  *
11312  * Originally Released Under LGPL - original licence link has changed is not relivant.
11313  *
11314  * Fork - LGPL
11315  * <script type="text/javascript">
11316  */
11317
11318 /**
11319  * @class Roo.data.SimpleStore
11320  * @extends Roo.data.Store
11321  * Small helper class to make creating Stores from Array data easier.
11322  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11323  * @cfg {Array} fields An array of field definition objects, or field name strings.
11324  * @cfg {Array} data The multi-dimensional array of data
11325  * @constructor
11326  * @param {Object} config
11327  */
11328 Roo.data.SimpleStore = function(config){
11329     Roo.data.SimpleStore.superclass.constructor.call(this, {
11330         isLocal : true,
11331         reader: new Roo.data.ArrayReader({
11332                 id: config.id
11333             },
11334             Roo.data.Record.create(config.fields)
11335         ),
11336         proxy : new Roo.data.MemoryProxy(config.data)
11337     });
11338     this.load();
11339 };
11340 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11341  * Based on:
11342  * Ext JS Library 1.1.1
11343  * Copyright(c) 2006-2007, Ext JS, LLC.
11344  *
11345  * Originally Released Under LGPL - original licence link has changed is not relivant.
11346  *
11347  * Fork - LGPL
11348  * <script type="text/javascript">
11349  */
11350
11351 /**
11352 /**
11353  * @extends Roo.data.Store
11354  * @class Roo.data.JsonStore
11355  * Small helper class to make creating Stores for JSON data easier. <br/>
11356 <pre><code>
11357 var store = new Roo.data.JsonStore({
11358     url: 'get-images.php',
11359     root: 'images',
11360     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11361 });
11362 </code></pre>
11363  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11364  * JsonReader and HttpProxy (unless inline data is provided).</b>
11365  * @cfg {Array} fields An array of field definition objects, or field name strings.
11366  * @constructor
11367  * @param {Object} config
11368  */
11369 Roo.data.JsonStore = function(c){
11370     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11371         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11372         reader: new Roo.data.JsonReader(c, c.fields)
11373     }));
11374 };
11375 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11376  * Based on:
11377  * Ext JS Library 1.1.1
11378  * Copyright(c) 2006-2007, Ext JS, LLC.
11379  *
11380  * Originally Released Under LGPL - original licence link has changed is not relivant.
11381  *
11382  * Fork - LGPL
11383  * <script type="text/javascript">
11384  */
11385
11386  
11387 Roo.data.Field = function(config){
11388     if(typeof config == "string"){
11389         config = {name: config};
11390     }
11391     Roo.apply(this, config);
11392     
11393     if(!this.type){
11394         this.type = "auto";
11395     }
11396     
11397     var st = Roo.data.SortTypes;
11398     // named sortTypes are supported, here we look them up
11399     if(typeof this.sortType == "string"){
11400         this.sortType = st[this.sortType];
11401     }
11402     
11403     // set default sortType for strings and dates
11404     if(!this.sortType){
11405         switch(this.type){
11406             case "string":
11407                 this.sortType = st.asUCString;
11408                 break;
11409             case "date":
11410                 this.sortType = st.asDate;
11411                 break;
11412             default:
11413                 this.sortType = st.none;
11414         }
11415     }
11416
11417     // define once
11418     var stripRe = /[\$,%]/g;
11419
11420     // prebuilt conversion function for this field, instead of
11421     // switching every time we're reading a value
11422     if(!this.convert){
11423         var cv, dateFormat = this.dateFormat;
11424         switch(this.type){
11425             case "":
11426             case "auto":
11427             case undefined:
11428                 cv = function(v){ return v; };
11429                 break;
11430             case "string":
11431                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11432                 break;
11433             case "int":
11434                 cv = function(v){
11435                     return v !== undefined && v !== null && v !== '' ?
11436                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11437                     };
11438                 break;
11439             case "float":
11440                 cv = function(v){
11441                     return v !== undefined && v !== null && v !== '' ?
11442                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11443                     };
11444                 break;
11445             case "bool":
11446             case "boolean":
11447                 cv = function(v){ return v === true || v === "true" || v == 1; };
11448                 break;
11449             case "date":
11450                 cv = function(v){
11451                     if(!v){
11452                         return '';
11453                     }
11454                     if(v instanceof Date){
11455                         return v;
11456                     }
11457                     if(dateFormat){
11458                         if(dateFormat == "timestamp"){
11459                             return new Date(v*1000);
11460                         }
11461                         return Date.parseDate(v, dateFormat);
11462                     }
11463                     var parsed = Date.parse(v);
11464                     return parsed ? new Date(parsed) : null;
11465                 };
11466              break;
11467             
11468         }
11469         this.convert = cv;
11470     }
11471 };
11472
11473 Roo.data.Field.prototype = {
11474     dateFormat: null,
11475     defaultValue: "",
11476     mapping: null,
11477     sortType : null,
11478     sortDir : "ASC"
11479 };/*
11480  * Based on:
11481  * Ext JS Library 1.1.1
11482  * Copyright(c) 2006-2007, Ext JS, LLC.
11483  *
11484  * Originally Released Under LGPL - original licence link has changed is not relivant.
11485  *
11486  * Fork - LGPL
11487  * <script type="text/javascript">
11488  */
11489  
11490 // Base class for reading structured data from a data source.  This class is intended to be
11491 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11492
11493 /**
11494  * @class Roo.data.DataReader
11495  * Base class for reading structured data from a data source.  This class is intended to be
11496  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11497  */
11498
11499 Roo.data.DataReader = function(meta, recordType){
11500     
11501     this.meta = meta;
11502     
11503     this.recordType = recordType instanceof Array ? 
11504         Roo.data.Record.create(recordType) : recordType;
11505 };
11506
11507 Roo.data.DataReader.prototype = {
11508      /**
11509      * Create an empty record
11510      * @param {Object} data (optional) - overlay some values
11511      * @return {Roo.data.Record} record created.
11512      */
11513     newRow :  function(d) {
11514         var da =  {};
11515         this.recordType.prototype.fields.each(function(c) {
11516             switch( c.type) {
11517                 case 'int' : da[c.name] = 0; break;
11518                 case 'date' : da[c.name] = new Date(); break;
11519                 case 'float' : da[c.name] = 0.0; break;
11520                 case 'boolean' : da[c.name] = false; break;
11521                 default : da[c.name] = ""; break;
11522             }
11523             
11524         });
11525         return new this.recordType(Roo.apply(da, d));
11526     }
11527     
11528 };/*
11529  * Based on:
11530  * Ext JS Library 1.1.1
11531  * Copyright(c) 2006-2007, Ext JS, LLC.
11532  *
11533  * Originally Released Under LGPL - original licence link has changed is not relivant.
11534  *
11535  * Fork - LGPL
11536  * <script type="text/javascript">
11537  */
11538
11539 /**
11540  * @class Roo.data.DataProxy
11541  * @extends Roo.data.Observable
11542  * This class is an abstract base class for implementations which provide retrieval of
11543  * unformatted data objects.<br>
11544  * <p>
11545  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11546  * (of the appropriate type which knows how to parse the data object) to provide a block of
11547  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11548  * <p>
11549  * Custom implementations must implement the load method as described in
11550  * {@link Roo.data.HttpProxy#load}.
11551  */
11552 Roo.data.DataProxy = function(){
11553     this.addEvents({
11554         /**
11555          * @event beforeload
11556          * Fires before a network request is made to retrieve a data object.
11557          * @param {Object} This DataProxy object.
11558          * @param {Object} params The params parameter to the load function.
11559          */
11560         beforeload : true,
11561         /**
11562          * @event load
11563          * Fires before the load method's callback is called.
11564          * @param {Object} This DataProxy object.
11565          * @param {Object} o The data object.
11566          * @param {Object} arg The callback argument object passed to the load function.
11567          */
11568         load : true,
11569         /**
11570          * @event loadexception
11571          * Fires if an Exception occurs during data retrieval.
11572          * @param {Object} This DataProxy object.
11573          * @param {Object} o The data object.
11574          * @param {Object} arg The callback argument object passed to the load function.
11575          * @param {Object} e The Exception.
11576          */
11577         loadexception : true
11578     });
11579     Roo.data.DataProxy.superclass.constructor.call(this);
11580 };
11581
11582 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11583
11584     /**
11585      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11586      */
11587 /*
11588  * Based on:
11589  * Ext JS Library 1.1.1
11590  * Copyright(c) 2006-2007, Ext JS, LLC.
11591  *
11592  * Originally Released Under LGPL - original licence link has changed is not relivant.
11593  *
11594  * Fork - LGPL
11595  * <script type="text/javascript">
11596  */
11597 /**
11598  * @class Roo.data.MemoryProxy
11599  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11600  * to the Reader when its load method is called.
11601  * @constructor
11602  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11603  */
11604 Roo.data.MemoryProxy = function(data){
11605     if (data.data) {
11606         data = data.data;
11607     }
11608     Roo.data.MemoryProxy.superclass.constructor.call(this);
11609     this.data = data;
11610 };
11611
11612 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11613     
11614     /**
11615      * Load data from the requested source (in this case an in-memory
11616      * data object passed to the constructor), read the data object into
11617      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11618      * process that block using the passed callback.
11619      * @param {Object} params This parameter is not used by the MemoryProxy class.
11620      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11621      * object into a block of Roo.data.Records.
11622      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11623      * The function must be passed <ul>
11624      * <li>The Record block object</li>
11625      * <li>The "arg" argument from the load function</li>
11626      * <li>A boolean success indicator</li>
11627      * </ul>
11628      * @param {Object} scope The scope in which to call the callback
11629      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11630      */
11631     load : function(params, reader, callback, scope, arg){
11632         params = params || {};
11633         var result;
11634         try {
11635             result = reader.readRecords(this.data);
11636         }catch(e){
11637             this.fireEvent("loadexception", this, arg, null, e);
11638             callback.call(scope, null, arg, false);
11639             return;
11640         }
11641         callback.call(scope, result, arg, true);
11642     },
11643     
11644     // private
11645     update : function(params, records){
11646         
11647     }
11648 });/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658 /**
11659  * @class Roo.data.HttpProxy
11660  * @extends Roo.data.DataProxy
11661  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11662  * configured to reference a certain URL.<br><br>
11663  * <p>
11664  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11665  * from which the running page was served.<br><br>
11666  * <p>
11667  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11668  * <p>
11669  * Be aware that to enable the browser to parse an XML document, the server must set
11670  * the Content-Type header in the HTTP response to "text/xml".
11671  * @constructor
11672  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11673  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11674  * will be used to make the request.
11675  */
11676 Roo.data.HttpProxy = function(conn){
11677     Roo.data.HttpProxy.superclass.constructor.call(this);
11678     // is conn a conn config or a real conn?
11679     this.conn = conn;
11680     this.useAjax = !conn || !conn.events;
11681   
11682 };
11683
11684 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11685     // thse are take from connection...
11686     
11687     /**
11688      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11689      */
11690     /**
11691      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11692      * extra parameters to each request made by this object. (defaults to undefined)
11693      */
11694     /**
11695      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11696      *  to each request made by this object. (defaults to undefined)
11697      */
11698     /**
11699      * @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)
11700      */
11701     /**
11702      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11703      */
11704      /**
11705      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11706      * @type Boolean
11707      */
11708   
11709
11710     /**
11711      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11712      * @type Boolean
11713      */
11714     /**
11715      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11716      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11717      * a finer-grained basis than the DataProxy events.
11718      */
11719     getConnection : function(){
11720         return this.useAjax ? Roo.Ajax : this.conn;
11721     },
11722
11723     /**
11724      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11725      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11726      * process that block using the passed callback.
11727      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11728      * for the request to the remote server.
11729      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11730      * object into a block of Roo.data.Records.
11731      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11732      * The function must be passed <ul>
11733      * <li>The Record block object</li>
11734      * <li>The "arg" argument from the load function</li>
11735      * <li>A boolean success indicator</li>
11736      * </ul>
11737      * @param {Object} scope The scope in which to call the callback
11738      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11739      */
11740     load : function(params, reader, callback, scope, arg){
11741         if(this.fireEvent("beforeload", this, params) !== false){
11742             var  o = {
11743                 params : params || {},
11744                 request: {
11745                     callback : callback,
11746                     scope : scope,
11747                     arg : arg
11748                 },
11749                 reader: reader,
11750                 callback : this.loadResponse,
11751                 scope: this
11752             };
11753             if(this.useAjax){
11754                 Roo.applyIf(o, this.conn);
11755                 if(this.activeRequest){
11756                     Roo.Ajax.abort(this.activeRequest);
11757                 }
11758                 this.activeRequest = Roo.Ajax.request(o);
11759             }else{
11760                 this.conn.request(o);
11761             }
11762         }else{
11763             callback.call(scope||this, null, arg, false);
11764         }
11765     },
11766
11767     // private
11768     loadResponse : function(o, success, response){
11769         delete this.activeRequest;
11770         if(!success){
11771             this.fireEvent("loadexception", this, o, response);
11772             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11773             return;
11774         }
11775         var result;
11776         try {
11777             result = o.reader.read(response);
11778         }catch(e){
11779             this.fireEvent("loadexception", this, o, response, e);
11780             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11781             return;
11782         }
11783         
11784         this.fireEvent("load", this, o, o.request.arg);
11785         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11786     },
11787
11788     // private
11789     update : function(dataSet){
11790
11791     },
11792
11793     // private
11794     updateResponse : function(dataSet){
11795
11796     }
11797 });/*
11798  * Based on:
11799  * Ext JS Library 1.1.1
11800  * Copyright(c) 2006-2007, Ext JS, LLC.
11801  *
11802  * Originally Released Under LGPL - original licence link has changed is not relivant.
11803  *
11804  * Fork - LGPL
11805  * <script type="text/javascript">
11806  */
11807
11808 /**
11809  * @class Roo.data.ScriptTagProxy
11810  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11811  * other than the originating domain of the running page.<br><br>
11812  * <p>
11813  * <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
11814  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11815  * <p>
11816  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11817  * source code that is used as the source inside a &lt;script> tag.<br><br>
11818  * <p>
11819  * In order for the browser to process the returned data, the server must wrap the data object
11820  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11821  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11822  * depending on whether the callback name was passed:
11823  * <p>
11824  * <pre><code>
11825 boolean scriptTag = false;
11826 String cb = request.getParameter("callback");
11827 if (cb != null) {
11828     scriptTag = true;
11829     response.setContentType("text/javascript");
11830 } else {
11831     response.setContentType("application/x-json");
11832 }
11833 Writer out = response.getWriter();
11834 if (scriptTag) {
11835     out.write(cb + "(");
11836 }
11837 out.print(dataBlock.toJsonString());
11838 if (scriptTag) {
11839     out.write(");");
11840 }
11841 </pre></code>
11842  *
11843  * @constructor
11844  * @param {Object} config A configuration object.
11845  */
11846 Roo.data.ScriptTagProxy = function(config){
11847     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11848     Roo.apply(this, config);
11849     this.head = document.getElementsByTagName("head")[0];
11850 };
11851
11852 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11853
11854 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11855     /**
11856      * @cfg {String} url The URL from which to request the data object.
11857      */
11858     /**
11859      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11860      */
11861     timeout : 30000,
11862     /**
11863      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11864      * the server the name of the callback function set up by the load call to process the returned data object.
11865      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11866      * javascript output which calls this named function passing the data object as its only parameter.
11867      */
11868     callbackParam : "callback",
11869     /**
11870      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11871      * name to the request.
11872      */
11873     nocache : true,
11874
11875     /**
11876      * Load data from the configured URL, read the data object into
11877      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11878      * process that block using the passed callback.
11879      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11880      * for the request to the remote server.
11881      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11882      * object into a block of Roo.data.Records.
11883      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11884      * The function must be passed <ul>
11885      * <li>The Record block object</li>
11886      * <li>The "arg" argument from the load function</li>
11887      * <li>A boolean success indicator</li>
11888      * </ul>
11889      * @param {Object} scope The scope in which to call the callback
11890      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11891      */
11892     load : function(params, reader, callback, scope, arg){
11893         if(this.fireEvent("beforeload", this, params) !== false){
11894
11895             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11896
11897             var url = this.url;
11898             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11899             if(this.nocache){
11900                 url += "&_dc=" + (new Date().getTime());
11901             }
11902             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11903             var trans = {
11904                 id : transId,
11905                 cb : "stcCallback"+transId,
11906                 scriptId : "stcScript"+transId,
11907                 params : params,
11908                 arg : arg,
11909                 url : url,
11910                 callback : callback,
11911                 scope : scope,
11912                 reader : reader
11913             };
11914             var conn = this;
11915
11916             window[trans.cb] = function(o){
11917                 conn.handleResponse(o, trans);
11918             };
11919
11920             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11921
11922             if(this.autoAbort !== false){
11923                 this.abort();
11924             }
11925
11926             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11927
11928             var script = document.createElement("script");
11929             script.setAttribute("src", url);
11930             script.setAttribute("type", "text/javascript");
11931             script.setAttribute("id", trans.scriptId);
11932             this.head.appendChild(script);
11933
11934             this.trans = trans;
11935         }else{
11936             callback.call(scope||this, null, arg, false);
11937         }
11938     },
11939
11940     // private
11941     isLoading : function(){
11942         return this.trans ? true : false;
11943     },
11944
11945     /**
11946      * Abort the current server request.
11947      */
11948     abort : function(){
11949         if(this.isLoading()){
11950             this.destroyTrans(this.trans);
11951         }
11952     },
11953
11954     // private
11955     destroyTrans : function(trans, isLoaded){
11956         this.head.removeChild(document.getElementById(trans.scriptId));
11957         clearTimeout(trans.timeoutId);
11958         if(isLoaded){
11959             window[trans.cb] = undefined;
11960             try{
11961                 delete window[trans.cb];
11962             }catch(e){}
11963         }else{
11964             // if hasn't been loaded, wait for load to remove it to prevent script error
11965             window[trans.cb] = function(){
11966                 window[trans.cb] = undefined;
11967                 try{
11968                     delete window[trans.cb];
11969                 }catch(e){}
11970             };
11971         }
11972     },
11973
11974     // private
11975     handleResponse : function(o, trans){
11976         this.trans = false;
11977         this.destroyTrans(trans, true);
11978         var result;
11979         try {
11980             result = trans.reader.readRecords(o);
11981         }catch(e){
11982             this.fireEvent("loadexception", this, o, trans.arg, e);
11983             trans.callback.call(trans.scope||window, null, trans.arg, false);
11984             return;
11985         }
11986         this.fireEvent("load", this, o, trans.arg);
11987         trans.callback.call(trans.scope||window, result, trans.arg, true);
11988     },
11989
11990     // private
11991     handleFailure : function(trans){
11992         this.trans = false;
11993         this.destroyTrans(trans, false);
11994         this.fireEvent("loadexception", this, null, trans.arg);
11995         trans.callback.call(trans.scope||window, null, trans.arg, false);
11996     }
11997 });/*
11998  * Based on:
11999  * Ext JS Library 1.1.1
12000  * Copyright(c) 2006-2007, Ext JS, LLC.
12001  *
12002  * Originally Released Under LGPL - original licence link has changed is not relivant.
12003  *
12004  * Fork - LGPL
12005  * <script type="text/javascript">
12006  */
12007
12008 /**
12009  * @class Roo.data.JsonReader
12010  * @extends Roo.data.DataReader
12011  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12012  * based on mappings in a provided Roo.data.Record constructor.
12013  * 
12014  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12015  * in the reply previously. 
12016  * 
12017  * <p>
12018  * Example code:
12019  * <pre><code>
12020 var RecordDef = Roo.data.Record.create([
12021     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12022     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12023 ]);
12024 var myReader = new Roo.data.JsonReader({
12025     totalProperty: "results",    // The property which contains the total dataset size (optional)
12026     root: "rows",                // The property which contains an Array of row objects
12027     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12028 }, RecordDef);
12029 </code></pre>
12030  * <p>
12031  * This would consume a JSON file like this:
12032  * <pre><code>
12033 { 'results': 2, 'rows': [
12034     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12035     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12036 }
12037 </code></pre>
12038  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12039  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12040  * paged from the remote server.
12041  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12042  * @cfg {String} root name of the property which contains the Array of row objects.
12043  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12044  * @cfg {Array} fields Array of field definition objects
12045  * @constructor
12046  * Create a new JsonReader
12047  * @param {Object} meta Metadata configuration options
12048  * @param {Object} recordType Either an Array of field definition objects,
12049  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12050  */
12051 Roo.data.JsonReader = function(meta, recordType){
12052     
12053     meta = meta || {};
12054     // set some defaults:
12055     Roo.applyIf(meta, {
12056         totalProperty: 'total',
12057         successProperty : 'success',
12058         root : 'data',
12059         id : 'id'
12060     });
12061     
12062     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12063 };
12064 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12065     
12066     /**
12067      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12068      * Used by Store query builder to append _requestMeta to params.
12069      * 
12070      */
12071     metaFromRemote : false,
12072     /**
12073      * This method is only used by a DataProxy which has retrieved data from a remote server.
12074      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12075      * @return {Object} data A data block which is used by an Roo.data.Store object as
12076      * a cache of Roo.data.Records.
12077      */
12078     read : function(response){
12079         var json = response.responseText;
12080        
12081         var o = /* eval:var:o */ eval("("+json+")");
12082         if(!o) {
12083             throw {message: "JsonReader.read: Json object not found"};
12084         }
12085         
12086         if(o.metaData){
12087             
12088             delete this.ef;
12089             this.metaFromRemote = true;
12090             this.meta = o.metaData;
12091             this.recordType = Roo.data.Record.create(o.metaData.fields);
12092             this.onMetaChange(this.meta, this.recordType, o);
12093         }
12094         return this.readRecords(o);
12095     },
12096
12097     // private function a store will implement
12098     onMetaChange : function(meta, recordType, o){
12099
12100     },
12101
12102     /**
12103          * @ignore
12104          */
12105     simpleAccess: function(obj, subsc) {
12106         return obj[subsc];
12107     },
12108
12109         /**
12110          * @ignore
12111          */
12112     getJsonAccessor: function(){
12113         var re = /[\[\.]/;
12114         return function(expr) {
12115             try {
12116                 return(re.test(expr))
12117                     ? new Function("obj", "return obj." + expr)
12118                     : function(obj){
12119                         return obj[expr];
12120                     };
12121             } catch(e){}
12122             return Roo.emptyFn;
12123         };
12124     }(),
12125
12126     /**
12127      * Create a data block containing Roo.data.Records from an XML document.
12128      * @param {Object} o An object which contains an Array of row objects in the property specified
12129      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12130      * which contains the total size of the dataset.
12131      * @return {Object} data A data block which is used by an Roo.data.Store object as
12132      * a cache of Roo.data.Records.
12133      */
12134     readRecords : function(o){
12135         /**
12136          * After any data loads, the raw JSON data is available for further custom processing.
12137          * @type Object
12138          */
12139         this.o = o;
12140         var s = this.meta, Record = this.recordType,
12141             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12142
12143 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12144         if (!this.ef) {
12145             if(s.totalProperty) {
12146                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12147                 }
12148                 if(s.successProperty) {
12149                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12150                 }
12151                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12152                 if (s.id) {
12153                         var g = this.getJsonAccessor(s.id);
12154                         this.getId = function(rec) {
12155                                 var r = g(rec);  
12156                                 return (r === undefined || r === "") ? null : r;
12157                         };
12158                 } else {
12159                         this.getId = function(){return null;};
12160                 }
12161             this.ef = [];
12162             for(var jj = 0; jj < fl; jj++){
12163                 f = fi[jj];
12164                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12165                 this.ef[jj] = this.getJsonAccessor(map);
12166             }
12167         }
12168
12169         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12170         if(s.totalProperty){
12171             var vt = parseInt(this.getTotal(o), 10);
12172             if(!isNaN(vt)){
12173                 totalRecords = vt;
12174             }
12175         }
12176         if(s.successProperty){
12177             var vs = this.getSuccess(o);
12178             if(vs === false || vs === 'false'){
12179                 success = false;
12180             }
12181         }
12182         var records = [];
12183         for(var i = 0; i < c; i++){
12184                 var n = root[i];
12185             var values = {};
12186             var id = this.getId(n);
12187             for(var j = 0; j < fl; j++){
12188                 f = fi[j];
12189             var v = this.ef[j](n);
12190             if (!f.convert) {
12191                 Roo.log('missing convert for ' + f.name);
12192                 Roo.log(f);
12193                 continue;
12194             }
12195             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12196             }
12197             var record = new Record(values, id);
12198             record.json = n;
12199             records[i] = record;
12200         }
12201         return {
12202             raw : o,
12203             success : success,
12204             records : records,
12205             totalRecords : totalRecords
12206         };
12207     }
12208 });/*
12209  * Based on:
12210  * Ext JS Library 1.1.1
12211  * Copyright(c) 2006-2007, Ext JS, LLC.
12212  *
12213  * Originally Released Under LGPL - original licence link has changed is not relivant.
12214  *
12215  * Fork - LGPL
12216  * <script type="text/javascript">
12217  */
12218
12219 /**
12220  * @class Roo.data.ArrayReader
12221  * @extends Roo.data.DataReader
12222  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12223  * Each element of that Array represents a row of data fields. The
12224  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12225  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12226  * <p>
12227  * Example code:.
12228  * <pre><code>
12229 var RecordDef = Roo.data.Record.create([
12230     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12231     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12232 ]);
12233 var myReader = new Roo.data.ArrayReader({
12234     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12235 }, RecordDef);
12236 </code></pre>
12237  * <p>
12238  * This would consume an Array like this:
12239  * <pre><code>
12240 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12241   </code></pre>
12242  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12243  * @constructor
12244  * Create a new JsonReader
12245  * @param {Object} meta Metadata configuration options.
12246  * @param {Object} recordType Either an Array of field definition objects
12247  * as specified to {@link Roo.data.Record#create},
12248  * or an {@link Roo.data.Record} object
12249  * created using {@link Roo.data.Record#create}.
12250  */
12251 Roo.data.ArrayReader = function(meta, recordType){
12252     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12253 };
12254
12255 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12256     /**
12257      * Create a data block containing Roo.data.Records from an XML document.
12258      * @param {Object} o An Array of row objects which represents the dataset.
12259      * @return {Object} data A data block which is used by an Roo.data.Store object as
12260      * a cache of Roo.data.Records.
12261      */
12262     readRecords : function(o){
12263         var sid = this.meta ? this.meta.id : null;
12264         var recordType = this.recordType, fields = recordType.prototype.fields;
12265         var records = [];
12266         var root = o;
12267             for(var i = 0; i < root.length; i++){
12268                     var n = root[i];
12269                 var values = {};
12270                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12271                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12272                 var f = fields.items[j];
12273                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12274                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12275                 v = f.convert(v);
12276                 values[f.name] = v;
12277             }
12278                 var record = new recordType(values, id);
12279                 record.json = n;
12280                 records[records.length] = record;
12281             }
12282             return {
12283                 records : records,
12284                 totalRecords : records.length
12285             };
12286     }
12287 });/*
12288  * - LGPL
12289  * * 
12290  */
12291
12292 /**
12293  * @class Roo.bootstrap.ComboBox
12294  * @extends Roo.bootstrap.TriggerField
12295  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12296  * @cfg {Boolean} append (true|false) default false
12297  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12298  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12299  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12300  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12301  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12302  * @cfg {Boolean} animate default true
12303  * @cfg {Boolean} emptyResultText only for touch device
12304  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12305  * @constructor
12306  * Create a new ComboBox.
12307  * @param {Object} config Configuration options
12308  */
12309 Roo.bootstrap.ComboBox = function(config){
12310     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12311     this.addEvents({
12312         /**
12313          * @event expand
12314          * Fires when the dropdown list is expanded
12315              * @param {Roo.bootstrap.ComboBox} combo This combo box
12316              */
12317         'expand' : true,
12318         /**
12319          * @event collapse
12320          * Fires when the dropdown list is collapsed
12321              * @param {Roo.bootstrap.ComboBox} combo This combo box
12322              */
12323         'collapse' : true,
12324         /**
12325          * @event beforeselect
12326          * Fires before a list item is selected. Return false to cancel the selection.
12327              * @param {Roo.bootstrap.ComboBox} combo This combo box
12328              * @param {Roo.data.Record} record The data record returned from the underlying store
12329              * @param {Number} index The index of the selected item in the dropdown list
12330              */
12331         'beforeselect' : true,
12332         /**
12333          * @event select
12334          * Fires when a list item is selected
12335              * @param {Roo.bootstrap.ComboBox} combo This combo box
12336              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12337              * @param {Number} index The index of the selected item in the dropdown list
12338              */
12339         'select' : true,
12340         /**
12341          * @event beforequery
12342          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12343          * The event object passed has these properties:
12344              * @param {Roo.bootstrap.ComboBox} combo This combo box
12345              * @param {String} query The query
12346              * @param {Boolean} forceAll true to force "all" query
12347              * @param {Boolean} cancel true to cancel the query
12348              * @param {Object} e The query event object
12349              */
12350         'beforequery': true,
12351          /**
12352          * @event add
12353          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12354              * @param {Roo.bootstrap.ComboBox} combo This combo box
12355              */
12356         'add' : true,
12357         /**
12358          * @event edit
12359          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12360              * @param {Roo.bootstrap.ComboBox} combo This combo box
12361              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12362              */
12363         'edit' : true,
12364         /**
12365          * @event remove
12366          * Fires when the remove value from the combobox array
12367              * @param {Roo.bootstrap.ComboBox} combo This combo box
12368              */
12369         'remove' : true,
12370         /**
12371          * @event afterremove
12372          * Fires when the remove value from the combobox array
12373              * @param {Roo.bootstrap.ComboBox} combo This combo box
12374              */
12375         'afterremove' : true,
12376         /**
12377          * @event specialfilter
12378          * Fires when specialfilter
12379             * @param {Roo.bootstrap.ComboBox} combo This combo box
12380             */
12381         'specialfilter' : true,
12382         /**
12383          * @event tick
12384          * Fires when tick the element
12385             * @param {Roo.bootstrap.ComboBox} combo This combo box
12386             */
12387         'tick' : true,
12388         /**
12389          * @event touchviewdisplay
12390          * Fires when touch view require special display (default is using displayField)
12391             * @param {Roo.bootstrap.ComboBox} combo This combo box
12392             * @param {Object} cfg set html .
12393             */
12394         'touchviewdisplay' : true
12395         
12396     });
12397     
12398     this.item = [];
12399     this.tickItems = [];
12400     
12401     this.selectedIndex = -1;
12402     if(this.mode == 'local'){
12403         if(config.queryDelay === undefined){
12404             this.queryDelay = 10;
12405         }
12406         if(config.minChars === undefined){
12407             this.minChars = 0;
12408         }
12409     }
12410 };
12411
12412 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12413      
12414     /**
12415      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12416      * rendering into an Roo.Editor, defaults to false)
12417      */
12418     /**
12419      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12420      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12421      */
12422     /**
12423      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12424      */
12425     /**
12426      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12427      * the dropdown list (defaults to undefined, with no header element)
12428      */
12429
12430      /**
12431      * @cfg {String/Roo.Template} tpl The template to use to render the output
12432      */
12433      
12434      /**
12435      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12436      */
12437     listWidth: undefined,
12438     /**
12439      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12440      * mode = 'remote' or 'text' if mode = 'local')
12441      */
12442     displayField: undefined,
12443     
12444     /**
12445      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12446      * mode = 'remote' or 'value' if mode = 'local'). 
12447      * Note: use of a valueField requires the user make a selection
12448      * in order for a value to be mapped.
12449      */
12450     valueField: undefined,
12451     /**
12452      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12453      */
12454     modalTitle : '',
12455     
12456     /**
12457      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12458      * field's data value (defaults to the underlying DOM element's name)
12459      */
12460     hiddenName: undefined,
12461     /**
12462      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12463      */
12464     listClass: '',
12465     /**
12466      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12467      */
12468     selectedClass: 'active',
12469     
12470     /**
12471      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12472      */
12473     shadow:'sides',
12474     /**
12475      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12476      * anchor positions (defaults to 'tl-bl')
12477      */
12478     listAlign: 'tl-bl?',
12479     /**
12480      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12481      */
12482     maxHeight: 300,
12483     /**
12484      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12485      * query specified by the allQuery config option (defaults to 'query')
12486      */
12487     triggerAction: 'query',
12488     /**
12489      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12490      * (defaults to 4, does not apply if editable = false)
12491      */
12492     minChars : 4,
12493     /**
12494      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12495      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12496      */
12497     typeAhead: false,
12498     /**
12499      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12500      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12501      */
12502     queryDelay: 500,
12503     /**
12504      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12505      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12506      */
12507     pageSize: 0,
12508     /**
12509      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12510      * when editable = true (defaults to false)
12511      */
12512     selectOnFocus:false,
12513     /**
12514      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12515      */
12516     queryParam: 'query',
12517     /**
12518      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12519      * when mode = 'remote' (defaults to 'Loading...')
12520      */
12521     loadingText: 'Loading...',
12522     /**
12523      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12524      */
12525     resizable: false,
12526     /**
12527      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12528      */
12529     handleHeight : 8,
12530     /**
12531      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12532      * traditional select (defaults to true)
12533      */
12534     editable: true,
12535     /**
12536      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12537      */
12538     allQuery: '',
12539     /**
12540      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12541      */
12542     mode: 'remote',
12543     /**
12544      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12545      * listWidth has a higher value)
12546      */
12547     minListWidth : 70,
12548     /**
12549      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12550      * allow the user to set arbitrary text into the field (defaults to false)
12551      */
12552     forceSelection:false,
12553     /**
12554      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12555      * if typeAhead = true (defaults to 250)
12556      */
12557     typeAheadDelay : 250,
12558     /**
12559      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12560      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12561      */
12562     valueNotFoundText : undefined,
12563     /**
12564      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12565      */
12566     blockFocus : false,
12567     
12568     /**
12569      * @cfg {Boolean} disableClear Disable showing of clear button.
12570      */
12571     disableClear : false,
12572     /**
12573      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12574      */
12575     alwaysQuery : false,
12576     
12577     /**
12578      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12579      */
12580     multiple : false,
12581     
12582     /**
12583      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12584      */
12585     invalidClass : "has-warning",
12586     
12587     /**
12588      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12589      */
12590     validClass : "has-success",
12591     
12592     /**
12593      * @cfg {Boolean} specialFilter (true|false) special filter default false
12594      */
12595     specialFilter : false,
12596     
12597     /**
12598      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12599      */
12600     mobileTouchView : true,
12601     
12602     /**
12603      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12604      */
12605     useNativeIOS : false,
12606     
12607     ios_options : false,
12608     
12609     //private
12610     addicon : false,
12611     editicon: false,
12612     
12613     page: 0,
12614     hasQuery: false,
12615     append: false,
12616     loadNext: false,
12617     autoFocus : true,
12618     tickable : false,
12619     btnPosition : 'right',
12620     triggerList : true,
12621     showToggleBtn : true,
12622     animate : true,
12623     emptyResultText: 'Empty',
12624     triggerText : 'Select',
12625     
12626     // element that contains real text value.. (when hidden is used..)
12627     
12628     getAutoCreate : function()
12629     {   
12630         var cfg = false;
12631         //render
12632         /*
12633          * Render classic select for iso
12634          */
12635         
12636         if(Roo.isIOS && this.useNativeIOS){
12637             cfg = this.getAutoCreateNativeIOS();
12638             return cfg;
12639         }
12640         
12641         /*
12642          * Touch Devices
12643          */
12644         
12645         if(Roo.isTouch && this.mobileTouchView){
12646             cfg = this.getAutoCreateTouchView();
12647             return cfg;;
12648         }
12649         
12650         /*
12651          *  Normal ComboBox
12652          */
12653         if(!this.tickable){
12654             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12655             if(this.name == 'info_year_invest_id_display_name'){
12656                 Roo.log('cfg.................................................');
12657                 Roo.log(cfg);
12658             }
12659             return cfg;
12660         }
12661         
12662         /*
12663          *  ComboBox with tickable selections
12664          */
12665              
12666         var align = this.labelAlign || this.parentLabelAlign();
12667         
12668         cfg = {
12669             cls : 'form-group roo-combobox-tickable' //input-group
12670         };
12671         
12672         var btn_text_select = '';
12673         var btn_text_done = '';
12674         var btn_text_cancel = '';
12675         
12676         if (this.btn_text_show) {
12677             btn_text_select = 'Select';
12678             btn_text_done = 'Done';
12679             btn_text_cancel = 'Cancel'; 
12680         }
12681         
12682         var buttons = {
12683             tag : 'div',
12684             cls : 'tickable-buttons',
12685             cn : [
12686                 {
12687                     tag : 'button',
12688                     type : 'button',
12689                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12690                     //html : this.triggerText
12691                     html: btn_text_select
12692                 },
12693                 {
12694                     tag : 'button',
12695                     type : 'button',
12696                     name : 'ok',
12697                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12698                     //html : 'Done'
12699                     html: btn_text_done
12700                 },
12701                 {
12702                     tag : 'button',
12703                     type : 'button',
12704                     name : 'cancel',
12705                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12706                     //html : 'Cancel'
12707                     html: btn_text_cancel
12708                 }
12709             ]
12710         };
12711         
12712         if(this.editable){
12713             buttons.cn.unshift({
12714                 tag: 'input',
12715                 cls: 'roo-select2-search-field-input'
12716             });
12717         }
12718         
12719         var _this = this;
12720         
12721         Roo.each(buttons.cn, function(c){
12722             if (_this.size) {
12723                 c.cls += ' btn-' + _this.size;
12724             }
12725
12726             if (_this.disabled) {
12727                 c.disabled = true;
12728             }
12729         });
12730         
12731         var box = {
12732             tag: 'div',
12733             cn: [
12734                 {
12735                     tag: 'input',
12736                     type : 'hidden',
12737                     cls: 'form-hidden-field'
12738                 },
12739                 {
12740                     tag: 'ul',
12741                     cls: 'roo-select2-choices',
12742                     cn:[
12743                         {
12744                             tag: 'li',
12745                             cls: 'roo-select2-search-field',
12746                             cn: [
12747                                 buttons
12748                             ]
12749                         }
12750                     ]
12751                 }
12752             ]
12753         };
12754         
12755         var combobox = {
12756             cls: 'roo-select2-container input-group roo-select2-container-multi',
12757             cn: [
12758                 box
12759 //                {
12760 //                    tag: 'ul',
12761 //                    cls: 'typeahead typeahead-long dropdown-menu',
12762 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12763 //                }
12764             ]
12765         };
12766         
12767         if(this.hasFeedback && !this.allowBlank){
12768             
12769             var feedback = {
12770                 tag: 'span',
12771                 cls: 'glyphicon form-control-feedback'
12772             };
12773
12774             combobox.cn.push(feedback);
12775         }
12776         
12777         
12778         if (align ==='left' && this.fieldLabel.length) {
12779             
12780             cfg.cls += ' roo-form-group-label-left';
12781             
12782             cfg.cn = [
12783                 {
12784                     tag : 'i',
12785                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12786                     tooltip : 'This field is required'
12787                 },
12788                 {
12789                     tag: 'label',
12790                     'for' :  id,
12791                     cls : 'control-label',
12792                     html : this.fieldLabel
12793
12794                 },
12795                 {
12796                     cls : "", 
12797                     cn: [
12798                         combobox
12799                     ]
12800                 }
12801
12802             ];
12803             
12804             var labelCfg = cfg.cn[1];
12805             var contentCfg = cfg.cn[2];
12806             
12807
12808             if(this.indicatorpos == 'right'){
12809                 
12810                 cfg.cn = [
12811                     {
12812                         tag: 'label',
12813                         'for' :  id,
12814                         cls : 'control-label',
12815                         cn : [
12816                             {
12817                                 tag : 'span',
12818                                 html : this.fieldLabel
12819                             },
12820                             {
12821                                 tag : 'i',
12822                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12823                                 tooltip : 'This field is required'
12824                             }
12825                         ]
12826                     },
12827                     {
12828                         cls : "",
12829                         cn: [
12830                             combobox
12831                         ]
12832                     }
12833
12834                 ];
12835                 
12836                 
12837                 
12838                 labelCfg = cfg.cn[0];
12839                 contentCfg = cfg.cn[1];
12840             
12841             }
12842             
12843             if(this.labelWidth > 12){
12844                 labelCfg.style = "width: " + this.labelWidth + 'px';
12845             }
12846             
12847             if(this.labelWidth < 13 && this.labelmd == 0){
12848                 this.labelmd = this.labelWidth;
12849             }
12850             
12851             if(this.labellg > 0){
12852                 labelCfg.cls += ' col-lg-' + this.labellg;
12853                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12854             }
12855             
12856             if(this.labelmd > 0){
12857                 labelCfg.cls += ' col-md-' + this.labelmd;
12858                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12859             }
12860             
12861             if(this.labelsm > 0){
12862                 labelCfg.cls += ' col-sm-' + this.labelsm;
12863                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12864             }
12865             
12866             if(this.labelxs > 0){
12867                 labelCfg.cls += ' col-xs-' + this.labelxs;
12868                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12869             }
12870                 
12871                 
12872         } else if ( this.fieldLabel.length) {
12873 //                Roo.log(" label");
12874                  cfg.cn = [
12875                     {
12876                         tag : 'i',
12877                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12878                         tooltip : 'This field is required'
12879                     },
12880                     {
12881                         tag: 'label',
12882                         //cls : 'input-group-addon',
12883                         html : this.fieldLabel
12884                         
12885                     },
12886                     
12887                     combobox
12888                     
12889                 ];
12890                 
12891                 if(this.indicatorpos == 'right'){
12892                     
12893                     cfg.cn = [
12894                         {
12895                             tag: 'label',
12896                             //cls : 'input-group-addon',
12897                             cn : [
12898                                 {
12899                                     tag : 'span',
12900                                     html : this.fieldLabel
12901                                 },
12902                                 {
12903                                     tag : 'i',
12904                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12905                                     tooltip : 'This field is required'
12906                                 }
12907                             ]
12908                         },
12909                         
12910                         
12911                         
12912                         combobox
12913
12914                     ];
12915                 
12916                 }
12917
12918         } else {
12919             
12920 //                Roo.log(" no label && no align");
12921                 cfg = combobox
12922                      
12923                 
12924         }
12925          
12926         var settings=this;
12927         ['xs','sm','md','lg'].map(function(size){
12928             if (settings[size]) {
12929                 cfg.cls += ' col-' + size + '-' + settings[size];
12930             }
12931         });
12932         
12933         return cfg;
12934         
12935     },
12936     
12937     _initEventsCalled : false,
12938     
12939     // private
12940     initEvents: function()
12941     {   
12942         if (this._initEventsCalled) { // as we call render... prevent looping...
12943             return;
12944         }
12945         this._initEventsCalled = true;
12946         
12947         if (!this.store) {
12948             throw "can not find store for combo";
12949         }
12950         
12951         this.store = Roo.factory(this.store, Roo.data);
12952         
12953         // if we are building from html. then this element is so complex, that we can not really
12954         // use the rendered HTML.
12955         // so we have to trash and replace the previous code.
12956         if (Roo.XComponent.build_from_html) {
12957             
12958             // remove this element....
12959             var e = this.el.dom, k=0;
12960             while (e ) { e = e.previousSibling;  ++k;}
12961
12962             this.el.remove();
12963             
12964             this.el=false;
12965             this.rendered = false;
12966             
12967             this.render(this.parent().getChildContainer(true), k);
12968             
12969             
12970             
12971         }
12972         
12973         if(Roo.isIOS && this.useNativeIOS){
12974             this.initIOSView();
12975             return;
12976         }
12977         
12978         /*
12979          * Touch Devices
12980          */
12981         
12982         if(Roo.isTouch && this.mobileTouchView){
12983             this.initTouchView();
12984             return;
12985         }
12986         
12987         if(this.tickable){
12988             this.initTickableEvents();
12989             return;
12990         }
12991         
12992         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12993         
12994         if(this.hiddenName){
12995             
12996             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12997             
12998             this.hiddenField.dom.value =
12999                 this.hiddenValue !== undefined ? this.hiddenValue :
13000                 this.value !== undefined ? this.value : '';
13001
13002             // prevent input submission
13003             this.el.dom.removeAttribute('name');
13004             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13005              
13006              
13007         }
13008         //if(Roo.isGecko){
13009         //    this.el.dom.setAttribute('autocomplete', 'off');
13010         //}
13011         
13012         var cls = 'x-combo-list';
13013         
13014         //this.list = new Roo.Layer({
13015         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13016         //});
13017         
13018         var _this = this;
13019         
13020         (function(){
13021             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13022             _this.list.setWidth(lw);
13023         }).defer(100);
13024         
13025         this.list.on('mouseover', this.onViewOver, this);
13026         this.list.on('mousemove', this.onViewMove, this);
13027         
13028         this.list.on('scroll', this.onViewScroll, this);
13029         
13030         /*
13031         this.list.swallowEvent('mousewheel');
13032         this.assetHeight = 0;
13033
13034         if(this.title){
13035             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13036             this.assetHeight += this.header.getHeight();
13037         }
13038
13039         this.innerList = this.list.createChild({cls:cls+'-inner'});
13040         this.innerList.on('mouseover', this.onViewOver, this);
13041         this.innerList.on('mousemove', this.onViewMove, this);
13042         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13043         
13044         if(this.allowBlank && !this.pageSize && !this.disableClear){
13045             this.footer = this.list.createChild({cls:cls+'-ft'});
13046             this.pageTb = new Roo.Toolbar(this.footer);
13047            
13048         }
13049         if(this.pageSize){
13050             this.footer = this.list.createChild({cls:cls+'-ft'});
13051             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13052                     {pageSize: this.pageSize});
13053             
13054         }
13055         
13056         if (this.pageTb && this.allowBlank && !this.disableClear) {
13057             var _this = this;
13058             this.pageTb.add(new Roo.Toolbar.Fill(), {
13059                 cls: 'x-btn-icon x-btn-clear',
13060                 text: '&#160;',
13061                 handler: function()
13062                 {
13063                     _this.collapse();
13064                     _this.clearValue();
13065                     _this.onSelect(false, -1);
13066                 }
13067             });
13068         }
13069         if (this.footer) {
13070             this.assetHeight += this.footer.getHeight();
13071         }
13072         */
13073             
13074         if(!this.tpl){
13075             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13076         }
13077
13078         this.view = new Roo.View(this.list, this.tpl, {
13079             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13080         });
13081         //this.view.wrapEl.setDisplayed(false);
13082         this.view.on('click', this.onViewClick, this);
13083         
13084         
13085         
13086         this.store.on('beforeload', this.onBeforeLoad, this);
13087         this.store.on('load', this.onLoad, this);
13088         this.store.on('loadexception', this.onLoadException, this);
13089         /*
13090         if(this.resizable){
13091             this.resizer = new Roo.Resizable(this.list,  {
13092                pinned:true, handles:'se'
13093             });
13094             this.resizer.on('resize', function(r, w, h){
13095                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13096                 this.listWidth = w;
13097                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13098                 this.restrictHeight();
13099             }, this);
13100             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13101         }
13102         */
13103         if(!this.editable){
13104             this.editable = true;
13105             this.setEditable(false);
13106         }
13107         
13108         /*
13109         
13110         if (typeof(this.events.add.listeners) != 'undefined') {
13111             
13112             this.addicon = this.wrap.createChild(
13113                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13114        
13115             this.addicon.on('click', function(e) {
13116                 this.fireEvent('add', this);
13117             }, this);
13118         }
13119         if (typeof(this.events.edit.listeners) != 'undefined') {
13120             
13121             this.editicon = this.wrap.createChild(
13122                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13123             if (this.addicon) {
13124                 this.editicon.setStyle('margin-left', '40px');
13125             }
13126             this.editicon.on('click', function(e) {
13127                 
13128                 // we fire even  if inothing is selected..
13129                 this.fireEvent('edit', this, this.lastData );
13130                 
13131             }, this);
13132         }
13133         */
13134         
13135         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13136             "up" : function(e){
13137                 this.inKeyMode = true;
13138                 this.selectPrev();
13139             },
13140
13141             "down" : function(e){
13142                 if(!this.isExpanded()){
13143                     this.onTriggerClick();
13144                 }else{
13145                     this.inKeyMode = true;
13146                     this.selectNext();
13147                 }
13148             },
13149
13150             "enter" : function(e){
13151 //                this.onViewClick();
13152                 //return true;
13153                 this.collapse();
13154                 
13155                 if(this.fireEvent("specialkey", this, e)){
13156                     this.onViewClick(false);
13157                 }
13158                 
13159                 return true;
13160             },
13161
13162             "esc" : function(e){
13163                 this.collapse();
13164             },
13165
13166             "tab" : function(e){
13167                 this.collapse();
13168                 
13169                 if(this.fireEvent("specialkey", this, e)){
13170                     this.onViewClick(false);
13171                 }
13172                 
13173                 return true;
13174             },
13175
13176             scope : this,
13177
13178             doRelay : function(foo, bar, hname){
13179                 if(hname == 'down' || this.scope.isExpanded()){
13180                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13181                 }
13182                 return true;
13183             },
13184
13185             forceKeyDown: true
13186         });
13187         
13188         
13189         this.queryDelay = Math.max(this.queryDelay || 10,
13190                 this.mode == 'local' ? 10 : 250);
13191         
13192         
13193         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13194         
13195         if(this.typeAhead){
13196             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13197         }
13198         if(this.editable !== false){
13199             this.inputEl().on("keyup", this.onKeyUp, this);
13200         }
13201         if(this.forceSelection){
13202             this.inputEl().on('blur', this.doForce, this);
13203         }
13204         
13205         if(this.multiple){
13206             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13207             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13208         }
13209     },
13210     
13211     initTickableEvents: function()
13212     {   
13213         this.createList();
13214         
13215         if(this.hiddenName){
13216             
13217             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13218             
13219             this.hiddenField.dom.value =
13220                 this.hiddenValue !== undefined ? this.hiddenValue :
13221                 this.value !== undefined ? this.value : '';
13222
13223             // prevent input submission
13224             this.el.dom.removeAttribute('name');
13225             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13226              
13227              
13228         }
13229         
13230 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13231         
13232         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13233         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13234         if(this.triggerList){
13235             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13236         }
13237          
13238         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13239         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13240         
13241         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13242         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13243         
13244         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13245         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13246         
13247         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13248         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13249         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13250         
13251         this.okBtn.hide();
13252         this.cancelBtn.hide();
13253         
13254         var _this = this;
13255         
13256         (function(){
13257             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13258             _this.list.setWidth(lw);
13259         }).defer(100);
13260         
13261         this.list.on('mouseover', this.onViewOver, this);
13262         this.list.on('mousemove', this.onViewMove, this);
13263         
13264         this.list.on('scroll', this.onViewScroll, this);
13265         
13266         if(!this.tpl){
13267             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>';
13268         }
13269
13270         this.view = new Roo.View(this.list, this.tpl, {
13271             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13272         });
13273         
13274         //this.view.wrapEl.setDisplayed(false);
13275         this.view.on('click', this.onViewClick, this);
13276         
13277         
13278         
13279         this.store.on('beforeload', this.onBeforeLoad, this);
13280         this.store.on('load', this.onLoad, this);
13281         this.store.on('loadexception', this.onLoadException, this);
13282         
13283         if(this.editable){
13284             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13285                 "up" : function(e){
13286                     this.inKeyMode = true;
13287                     this.selectPrev();
13288                 },
13289
13290                 "down" : function(e){
13291                     this.inKeyMode = true;
13292                     this.selectNext();
13293                 },
13294
13295                 "enter" : function(e){
13296                     if(this.fireEvent("specialkey", this, e)){
13297                         this.onViewClick(false);
13298                     }
13299                     
13300                     return true;
13301                 },
13302
13303                 "esc" : function(e){
13304                     this.onTickableFooterButtonClick(e, false, false);
13305                 },
13306
13307                 "tab" : function(e){
13308                     this.fireEvent("specialkey", this, e);
13309                     
13310                     this.onTickableFooterButtonClick(e, false, false);
13311                     
13312                     return true;
13313                 },
13314
13315                 scope : this,
13316
13317                 doRelay : function(e, fn, key){
13318                     if(this.scope.isExpanded()){
13319                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13320                     }
13321                     return true;
13322                 },
13323
13324                 forceKeyDown: true
13325             });
13326         }
13327         
13328         this.queryDelay = Math.max(this.queryDelay || 10,
13329                 this.mode == 'local' ? 10 : 250);
13330         
13331         
13332         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13333         
13334         if(this.typeAhead){
13335             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13336         }
13337         
13338         if(this.editable !== false){
13339             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13340         }
13341         
13342         this.indicator = this.indicatorEl();
13343         
13344         if(this.indicator){
13345             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13346             this.indicator.hide();
13347         }
13348         
13349     },
13350
13351     onDestroy : function(){
13352         if(this.view){
13353             this.view.setStore(null);
13354             this.view.el.removeAllListeners();
13355             this.view.el.remove();
13356             this.view.purgeListeners();
13357         }
13358         if(this.list){
13359             this.list.dom.innerHTML  = '';
13360         }
13361         
13362         if(this.store){
13363             this.store.un('beforeload', this.onBeforeLoad, this);
13364             this.store.un('load', this.onLoad, this);
13365             this.store.un('loadexception', this.onLoadException, this);
13366         }
13367         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13368     },
13369
13370     // private
13371     fireKey : function(e){
13372         if(e.isNavKeyPress() && !this.list.isVisible()){
13373             this.fireEvent("specialkey", this, e);
13374         }
13375     },
13376
13377     // private
13378     onResize: function(w, h){
13379 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13380 //        
13381 //        if(typeof w != 'number'){
13382 //            // we do not handle it!?!?
13383 //            return;
13384 //        }
13385 //        var tw = this.trigger.getWidth();
13386 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13387 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13388 //        var x = w - tw;
13389 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13390 //            
13391 //        //this.trigger.setStyle('left', x+'px');
13392 //        
13393 //        if(this.list && this.listWidth === undefined){
13394 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13395 //            this.list.setWidth(lw);
13396 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13397 //        }
13398         
13399     
13400         
13401     },
13402
13403     /**
13404      * Allow or prevent the user from directly editing the field text.  If false is passed,
13405      * the user will only be able to select from the items defined in the dropdown list.  This method
13406      * is the runtime equivalent of setting the 'editable' config option at config time.
13407      * @param {Boolean} value True to allow the user to directly edit the field text
13408      */
13409     setEditable : function(value){
13410         if(value == this.editable){
13411             return;
13412         }
13413         this.editable = value;
13414         if(!value){
13415             this.inputEl().dom.setAttribute('readOnly', true);
13416             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13417             this.inputEl().addClass('x-combo-noedit');
13418         }else{
13419             this.inputEl().dom.setAttribute('readOnly', false);
13420             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13421             this.inputEl().removeClass('x-combo-noedit');
13422         }
13423     },
13424
13425     // private
13426     
13427     onBeforeLoad : function(combo,opts){
13428         if(!this.hasFocus){
13429             return;
13430         }
13431          if (!opts.add) {
13432             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13433          }
13434         this.restrictHeight();
13435         this.selectedIndex = -1;
13436     },
13437
13438     // private
13439     onLoad : function(){
13440         
13441         this.hasQuery = false;
13442         
13443         if(!this.hasFocus){
13444             return;
13445         }
13446         
13447         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13448             this.loading.hide();
13449         }
13450              
13451         if(this.store.getCount() > 0){
13452             this.expand();
13453             this.restrictHeight();
13454             if(this.lastQuery == this.allQuery){
13455                 if(this.editable && !this.tickable){
13456                     this.inputEl().dom.select();
13457                 }
13458                 
13459                 if(
13460                     !this.selectByValue(this.value, true) &&
13461                     this.autoFocus && 
13462                     (
13463                         !this.store.lastOptions ||
13464                         typeof(this.store.lastOptions.add) == 'undefined' || 
13465                         this.store.lastOptions.add != true
13466                     )
13467                 ){
13468                     this.select(0, true);
13469                 }
13470             }else{
13471                 if(this.autoFocus){
13472                     this.selectNext();
13473                 }
13474                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13475                     this.taTask.delay(this.typeAheadDelay);
13476                 }
13477             }
13478         }else{
13479             this.onEmptyResults();
13480         }
13481         
13482         //this.el.focus();
13483     },
13484     // private
13485     onLoadException : function()
13486     {
13487         this.hasQuery = false;
13488         
13489         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13490             this.loading.hide();
13491         }
13492         
13493         if(this.tickable && this.editable){
13494             return;
13495         }
13496         
13497         this.collapse();
13498         // only causes errors at present
13499         //Roo.log(this.store.reader.jsonData);
13500         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13501             // fixme
13502             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13503         //}
13504         
13505         
13506     },
13507     // private
13508     onTypeAhead : function(){
13509         if(this.store.getCount() > 0){
13510             var r = this.store.getAt(0);
13511             var newValue = r.data[this.displayField];
13512             var len = newValue.length;
13513             var selStart = this.getRawValue().length;
13514             
13515             if(selStart != len){
13516                 this.setRawValue(newValue);
13517                 this.selectText(selStart, newValue.length);
13518             }
13519         }
13520     },
13521
13522     // private
13523     onSelect : function(record, index){
13524         
13525         if(this.fireEvent('beforeselect', this, record, index) !== false){
13526         
13527             this.setFromData(index > -1 ? record.data : false);
13528             
13529             this.collapse();
13530             this.fireEvent('select', this, record, index);
13531         }
13532     },
13533
13534     /**
13535      * Returns the currently selected field value or empty string if no value is set.
13536      * @return {String} value The selected value
13537      */
13538     getValue : function()
13539     {
13540         if(Roo.isIOS && this.useNativeIOS){
13541             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13542         }
13543         
13544         if(this.multiple){
13545             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13546         }
13547         
13548         if(this.valueField){
13549             return typeof this.value != 'undefined' ? this.value : '';
13550         }else{
13551             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13552         }
13553     },
13554     
13555     getRawValue : function()
13556     {
13557         if(Roo.isIOS && this.useNativeIOS){
13558             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13559         }
13560         
13561         var v = this.inputEl().getValue();
13562         
13563         return v;
13564     },
13565
13566     /**
13567      * Clears any text/value currently set in the field
13568      */
13569     clearValue : function(){
13570         
13571         if(this.hiddenField){
13572             this.hiddenField.dom.value = '';
13573         }
13574         this.value = '';
13575         this.setRawValue('');
13576         this.lastSelectionText = '';
13577         this.lastData = false;
13578         
13579         var close = this.closeTriggerEl();
13580         
13581         if(close){
13582             close.hide();
13583         }
13584         
13585         this.validate();
13586         
13587     },
13588
13589     /**
13590      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13591      * will be displayed in the field.  If the value does not match the data value of an existing item,
13592      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13593      * Otherwise the field will be blank (although the value will still be set).
13594      * @param {String} value The value to match
13595      */
13596     setValue : function(v)
13597     {
13598         if(Roo.isIOS && this.useNativeIOS){
13599             this.setIOSValue(v);
13600             return;
13601         }
13602         
13603         if(this.multiple){
13604             this.syncValue();
13605             return;
13606         }
13607         
13608         var text = v;
13609         if(this.valueField){
13610             var r = this.findRecord(this.valueField, v);
13611             if(r){
13612                 text = r.data[this.displayField];
13613             }else if(this.valueNotFoundText !== undefined){
13614                 text = this.valueNotFoundText;
13615             }
13616         }
13617         this.lastSelectionText = text;
13618         if(this.hiddenField){
13619             this.hiddenField.dom.value = v;
13620         }
13621         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13622         this.value = v;
13623         
13624         var close = this.closeTriggerEl();
13625         
13626         if(close){
13627             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13628         }
13629         
13630         this.validate();
13631     },
13632     /**
13633      * @property {Object} the last set data for the element
13634      */
13635     
13636     lastData : false,
13637     /**
13638      * Sets the value of the field based on a object which is related to the record format for the store.
13639      * @param {Object} value the value to set as. or false on reset?
13640      */
13641     setFromData : function(o){
13642         
13643         if(this.multiple){
13644             this.addItem(o);
13645             return;
13646         }
13647             
13648         var dv = ''; // display value
13649         var vv = ''; // value value..
13650         this.lastData = o;
13651         if (this.displayField) {
13652             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13653         } else {
13654             // this is an error condition!!!
13655             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13656         }
13657         
13658         if(this.valueField){
13659             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13660         }
13661         
13662         var close = this.closeTriggerEl();
13663         
13664         if(close){
13665             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13666         }
13667         
13668         if(this.hiddenField){
13669             this.hiddenField.dom.value = vv;
13670             
13671             this.lastSelectionText = dv;
13672             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13673             this.value = vv;
13674             return;
13675         }
13676         // no hidden field.. - we store the value in 'value', but still display
13677         // display field!!!!
13678         this.lastSelectionText = dv;
13679         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13680         this.value = vv;
13681         
13682         
13683         
13684     },
13685     // private
13686     reset : function(){
13687         // overridden so that last data is reset..
13688         
13689         if(this.multiple){
13690             this.clearItem();
13691             return;
13692         }
13693         
13694         this.setValue(this.originalValue);
13695         //this.clearInvalid();
13696         this.lastData = false;
13697         if (this.view) {
13698             this.view.clearSelections();
13699         }
13700         
13701         this.validate();
13702     },
13703     // private
13704     findRecord : function(prop, value){
13705         var record;
13706         if(this.store.getCount() > 0){
13707             this.store.each(function(r){
13708                 if(r.data[prop] == value){
13709                     record = r;
13710                     return false;
13711                 }
13712                 return true;
13713             });
13714         }
13715         return record;
13716     },
13717     
13718     getName: function()
13719     {
13720         // returns hidden if it's set..
13721         if (!this.rendered) {return ''};
13722         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13723         
13724     },
13725     // private
13726     onViewMove : function(e, t){
13727         this.inKeyMode = false;
13728     },
13729
13730     // private
13731     onViewOver : function(e, t){
13732         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13733             return;
13734         }
13735         var item = this.view.findItemFromChild(t);
13736         
13737         if(item){
13738             var index = this.view.indexOf(item);
13739             this.select(index, false);
13740         }
13741     },
13742
13743     // private
13744     onViewClick : function(view, doFocus, el, e)
13745     {
13746         var index = this.view.getSelectedIndexes()[0];
13747         
13748         var r = this.store.getAt(index);
13749         
13750         if(this.tickable){
13751             
13752             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13753                 return;
13754             }
13755             
13756             var rm = false;
13757             var _this = this;
13758             
13759             Roo.each(this.tickItems, function(v,k){
13760                 
13761                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13762                     Roo.log(v);
13763                     _this.tickItems.splice(k, 1);
13764                     
13765                     if(typeof(e) == 'undefined' && view == false){
13766                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13767                     }
13768                     
13769                     rm = true;
13770                     return;
13771                 }
13772             });
13773             
13774             if(rm){
13775                 return;
13776             }
13777             
13778             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13779                 this.tickItems.push(r.data);
13780             }
13781             
13782             if(typeof(e) == 'undefined' && view == false){
13783                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13784             }
13785                     
13786             return;
13787         }
13788         
13789         if(r){
13790             this.onSelect(r, index);
13791         }
13792         if(doFocus !== false && !this.blockFocus){
13793             this.inputEl().focus();
13794         }
13795     },
13796
13797     // private
13798     restrictHeight : function(){
13799         //this.innerList.dom.style.height = '';
13800         //var inner = this.innerList.dom;
13801         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13802         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13803         //this.list.beginUpdate();
13804         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13805         this.list.alignTo(this.inputEl(), this.listAlign);
13806         this.list.alignTo(this.inputEl(), this.listAlign);
13807         //this.list.endUpdate();
13808     },
13809
13810     // private
13811     onEmptyResults : function(){
13812         
13813         if(this.tickable && this.editable){
13814             this.restrictHeight();
13815             return;
13816         }
13817         
13818         this.collapse();
13819     },
13820
13821     /**
13822      * Returns true if the dropdown list is expanded, else false.
13823      */
13824     isExpanded : function(){
13825         return this.list.isVisible();
13826     },
13827
13828     /**
13829      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13830      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13831      * @param {String} value The data value of the item to select
13832      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13833      * selected item if it is not currently in view (defaults to true)
13834      * @return {Boolean} True if the value matched an item in the list, else false
13835      */
13836     selectByValue : function(v, scrollIntoView){
13837         if(v !== undefined && v !== null){
13838             var r = this.findRecord(this.valueField || this.displayField, v);
13839             if(r){
13840                 this.select(this.store.indexOf(r), scrollIntoView);
13841                 return true;
13842             }
13843         }
13844         return false;
13845     },
13846
13847     /**
13848      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13849      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13850      * @param {Number} index The zero-based index of the list item to select
13851      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13852      * selected item if it is not currently in view (defaults to true)
13853      */
13854     select : function(index, scrollIntoView){
13855         this.selectedIndex = index;
13856         this.view.select(index);
13857         if(scrollIntoView !== false){
13858             var el = this.view.getNode(index);
13859             /*
13860              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13861              */
13862             if(el){
13863                 this.list.scrollChildIntoView(el, false);
13864             }
13865         }
13866     },
13867
13868     // private
13869     selectNext : function(){
13870         var ct = this.store.getCount();
13871         if(ct > 0){
13872             if(this.selectedIndex == -1){
13873                 this.select(0);
13874             }else if(this.selectedIndex < ct-1){
13875                 this.select(this.selectedIndex+1);
13876             }
13877         }
13878     },
13879
13880     // private
13881     selectPrev : function(){
13882         var ct = this.store.getCount();
13883         if(ct > 0){
13884             if(this.selectedIndex == -1){
13885                 this.select(0);
13886             }else if(this.selectedIndex != 0){
13887                 this.select(this.selectedIndex-1);
13888             }
13889         }
13890     },
13891
13892     // private
13893     onKeyUp : function(e){
13894         if(this.editable !== false && !e.isSpecialKey()){
13895             this.lastKey = e.getKey();
13896             this.dqTask.delay(this.queryDelay);
13897         }
13898     },
13899
13900     // private
13901     validateBlur : function(){
13902         return !this.list || !this.list.isVisible();   
13903     },
13904
13905     // private
13906     initQuery : function(){
13907         
13908         var v = this.getRawValue();
13909         
13910         if(this.tickable && this.editable){
13911             v = this.tickableInputEl().getValue();
13912         }
13913         
13914         this.doQuery(v);
13915     },
13916
13917     // private
13918     doForce : function(){
13919         if(this.inputEl().dom.value.length > 0){
13920             this.inputEl().dom.value =
13921                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13922              
13923         }
13924     },
13925
13926     /**
13927      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13928      * query allowing the query action to be canceled if needed.
13929      * @param {String} query The SQL query to execute
13930      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13931      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13932      * saved in the current store (defaults to false)
13933      */
13934     doQuery : function(q, forceAll){
13935         
13936         if(q === undefined || q === null){
13937             q = '';
13938         }
13939         var qe = {
13940             query: q,
13941             forceAll: forceAll,
13942             combo: this,
13943             cancel:false
13944         };
13945         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13946             return false;
13947         }
13948         q = qe.query;
13949         
13950         forceAll = qe.forceAll;
13951         if(forceAll === true || (q.length >= this.minChars)){
13952             
13953             this.hasQuery = true;
13954             
13955             if(this.lastQuery != q || this.alwaysQuery){
13956                 this.lastQuery = q;
13957                 if(this.mode == 'local'){
13958                     this.selectedIndex = -1;
13959                     if(forceAll){
13960                         this.store.clearFilter();
13961                     }else{
13962                         
13963                         if(this.specialFilter){
13964                             this.fireEvent('specialfilter', this);
13965                             this.onLoad();
13966                             return;
13967                         }
13968                         
13969                         this.store.filter(this.displayField, q);
13970                     }
13971                     
13972                     this.store.fireEvent("datachanged", this.store);
13973                     
13974                     this.onLoad();
13975                     
13976                     
13977                 }else{
13978                     
13979                     this.store.baseParams[this.queryParam] = q;
13980                     
13981                     var options = {params : this.getParams(q)};
13982                     
13983                     if(this.loadNext){
13984                         options.add = true;
13985                         options.params.start = this.page * this.pageSize;
13986                     }
13987                     
13988                     this.store.load(options);
13989                     
13990                     /*
13991                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13992                      *  we should expand the list on onLoad
13993                      *  so command out it
13994                      */
13995 //                    this.expand();
13996                 }
13997             }else{
13998                 this.selectedIndex = -1;
13999                 this.onLoad();   
14000             }
14001         }
14002         
14003         this.loadNext = false;
14004     },
14005     
14006     // private
14007     getParams : function(q){
14008         var p = {};
14009         //p[this.queryParam] = q;
14010         
14011         if(this.pageSize){
14012             p.start = 0;
14013             p.limit = this.pageSize;
14014         }
14015         return p;
14016     },
14017
14018     /**
14019      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14020      */
14021     collapse : function(){
14022         if(!this.isExpanded()){
14023             return;
14024         }
14025         
14026         this.list.hide();
14027         
14028         this.hasFocus = false;
14029         
14030         if(this.tickable){
14031             this.okBtn.hide();
14032             this.cancelBtn.hide();
14033             this.trigger.show();
14034             
14035             if(this.editable){
14036                 this.tickableInputEl().dom.value = '';
14037                 this.tickableInputEl().blur();
14038             }
14039             
14040         }
14041         
14042         Roo.get(document).un('mousedown', this.collapseIf, this);
14043         Roo.get(document).un('mousewheel', this.collapseIf, this);
14044         if (!this.editable) {
14045             Roo.get(document).un('keydown', this.listKeyPress, this);
14046         }
14047         this.fireEvent('collapse', this);
14048         
14049         this.validate();
14050     },
14051
14052     // private
14053     collapseIf : function(e){
14054         var in_combo  = e.within(this.el);
14055         var in_list =  e.within(this.list);
14056         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14057         
14058         if (in_combo || in_list || is_list) {
14059             //e.stopPropagation();
14060             return;
14061         }
14062         
14063         if(this.tickable){
14064             this.onTickableFooterButtonClick(e, false, false);
14065         }
14066
14067         this.collapse();
14068         
14069     },
14070
14071     /**
14072      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14073      */
14074     expand : function(){
14075        
14076         if(this.isExpanded() || !this.hasFocus){
14077             return;
14078         }
14079         
14080         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14081         this.list.setWidth(lw);
14082         
14083         Roo.log('expand');
14084         
14085         this.list.show();
14086         
14087         this.restrictHeight();
14088         
14089         if(this.tickable){
14090             
14091             this.tickItems = Roo.apply([], this.item);
14092             
14093             this.okBtn.show();
14094             this.cancelBtn.show();
14095             this.trigger.hide();
14096             
14097             if(this.editable){
14098                 this.tickableInputEl().focus();
14099             }
14100             
14101         }
14102         
14103         Roo.get(document).on('mousedown', this.collapseIf, this);
14104         Roo.get(document).on('mousewheel', this.collapseIf, this);
14105         if (!this.editable) {
14106             Roo.get(document).on('keydown', this.listKeyPress, this);
14107         }
14108         
14109         this.fireEvent('expand', this);
14110     },
14111
14112     // private
14113     // Implements the default empty TriggerField.onTriggerClick function
14114     onTriggerClick : function(e)
14115     {
14116         Roo.log('trigger click');
14117         
14118         if(this.disabled || !this.triggerList){
14119             return;
14120         }
14121         
14122         this.page = 0;
14123         this.loadNext = false;
14124         
14125         if(this.isExpanded()){
14126             this.collapse();
14127             if (!this.blockFocus) {
14128                 this.inputEl().focus();
14129             }
14130             
14131         }else {
14132             this.hasFocus = true;
14133             if(this.triggerAction == 'all') {
14134                 this.doQuery(this.allQuery, true);
14135             } else {
14136                 this.doQuery(this.getRawValue());
14137             }
14138             if (!this.blockFocus) {
14139                 this.inputEl().focus();
14140             }
14141         }
14142     },
14143     
14144     onTickableTriggerClick : function(e)
14145     {
14146         if(this.disabled){
14147             return;
14148         }
14149         
14150         this.page = 0;
14151         this.loadNext = false;
14152         this.hasFocus = true;
14153         
14154         if(this.triggerAction == 'all') {
14155             this.doQuery(this.allQuery, true);
14156         } else {
14157             this.doQuery(this.getRawValue());
14158         }
14159     },
14160     
14161     onSearchFieldClick : function(e)
14162     {
14163         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14164             this.onTickableFooterButtonClick(e, false, false);
14165             return;
14166         }
14167         
14168         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14169             return;
14170         }
14171         
14172         this.page = 0;
14173         this.loadNext = false;
14174         this.hasFocus = true;
14175         
14176         if(this.triggerAction == 'all') {
14177             this.doQuery(this.allQuery, true);
14178         } else {
14179             this.doQuery(this.getRawValue());
14180         }
14181     },
14182     
14183     listKeyPress : function(e)
14184     {
14185         //Roo.log('listkeypress');
14186         // scroll to first matching element based on key pres..
14187         if (e.isSpecialKey()) {
14188             return false;
14189         }
14190         var k = String.fromCharCode(e.getKey()).toUpperCase();
14191         //Roo.log(k);
14192         var match  = false;
14193         var csel = this.view.getSelectedNodes();
14194         var cselitem = false;
14195         if (csel.length) {
14196             var ix = this.view.indexOf(csel[0]);
14197             cselitem  = this.store.getAt(ix);
14198             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14199                 cselitem = false;
14200             }
14201             
14202         }
14203         
14204         this.store.each(function(v) { 
14205             if (cselitem) {
14206                 // start at existing selection.
14207                 if (cselitem.id == v.id) {
14208                     cselitem = false;
14209                 }
14210                 return true;
14211             }
14212                 
14213             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14214                 match = this.store.indexOf(v);
14215                 return false;
14216             }
14217             return true;
14218         }, this);
14219         
14220         if (match === false) {
14221             return true; // no more action?
14222         }
14223         // scroll to?
14224         this.view.select(match);
14225         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14226         sn.scrollIntoView(sn.dom.parentNode, false);
14227     },
14228     
14229     onViewScroll : function(e, t){
14230         
14231         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){
14232             return;
14233         }
14234         
14235         this.hasQuery = true;
14236         
14237         this.loading = this.list.select('.loading', true).first();
14238         
14239         if(this.loading === null){
14240             this.list.createChild({
14241                 tag: 'div',
14242                 cls: 'loading roo-select2-more-results roo-select2-active',
14243                 html: 'Loading more results...'
14244             });
14245             
14246             this.loading = this.list.select('.loading', true).first();
14247             
14248             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14249             
14250             this.loading.hide();
14251         }
14252         
14253         this.loading.show();
14254         
14255         var _combo = this;
14256         
14257         this.page++;
14258         this.loadNext = true;
14259         
14260         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14261         
14262         return;
14263     },
14264     
14265     addItem : function(o)
14266     {   
14267         var dv = ''; // display value
14268         
14269         if (this.displayField) {
14270             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14271         } else {
14272             // this is an error condition!!!
14273             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14274         }
14275         
14276         if(!dv.length){
14277             return;
14278         }
14279         
14280         var choice = this.choices.createChild({
14281             tag: 'li',
14282             cls: 'roo-select2-search-choice',
14283             cn: [
14284                 {
14285                     tag: 'div',
14286                     html: dv
14287                 },
14288                 {
14289                     tag: 'a',
14290                     href: '#',
14291                     cls: 'roo-select2-search-choice-close',
14292                     tabindex: '-1'
14293                 }
14294             ]
14295             
14296         }, this.searchField);
14297         
14298         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14299         
14300         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14301         
14302         this.item.push(o);
14303         
14304         this.lastData = o;
14305         
14306         this.syncValue();
14307         
14308         this.inputEl().dom.value = '';
14309         
14310         this.validate();
14311     },
14312     
14313     onRemoveItem : function(e, _self, o)
14314     {
14315         e.preventDefault();
14316         
14317         this.lastItem = Roo.apply([], this.item);
14318         
14319         var index = this.item.indexOf(o.data) * 1;
14320         
14321         if( index < 0){
14322             Roo.log('not this item?!');
14323             return;
14324         }
14325         
14326         this.item.splice(index, 1);
14327         o.item.remove();
14328         
14329         this.syncValue();
14330         
14331         this.fireEvent('remove', this, e);
14332         
14333         this.validate();
14334         
14335     },
14336     
14337     syncValue : function()
14338     {
14339         if(!this.item.length){
14340             this.clearValue();
14341             return;
14342         }
14343             
14344         var value = [];
14345         var _this = this;
14346         Roo.each(this.item, function(i){
14347             if(_this.valueField){
14348                 value.push(i[_this.valueField]);
14349                 return;
14350             }
14351
14352             value.push(i);
14353         });
14354
14355         this.value = value.join(',');
14356
14357         if(this.hiddenField){
14358             this.hiddenField.dom.value = this.value;
14359         }
14360         
14361         this.store.fireEvent("datachanged", this.store);
14362         
14363         this.validate();
14364     },
14365     
14366     clearItem : function()
14367     {
14368         if(!this.multiple){
14369             return;
14370         }
14371         
14372         this.item = [];
14373         
14374         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14375            c.remove();
14376         });
14377         
14378         this.syncValue();
14379         
14380         this.validate();
14381         
14382         if(this.tickable && !Roo.isTouch){
14383             this.view.refresh();
14384         }
14385     },
14386     
14387     inputEl: function ()
14388     {
14389         if(Roo.isIOS && this.useNativeIOS){
14390             return this.el.select('select.roo-ios-select', true).first();
14391         }
14392         
14393         if(Roo.isTouch && this.mobileTouchView){
14394             return this.el.select('input.form-control',true).first();
14395         }
14396         
14397         if(this.tickable){
14398             return this.searchField;
14399         }
14400         
14401         return this.el.select('input.form-control',true).first();
14402     },
14403     
14404     onTickableFooterButtonClick : function(e, btn, el)
14405     {
14406         e.preventDefault();
14407         
14408         this.lastItem = Roo.apply([], this.item);
14409         
14410         if(btn && btn.name == 'cancel'){
14411             this.tickItems = Roo.apply([], this.item);
14412             this.collapse();
14413             return;
14414         }
14415         
14416         this.clearItem();
14417         
14418         var _this = this;
14419         
14420         Roo.each(this.tickItems, function(o){
14421             _this.addItem(o);
14422         });
14423         
14424         this.collapse();
14425         
14426     },
14427     
14428     validate : function()
14429     {
14430         var v = this.getRawValue();
14431         
14432         if(this.multiple){
14433             v = this.getValue();
14434         }
14435         
14436         if(this.disabled || this.allowBlank || v.length){
14437             this.markValid();
14438             return true;
14439         }
14440         
14441         this.markInvalid();
14442         return false;
14443     },
14444     
14445     tickableInputEl : function()
14446     {
14447         if(!this.tickable || !this.editable){
14448             return this.inputEl();
14449         }
14450         
14451         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14452     },
14453     
14454     
14455     getAutoCreateTouchView : function()
14456     {
14457         var id = Roo.id();
14458         
14459         var cfg = {
14460             cls: 'form-group' //input-group
14461         };
14462         
14463         var input =  {
14464             tag: 'input',
14465             id : id,
14466             type : this.inputType,
14467             cls : 'form-control x-combo-noedit',
14468             autocomplete: 'new-password',
14469             placeholder : this.placeholder || '',
14470             readonly : true
14471         };
14472         
14473         if (this.name) {
14474             input.name = this.name;
14475         }
14476         
14477         if (this.size) {
14478             input.cls += ' input-' + this.size;
14479         }
14480         
14481         if (this.disabled) {
14482             input.disabled = true;
14483         }
14484         
14485         var inputblock = {
14486             cls : '',
14487             cn : [
14488                 input
14489             ]
14490         };
14491         
14492         if(this.before){
14493             inputblock.cls += ' input-group';
14494             
14495             inputblock.cn.unshift({
14496                 tag :'span',
14497                 cls : 'input-group-addon',
14498                 html : this.before
14499             });
14500         }
14501         
14502         if(this.removable && !this.multiple){
14503             inputblock.cls += ' roo-removable';
14504             
14505             inputblock.cn.push({
14506                 tag: 'button',
14507                 html : 'x',
14508                 cls : 'roo-combo-removable-btn close'
14509             });
14510         }
14511
14512         if(this.hasFeedback && !this.allowBlank){
14513             
14514             inputblock.cls += ' has-feedback';
14515             
14516             inputblock.cn.push({
14517                 tag: 'span',
14518                 cls: 'glyphicon form-control-feedback'
14519             });
14520             
14521         }
14522         
14523         if (this.after) {
14524             
14525             inputblock.cls += (this.before) ? '' : ' input-group';
14526             
14527             inputblock.cn.push({
14528                 tag :'span',
14529                 cls : 'input-group-addon',
14530                 html : this.after
14531             });
14532         }
14533
14534         var box = {
14535             tag: 'div',
14536             cn: [
14537                 {
14538                     tag: 'input',
14539                     type : 'hidden',
14540                     cls: 'form-hidden-field'
14541                 },
14542                 inputblock
14543             ]
14544             
14545         };
14546         
14547         if(this.multiple){
14548             box = {
14549                 tag: 'div',
14550                 cn: [
14551                     {
14552                         tag: 'input',
14553                         type : 'hidden',
14554                         cls: 'form-hidden-field'
14555                     },
14556                     {
14557                         tag: 'ul',
14558                         cls: 'roo-select2-choices',
14559                         cn:[
14560                             {
14561                                 tag: 'li',
14562                                 cls: 'roo-select2-search-field',
14563                                 cn: [
14564
14565                                     inputblock
14566                                 ]
14567                             }
14568                         ]
14569                     }
14570                 ]
14571             }
14572         };
14573         
14574         var combobox = {
14575             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14576             cn: [
14577                 box
14578             ]
14579         };
14580         
14581         if(!this.multiple && this.showToggleBtn){
14582             
14583             var caret = {
14584                         tag: 'span',
14585                         cls: 'caret'
14586             };
14587             
14588             if (this.caret != false) {
14589                 caret = {
14590                      tag: 'i',
14591                      cls: 'fa fa-' + this.caret
14592                 };
14593                 
14594             }
14595             
14596             combobox.cn.push({
14597                 tag :'span',
14598                 cls : 'input-group-addon btn dropdown-toggle',
14599                 cn : [
14600                     caret,
14601                     {
14602                         tag: 'span',
14603                         cls: 'combobox-clear',
14604                         cn  : [
14605                             {
14606                                 tag : 'i',
14607                                 cls: 'icon-remove'
14608                             }
14609                         ]
14610                     }
14611                 ]
14612
14613             })
14614         }
14615         
14616         if(this.multiple){
14617             combobox.cls += ' roo-select2-container-multi';
14618         }
14619         
14620         var align = this.labelAlign || this.parentLabelAlign();
14621         
14622         if (align ==='left' && this.fieldLabel.length) {
14623
14624             cfg.cn = [
14625                 {
14626                    tag : 'i',
14627                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14628                    tooltip : 'This field is required'
14629                 },
14630                 {
14631                     tag: 'label',
14632                     cls : 'control-label',
14633                     html : this.fieldLabel
14634
14635                 },
14636                 {
14637                     cls : '', 
14638                     cn: [
14639                         combobox
14640                     ]
14641                 }
14642             ];
14643             
14644             var labelCfg = cfg.cn[1];
14645             var contentCfg = cfg.cn[2];
14646             
14647
14648             if(this.indicatorpos == 'right'){
14649                 cfg.cn = [
14650                     {
14651                         tag: 'label',
14652                         cls : 'control-label',
14653                         html : this.fieldLabel,
14654                         cn : [
14655                             {
14656                                tag : 'i',
14657                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14658                                tooltip : 'This field is required'
14659                             }
14660                         ]
14661                     },
14662                     {
14663                         cls : '', 
14664                         cn: [
14665                             combobox
14666                         ]
14667                     }
14668                 ];
14669             }
14670             
14671             labelCfg = cfg.cn[0];
14672             contentCfg = cfg.cn[2];
14673             
14674             if(this.labelWidth > 12){
14675                 labelCfg.style = "width: " + this.labelWidth + 'px';
14676             }
14677             
14678             if(this.labelWidth < 13 && this.labelmd == 0){
14679                 this.labelmd = this.labelWidth;
14680             }
14681             
14682             if(this.labellg > 0){
14683                 labelCfg.cls += ' col-lg-' + this.labellg;
14684                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14685             }
14686             
14687             if(this.labelmd > 0){
14688                 labelCfg.cls += ' col-md-' + this.labelmd;
14689                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14690             }
14691             
14692             if(this.labelsm > 0){
14693                 labelCfg.cls += ' col-sm-' + this.labelsm;
14694                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14695             }
14696             
14697             if(this.labelxs > 0){
14698                 labelCfg.cls += ' col-xs-' + this.labelxs;
14699                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14700             }
14701                 
14702                 
14703         } else if ( this.fieldLabel.length) {
14704             cfg.cn = [
14705                 {
14706                    tag : 'i',
14707                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14708                    tooltip : 'This field is required'
14709                 },
14710                 {
14711                     tag: 'label',
14712                     cls : 'control-label',
14713                     html : this.fieldLabel
14714
14715                 },
14716                 {
14717                     cls : '', 
14718                     cn: [
14719                         combobox
14720                     ]
14721                 }
14722             ];
14723             
14724             if(this.indicatorpos == 'right'){
14725                 cfg.cn = [
14726                     {
14727                         tag: 'label',
14728                         cls : 'control-label',
14729                         html : this.fieldLabel,
14730                         cn : [
14731                             {
14732                                tag : 'i',
14733                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14734                                tooltip : 'This field is required'
14735                             }
14736                         ]
14737                     },
14738                     {
14739                         cls : '', 
14740                         cn: [
14741                             combobox
14742                         ]
14743                     }
14744                 ];
14745             }
14746         } else {
14747             cfg.cn = combobox;    
14748         }
14749         
14750         
14751         var settings = this;
14752         
14753         ['xs','sm','md','lg'].map(function(size){
14754             if (settings[size]) {
14755                 cfg.cls += ' col-' + size + '-' + settings[size];
14756             }
14757         });
14758         
14759         return cfg;
14760     },
14761     
14762     initTouchView : function()
14763     {
14764         this.renderTouchView();
14765         
14766         this.touchViewEl.on('scroll', function(){
14767             this.el.dom.scrollTop = 0;
14768         }, this);
14769         
14770         this.originalValue = this.getValue();
14771         
14772         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14773         
14774         this.inputEl().on("click", this.showTouchView, this);
14775         if (this.triggerEl) {
14776             this.triggerEl.on("click", this.showTouchView, this);
14777         }
14778         
14779         
14780         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14781         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14782         
14783         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14784         
14785         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14786         this.store.on('load', this.onTouchViewLoad, this);
14787         this.store.on('loadexception', this.onTouchViewLoadException, this);
14788         
14789         if(this.hiddenName){
14790             
14791             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14792             
14793             this.hiddenField.dom.value =
14794                 this.hiddenValue !== undefined ? this.hiddenValue :
14795                 this.value !== undefined ? this.value : '';
14796         
14797             this.el.dom.removeAttribute('name');
14798             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14799         }
14800         
14801         if(this.multiple){
14802             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14803             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14804         }
14805         
14806         if(this.removable && !this.multiple){
14807             var close = this.closeTriggerEl();
14808             if(close){
14809                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14810                 close.on('click', this.removeBtnClick, this, close);
14811             }
14812         }
14813         /*
14814          * fix the bug in Safari iOS8
14815          */
14816         this.inputEl().on("focus", function(e){
14817             document.activeElement.blur();
14818         }, this);
14819         
14820         return;
14821         
14822         
14823     },
14824     
14825     renderTouchView : function()
14826     {
14827         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14828         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14829         
14830         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14831         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14832         
14833         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14834         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14835         this.touchViewBodyEl.setStyle('overflow', 'auto');
14836         
14837         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14838         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14839         
14840         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14841         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14842         
14843     },
14844     
14845     showTouchView : function()
14846     {
14847         if(this.disabled){
14848             return;
14849         }
14850         
14851         this.touchViewHeaderEl.hide();
14852
14853         if(this.modalTitle.length){
14854             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14855             this.touchViewHeaderEl.show();
14856         }
14857
14858         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14859         this.touchViewEl.show();
14860
14861         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14862         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14863                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14864
14865         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14866
14867         if(this.modalTitle.length){
14868             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14869         }
14870         
14871         this.touchViewBodyEl.setHeight(bodyHeight);
14872
14873         if(this.animate){
14874             var _this = this;
14875             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14876         }else{
14877             this.touchViewEl.addClass('in');
14878         }
14879
14880         this.doTouchViewQuery();
14881         
14882     },
14883     
14884     hideTouchView : function()
14885     {
14886         this.touchViewEl.removeClass('in');
14887
14888         if(this.animate){
14889             var _this = this;
14890             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14891         }else{
14892             this.touchViewEl.setStyle('display', 'none');
14893         }
14894         
14895     },
14896     
14897     setTouchViewValue : function()
14898     {
14899         if(this.multiple){
14900             this.clearItem();
14901         
14902             var _this = this;
14903
14904             Roo.each(this.tickItems, function(o){
14905                 this.addItem(o);
14906             }, this);
14907         }
14908         
14909         this.hideTouchView();
14910     },
14911     
14912     doTouchViewQuery : function()
14913     {
14914         var qe = {
14915             query: '',
14916             forceAll: true,
14917             combo: this,
14918             cancel:false
14919         };
14920         
14921         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14922             return false;
14923         }
14924         
14925         if(!this.alwaysQuery || this.mode == 'local'){
14926             this.onTouchViewLoad();
14927             return;
14928         }
14929         
14930         this.store.load();
14931     },
14932     
14933     onTouchViewBeforeLoad : function(combo,opts)
14934     {
14935         return;
14936     },
14937
14938     // private
14939     onTouchViewLoad : function()
14940     {
14941         if(this.store.getCount() < 1){
14942             this.onTouchViewEmptyResults();
14943             return;
14944         }
14945         
14946         this.clearTouchView();
14947         
14948         var rawValue = this.getRawValue();
14949         
14950         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14951         
14952         this.tickItems = [];
14953         
14954         this.store.data.each(function(d, rowIndex){
14955             var row = this.touchViewListGroup.createChild(template);
14956             
14957             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14958                 row.addClass(d.data.cls);
14959             }
14960             
14961             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14962                 var cfg = {
14963                     data : d.data,
14964                     html : d.data[this.displayField]
14965                 };
14966                 
14967                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14968                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14969                 }
14970             }
14971             row.removeClass('selected');
14972             if(!this.multiple && this.valueField &&
14973                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14974             {
14975                 // radio buttons..
14976                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14977                 row.addClass('selected');
14978             }
14979             
14980             if(this.multiple && this.valueField &&
14981                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14982             {
14983                 
14984                 // checkboxes...
14985                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14986                 this.tickItems.push(d.data);
14987             }
14988             
14989             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14990             
14991         }, this);
14992         
14993         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14994         
14995         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14996
14997         if(this.modalTitle.length){
14998             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14999         }
15000
15001         var listHeight = this.touchViewListGroup.getHeight();
15002         
15003         var _this = this;
15004         
15005         if(firstChecked && listHeight > bodyHeight){
15006             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15007         }
15008         
15009     },
15010     
15011     onTouchViewLoadException : function()
15012     {
15013         this.hideTouchView();
15014     },
15015     
15016     onTouchViewEmptyResults : function()
15017     {
15018         this.clearTouchView();
15019         
15020         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15021         
15022         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15023         
15024     },
15025     
15026     clearTouchView : function()
15027     {
15028         this.touchViewListGroup.dom.innerHTML = '';
15029     },
15030     
15031     onTouchViewClick : function(e, el, o)
15032     {
15033         e.preventDefault();
15034         
15035         var row = o.row;
15036         var rowIndex = o.rowIndex;
15037         
15038         var r = this.store.getAt(rowIndex);
15039         
15040         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15041             
15042             if(!this.multiple){
15043                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15044                     c.dom.removeAttribute('checked');
15045                 }, this);
15046
15047                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15048
15049                 this.setFromData(r.data);
15050
15051                 var close = this.closeTriggerEl();
15052
15053                 if(close){
15054                     close.show();
15055                 }
15056
15057                 this.hideTouchView();
15058
15059                 this.fireEvent('select', this, r, rowIndex);
15060
15061                 return;
15062             }
15063
15064             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15065                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15066                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15067                 return;
15068             }
15069
15070             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15071             this.addItem(r.data);
15072             this.tickItems.push(r.data);
15073         }
15074     },
15075     
15076     getAutoCreateNativeIOS : function()
15077     {
15078         var cfg = {
15079             cls: 'form-group' //input-group,
15080         };
15081         
15082         var combobox =  {
15083             tag: 'select',
15084             cls : 'roo-ios-select'
15085         };
15086         
15087         if (this.name) {
15088             combobox.name = this.name;
15089         }
15090         
15091         if (this.disabled) {
15092             combobox.disabled = true;
15093         }
15094         
15095         var settings = this;
15096         
15097         ['xs','sm','md','lg'].map(function(size){
15098             if (settings[size]) {
15099                 cfg.cls += ' col-' + size + '-' + settings[size];
15100             }
15101         });
15102         
15103         cfg.cn = combobox;
15104         
15105         return cfg;
15106         
15107     },
15108     
15109     initIOSView : function()
15110     {
15111         this.store.on('load', this.onIOSViewLoad, this);
15112         
15113         return;
15114     },
15115     
15116     onIOSViewLoad : function()
15117     {
15118         if(this.store.getCount() < 1){
15119             return;
15120         }
15121         
15122         this.clearIOSView();
15123         
15124         if(this.allowBlank) {
15125             
15126             var default_text = '-- SELECT --';
15127             
15128             var opt = this.inputEl().createChild({
15129                 tag: 'option',
15130                 value : 0,
15131                 html : default_text
15132             });
15133             
15134             var o = {};
15135             o[this.valueField] = 0;
15136             o[this.displayField] = default_text;
15137             
15138             this.ios_options.push({
15139                 data : o,
15140                 el : opt
15141             });
15142             
15143         }
15144         
15145         this.store.data.each(function(d, rowIndex){
15146             
15147             var html = '';
15148             
15149             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15150                 html = d.data[this.displayField];
15151             }
15152             
15153             var value = '';
15154             
15155             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15156                 value = d.data[this.valueField];
15157             }
15158             
15159             var option = {
15160                 tag: 'option',
15161                 value : value,
15162                 html : html
15163             };
15164             
15165             if(this.value == d.data[this.valueField]){
15166                 option['selected'] = true;
15167             }
15168             
15169             var opt = this.inputEl().createChild(option);
15170             
15171             this.ios_options.push({
15172                 data : d.data,
15173                 el : opt
15174             });
15175             
15176         }, this);
15177         
15178         this.inputEl().on('change', function(){
15179            this.fireEvent('select', this);
15180         }, this);
15181         
15182     },
15183     
15184     clearIOSView: function()
15185     {
15186         this.inputEl().dom.innerHTML = '';
15187         
15188         this.ios_options = [];
15189     },
15190     
15191     setIOSValue: function(v)
15192     {
15193         this.value = v;
15194         
15195         if(!this.ios_options){
15196             return;
15197         }
15198         
15199         Roo.each(this.ios_options, function(opts){
15200            
15201            opts.el.dom.removeAttribute('selected');
15202            
15203            if(opts.data[this.valueField] != v){
15204                return;
15205            }
15206            
15207            opts.el.dom.setAttribute('selected', true);
15208            
15209         }, this);
15210     }
15211
15212     /** 
15213     * @cfg {Boolean} grow 
15214     * @hide 
15215     */
15216     /** 
15217     * @cfg {Number} growMin 
15218     * @hide 
15219     */
15220     /** 
15221     * @cfg {Number} growMax 
15222     * @hide 
15223     */
15224     /**
15225      * @hide
15226      * @method autoSize
15227      */
15228 });
15229
15230 Roo.apply(Roo.bootstrap.ComboBox,  {
15231     
15232     header : {
15233         tag: 'div',
15234         cls: 'modal-header',
15235         cn: [
15236             {
15237                 tag: 'h4',
15238                 cls: 'modal-title'
15239             }
15240         ]
15241     },
15242     
15243     body : {
15244         tag: 'div',
15245         cls: 'modal-body',
15246         cn: [
15247             {
15248                 tag: 'ul',
15249                 cls: 'list-group'
15250             }
15251         ]
15252     },
15253     
15254     listItemRadio : {
15255         tag: 'li',
15256         cls: 'list-group-item',
15257         cn: [
15258             {
15259                 tag: 'span',
15260                 cls: 'roo-combobox-list-group-item-value'
15261             },
15262             {
15263                 tag: 'div',
15264                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15265                 cn: [
15266                     {
15267                         tag: 'input',
15268                         type: 'radio'
15269                     },
15270                     {
15271                         tag: 'label'
15272                     }
15273                 ]
15274             }
15275         ]
15276     },
15277     
15278     listItemCheckbox : {
15279         tag: 'li',
15280         cls: 'list-group-item',
15281         cn: [
15282             {
15283                 tag: 'span',
15284                 cls: 'roo-combobox-list-group-item-value'
15285             },
15286             {
15287                 tag: 'div',
15288                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15289                 cn: [
15290                     {
15291                         tag: 'input',
15292                         type: 'checkbox'
15293                     },
15294                     {
15295                         tag: 'label'
15296                     }
15297                 ]
15298             }
15299         ]
15300     },
15301     
15302     emptyResult : {
15303         tag: 'div',
15304         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15305     },
15306     
15307     footer : {
15308         tag: 'div',
15309         cls: 'modal-footer',
15310         cn: [
15311             {
15312                 tag: 'div',
15313                 cls: 'row',
15314                 cn: [
15315                     {
15316                         tag: 'div',
15317                         cls: 'col-xs-6 text-left',
15318                         cn: {
15319                             tag: 'button',
15320                             cls: 'btn btn-danger roo-touch-view-cancel',
15321                             html: 'Cancel'
15322                         }
15323                     },
15324                     {
15325                         tag: 'div',
15326                         cls: 'col-xs-6 text-right',
15327                         cn: {
15328                             tag: 'button',
15329                             cls: 'btn btn-success roo-touch-view-ok',
15330                             html: 'OK'
15331                         }
15332                     }
15333                 ]
15334             }
15335         ]
15336         
15337     }
15338 });
15339
15340 Roo.apply(Roo.bootstrap.ComboBox,  {
15341     
15342     touchViewTemplate : {
15343         tag: 'div',
15344         cls: 'modal fade roo-combobox-touch-view',
15345         cn: [
15346             {
15347                 tag: 'div',
15348                 cls: 'modal-dialog',
15349                 style : 'position:fixed', // we have to fix position....
15350                 cn: [
15351                     {
15352                         tag: 'div',
15353                         cls: 'modal-content',
15354                         cn: [
15355                             Roo.bootstrap.ComboBox.header,
15356                             Roo.bootstrap.ComboBox.body,
15357                             Roo.bootstrap.ComboBox.footer
15358                         ]
15359                     }
15360                 ]
15361             }
15362         ]
15363     }
15364 });/*
15365  * Based on:
15366  * Ext JS Library 1.1.1
15367  * Copyright(c) 2006-2007, Ext JS, LLC.
15368  *
15369  * Originally Released Under LGPL - original licence link has changed is not relivant.
15370  *
15371  * Fork - LGPL
15372  * <script type="text/javascript">
15373  */
15374
15375 /**
15376  * @class Roo.View
15377  * @extends Roo.util.Observable
15378  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15379  * This class also supports single and multi selection modes. <br>
15380  * Create a data model bound view:
15381  <pre><code>
15382  var store = new Roo.data.Store(...);
15383
15384  var view = new Roo.View({
15385     el : "my-element",
15386     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15387  
15388     singleSelect: true,
15389     selectedClass: "ydataview-selected",
15390     store: store
15391  });
15392
15393  // listen for node click?
15394  view.on("click", function(vw, index, node, e){
15395  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15396  });
15397
15398  // load XML data
15399  dataModel.load("foobar.xml");
15400  </code></pre>
15401  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15402  * <br><br>
15403  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15404  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15405  * 
15406  * Note: old style constructor is still suported (container, template, config)
15407  * 
15408  * @constructor
15409  * Create a new View
15410  * @param {Object} config The config object
15411  * 
15412  */
15413 Roo.View = function(config, depreciated_tpl, depreciated_config){
15414     
15415     this.parent = false;
15416     
15417     if (typeof(depreciated_tpl) == 'undefined') {
15418         // new way.. - universal constructor.
15419         Roo.apply(this, config);
15420         this.el  = Roo.get(this.el);
15421     } else {
15422         // old format..
15423         this.el  = Roo.get(config);
15424         this.tpl = depreciated_tpl;
15425         Roo.apply(this, depreciated_config);
15426     }
15427     this.wrapEl  = this.el.wrap().wrap();
15428     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15429     
15430     
15431     if(typeof(this.tpl) == "string"){
15432         this.tpl = new Roo.Template(this.tpl);
15433     } else {
15434         // support xtype ctors..
15435         this.tpl = new Roo.factory(this.tpl, Roo);
15436     }
15437     
15438     
15439     this.tpl.compile();
15440     
15441     /** @private */
15442     this.addEvents({
15443         /**
15444          * @event beforeclick
15445          * Fires before a click is processed. Returns false to cancel the default action.
15446          * @param {Roo.View} this
15447          * @param {Number} index The index of the target node
15448          * @param {HTMLElement} node The target node
15449          * @param {Roo.EventObject} e The raw event object
15450          */
15451             "beforeclick" : true,
15452         /**
15453          * @event click
15454          * Fires when a template node is clicked.
15455          * @param {Roo.View} this
15456          * @param {Number} index The index of the target node
15457          * @param {HTMLElement} node The target node
15458          * @param {Roo.EventObject} e The raw event object
15459          */
15460             "click" : true,
15461         /**
15462          * @event dblclick
15463          * Fires when a template node is double clicked.
15464          * @param {Roo.View} this
15465          * @param {Number} index The index of the target node
15466          * @param {HTMLElement} node The target node
15467          * @param {Roo.EventObject} e The raw event object
15468          */
15469             "dblclick" : true,
15470         /**
15471          * @event contextmenu
15472          * Fires when a template node is right clicked.
15473          * @param {Roo.View} this
15474          * @param {Number} index The index of the target node
15475          * @param {HTMLElement} node The target node
15476          * @param {Roo.EventObject} e The raw event object
15477          */
15478             "contextmenu" : true,
15479         /**
15480          * @event selectionchange
15481          * Fires when the selected nodes change.
15482          * @param {Roo.View} this
15483          * @param {Array} selections Array of the selected nodes
15484          */
15485             "selectionchange" : true,
15486     
15487         /**
15488          * @event beforeselect
15489          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15490          * @param {Roo.View} this
15491          * @param {HTMLElement} node The node to be selected
15492          * @param {Array} selections Array of currently selected nodes
15493          */
15494             "beforeselect" : true,
15495         /**
15496          * @event preparedata
15497          * Fires on every row to render, to allow you to change the data.
15498          * @param {Roo.View} this
15499          * @param {Object} data to be rendered (change this)
15500          */
15501           "preparedata" : true
15502           
15503           
15504         });
15505
15506
15507
15508     this.el.on({
15509         "click": this.onClick,
15510         "dblclick": this.onDblClick,
15511         "contextmenu": this.onContextMenu,
15512         scope:this
15513     });
15514
15515     this.selections = [];
15516     this.nodes = [];
15517     this.cmp = new Roo.CompositeElementLite([]);
15518     if(this.store){
15519         this.store = Roo.factory(this.store, Roo.data);
15520         this.setStore(this.store, true);
15521     }
15522     
15523     if ( this.footer && this.footer.xtype) {
15524            
15525          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15526         
15527         this.footer.dataSource = this.store;
15528         this.footer.container = fctr;
15529         this.footer = Roo.factory(this.footer, Roo);
15530         fctr.insertFirst(this.el);
15531         
15532         // this is a bit insane - as the paging toolbar seems to detach the el..
15533 //        dom.parentNode.parentNode.parentNode
15534          // they get detached?
15535     }
15536     
15537     
15538     Roo.View.superclass.constructor.call(this);
15539     
15540     
15541 };
15542
15543 Roo.extend(Roo.View, Roo.util.Observable, {
15544     
15545      /**
15546      * @cfg {Roo.data.Store} store Data store to load data from.
15547      */
15548     store : false,
15549     
15550     /**
15551      * @cfg {String|Roo.Element} el The container element.
15552      */
15553     el : '',
15554     
15555     /**
15556      * @cfg {String|Roo.Template} tpl The template used by this View 
15557      */
15558     tpl : false,
15559     /**
15560      * @cfg {String} dataName the named area of the template to use as the data area
15561      *                          Works with domtemplates roo-name="name"
15562      */
15563     dataName: false,
15564     /**
15565      * @cfg {String} selectedClass The css class to add to selected nodes
15566      */
15567     selectedClass : "x-view-selected",
15568      /**
15569      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15570      */
15571     emptyText : "",
15572     
15573     /**
15574      * @cfg {String} text to display on mask (default Loading)
15575      */
15576     mask : false,
15577     /**
15578      * @cfg {Boolean} multiSelect Allow multiple selection
15579      */
15580     multiSelect : false,
15581     /**
15582      * @cfg {Boolean} singleSelect Allow single selection
15583      */
15584     singleSelect:  false,
15585     
15586     /**
15587      * @cfg {Boolean} toggleSelect - selecting 
15588      */
15589     toggleSelect : false,
15590     
15591     /**
15592      * @cfg {Boolean} tickable - selecting 
15593      */
15594     tickable : false,
15595     
15596     /**
15597      * Returns the element this view is bound to.
15598      * @return {Roo.Element}
15599      */
15600     getEl : function(){
15601         return this.wrapEl;
15602     },
15603     
15604     
15605
15606     /**
15607      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15608      */
15609     refresh : function(){
15610         //Roo.log('refresh');
15611         var t = this.tpl;
15612         
15613         // if we are using something like 'domtemplate', then
15614         // the what gets used is:
15615         // t.applySubtemplate(NAME, data, wrapping data..)
15616         // the outer template then get' applied with
15617         //     the store 'extra data'
15618         // and the body get's added to the
15619         //      roo-name="data" node?
15620         //      <span class='roo-tpl-{name}'></span> ?????
15621         
15622         
15623         
15624         this.clearSelections();
15625         this.el.update("");
15626         var html = [];
15627         var records = this.store.getRange();
15628         if(records.length < 1) {
15629             
15630             // is this valid??  = should it render a template??
15631             
15632             this.el.update(this.emptyText);
15633             return;
15634         }
15635         var el = this.el;
15636         if (this.dataName) {
15637             this.el.update(t.apply(this.store.meta)); //????
15638             el = this.el.child('.roo-tpl-' + this.dataName);
15639         }
15640         
15641         for(var i = 0, len = records.length; i < len; i++){
15642             var data = this.prepareData(records[i].data, i, records[i]);
15643             this.fireEvent("preparedata", this, data, i, records[i]);
15644             
15645             var d = Roo.apply({}, data);
15646             
15647             if(this.tickable){
15648                 Roo.apply(d, {'roo-id' : Roo.id()});
15649                 
15650                 var _this = this;
15651             
15652                 Roo.each(this.parent.item, function(item){
15653                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15654                         return;
15655                     }
15656                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15657                 });
15658             }
15659             
15660             html[html.length] = Roo.util.Format.trim(
15661                 this.dataName ?
15662                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15663                     t.apply(d)
15664             );
15665         }
15666         
15667         
15668         
15669         el.update(html.join(""));
15670         this.nodes = el.dom.childNodes;
15671         this.updateIndexes(0);
15672     },
15673     
15674
15675     /**
15676      * Function to override to reformat the data that is sent to
15677      * the template for each node.
15678      * DEPRICATED - use the preparedata event handler.
15679      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15680      * a JSON object for an UpdateManager bound view).
15681      */
15682     prepareData : function(data, index, record)
15683     {
15684         this.fireEvent("preparedata", this, data, index, record);
15685         return data;
15686     },
15687
15688     onUpdate : function(ds, record){
15689         // Roo.log('on update');   
15690         this.clearSelections();
15691         var index = this.store.indexOf(record);
15692         var n = this.nodes[index];
15693         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15694         n.parentNode.removeChild(n);
15695         this.updateIndexes(index, index);
15696     },
15697
15698     
15699     
15700 // --------- FIXME     
15701     onAdd : function(ds, records, index)
15702     {
15703         //Roo.log(['on Add', ds, records, index] );        
15704         this.clearSelections();
15705         if(this.nodes.length == 0){
15706             this.refresh();
15707             return;
15708         }
15709         var n = this.nodes[index];
15710         for(var i = 0, len = records.length; i < len; i++){
15711             var d = this.prepareData(records[i].data, i, records[i]);
15712             if(n){
15713                 this.tpl.insertBefore(n, d);
15714             }else{
15715                 
15716                 this.tpl.append(this.el, d);
15717             }
15718         }
15719         this.updateIndexes(index);
15720     },
15721
15722     onRemove : function(ds, record, index){
15723        // Roo.log('onRemove');
15724         this.clearSelections();
15725         var el = this.dataName  ?
15726             this.el.child('.roo-tpl-' + this.dataName) :
15727             this.el; 
15728         
15729         el.dom.removeChild(this.nodes[index]);
15730         this.updateIndexes(index);
15731     },
15732
15733     /**
15734      * Refresh an individual node.
15735      * @param {Number} index
15736      */
15737     refreshNode : function(index){
15738         this.onUpdate(this.store, this.store.getAt(index));
15739     },
15740
15741     updateIndexes : function(startIndex, endIndex){
15742         var ns = this.nodes;
15743         startIndex = startIndex || 0;
15744         endIndex = endIndex || ns.length - 1;
15745         for(var i = startIndex; i <= endIndex; i++){
15746             ns[i].nodeIndex = i;
15747         }
15748     },
15749
15750     /**
15751      * Changes the data store this view uses and refresh the view.
15752      * @param {Store} store
15753      */
15754     setStore : function(store, initial){
15755         if(!initial && this.store){
15756             this.store.un("datachanged", this.refresh);
15757             this.store.un("add", this.onAdd);
15758             this.store.un("remove", this.onRemove);
15759             this.store.un("update", this.onUpdate);
15760             this.store.un("clear", this.refresh);
15761             this.store.un("beforeload", this.onBeforeLoad);
15762             this.store.un("load", this.onLoad);
15763             this.store.un("loadexception", this.onLoad);
15764         }
15765         if(store){
15766           
15767             store.on("datachanged", this.refresh, this);
15768             store.on("add", this.onAdd, this);
15769             store.on("remove", this.onRemove, this);
15770             store.on("update", this.onUpdate, this);
15771             store.on("clear", this.refresh, this);
15772             store.on("beforeload", this.onBeforeLoad, this);
15773             store.on("load", this.onLoad, this);
15774             store.on("loadexception", this.onLoad, this);
15775         }
15776         
15777         if(store){
15778             this.refresh();
15779         }
15780     },
15781     /**
15782      * onbeforeLoad - masks the loading area.
15783      *
15784      */
15785     onBeforeLoad : function(store,opts)
15786     {
15787          //Roo.log('onBeforeLoad');   
15788         if (!opts.add) {
15789             this.el.update("");
15790         }
15791         this.el.mask(this.mask ? this.mask : "Loading" ); 
15792     },
15793     onLoad : function ()
15794     {
15795         this.el.unmask();
15796     },
15797     
15798
15799     /**
15800      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15801      * @param {HTMLElement} node
15802      * @return {HTMLElement} The template node
15803      */
15804     findItemFromChild : function(node){
15805         var el = this.dataName  ?
15806             this.el.child('.roo-tpl-' + this.dataName,true) :
15807             this.el.dom; 
15808         
15809         if(!node || node.parentNode == el){
15810                     return node;
15811             }
15812             var p = node.parentNode;
15813             while(p && p != el){
15814             if(p.parentNode == el){
15815                 return p;
15816             }
15817             p = p.parentNode;
15818         }
15819             return null;
15820     },
15821
15822     /** @ignore */
15823     onClick : function(e){
15824         var item = this.findItemFromChild(e.getTarget());
15825         if(item){
15826             var index = this.indexOf(item);
15827             if(this.onItemClick(item, index, e) !== false){
15828                 this.fireEvent("click", this, index, item, e);
15829             }
15830         }else{
15831             this.clearSelections();
15832         }
15833     },
15834
15835     /** @ignore */
15836     onContextMenu : function(e){
15837         var item = this.findItemFromChild(e.getTarget());
15838         if(item){
15839             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15840         }
15841     },
15842
15843     /** @ignore */
15844     onDblClick : function(e){
15845         var item = this.findItemFromChild(e.getTarget());
15846         if(item){
15847             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15848         }
15849     },
15850
15851     onItemClick : function(item, index, e)
15852     {
15853         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15854             return false;
15855         }
15856         if (this.toggleSelect) {
15857             var m = this.isSelected(item) ? 'unselect' : 'select';
15858             //Roo.log(m);
15859             var _t = this;
15860             _t[m](item, true, false);
15861             return true;
15862         }
15863         if(this.multiSelect || this.singleSelect){
15864             if(this.multiSelect && e.shiftKey && this.lastSelection){
15865                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15866             }else{
15867                 this.select(item, this.multiSelect && e.ctrlKey);
15868                 this.lastSelection = item;
15869             }
15870             
15871             if(!this.tickable){
15872                 e.preventDefault();
15873             }
15874             
15875         }
15876         return true;
15877     },
15878
15879     /**
15880      * Get the number of selected nodes.
15881      * @return {Number}
15882      */
15883     getSelectionCount : function(){
15884         return this.selections.length;
15885     },
15886
15887     /**
15888      * Get the currently selected nodes.
15889      * @return {Array} An array of HTMLElements
15890      */
15891     getSelectedNodes : function(){
15892         return this.selections;
15893     },
15894
15895     /**
15896      * Get the indexes of the selected nodes.
15897      * @return {Array}
15898      */
15899     getSelectedIndexes : function(){
15900         var indexes = [], s = this.selections;
15901         for(var i = 0, len = s.length; i < len; i++){
15902             indexes.push(s[i].nodeIndex);
15903         }
15904         return indexes;
15905     },
15906
15907     /**
15908      * Clear all selections
15909      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15910      */
15911     clearSelections : function(suppressEvent){
15912         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15913             this.cmp.elements = this.selections;
15914             this.cmp.removeClass(this.selectedClass);
15915             this.selections = [];
15916             if(!suppressEvent){
15917                 this.fireEvent("selectionchange", this, this.selections);
15918             }
15919         }
15920     },
15921
15922     /**
15923      * Returns true if the passed node is selected
15924      * @param {HTMLElement/Number} node The node or node index
15925      * @return {Boolean}
15926      */
15927     isSelected : function(node){
15928         var s = this.selections;
15929         if(s.length < 1){
15930             return false;
15931         }
15932         node = this.getNode(node);
15933         return s.indexOf(node) !== -1;
15934     },
15935
15936     /**
15937      * Selects nodes.
15938      * @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
15939      * @param {Boolean} keepExisting (optional) true to keep existing selections
15940      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15941      */
15942     select : function(nodeInfo, keepExisting, suppressEvent){
15943         if(nodeInfo instanceof Array){
15944             if(!keepExisting){
15945                 this.clearSelections(true);
15946             }
15947             for(var i = 0, len = nodeInfo.length; i < len; i++){
15948                 this.select(nodeInfo[i], true, true);
15949             }
15950             return;
15951         } 
15952         var node = this.getNode(nodeInfo);
15953         if(!node || this.isSelected(node)){
15954             return; // already selected.
15955         }
15956         if(!keepExisting){
15957             this.clearSelections(true);
15958         }
15959         
15960         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15961             Roo.fly(node).addClass(this.selectedClass);
15962             this.selections.push(node);
15963             if(!suppressEvent){
15964                 this.fireEvent("selectionchange", this, this.selections);
15965             }
15966         }
15967         
15968         
15969     },
15970       /**
15971      * Unselects nodes.
15972      * @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
15973      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15974      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15975      */
15976     unselect : function(nodeInfo, keepExisting, suppressEvent)
15977     {
15978         if(nodeInfo instanceof Array){
15979             Roo.each(this.selections, function(s) {
15980                 this.unselect(s, nodeInfo);
15981             }, this);
15982             return;
15983         }
15984         var node = this.getNode(nodeInfo);
15985         if(!node || !this.isSelected(node)){
15986             //Roo.log("not selected");
15987             return; // not selected.
15988         }
15989         // fireevent???
15990         var ns = [];
15991         Roo.each(this.selections, function(s) {
15992             if (s == node ) {
15993                 Roo.fly(node).removeClass(this.selectedClass);
15994
15995                 return;
15996             }
15997             ns.push(s);
15998         },this);
15999         
16000         this.selections= ns;
16001         this.fireEvent("selectionchange", this, this.selections);
16002     },
16003
16004     /**
16005      * Gets a template node.
16006      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16007      * @return {HTMLElement} The node or null if it wasn't found
16008      */
16009     getNode : function(nodeInfo){
16010         if(typeof nodeInfo == "string"){
16011             return document.getElementById(nodeInfo);
16012         }else if(typeof nodeInfo == "number"){
16013             return this.nodes[nodeInfo];
16014         }
16015         return nodeInfo;
16016     },
16017
16018     /**
16019      * Gets a range template nodes.
16020      * @param {Number} startIndex
16021      * @param {Number} endIndex
16022      * @return {Array} An array of nodes
16023      */
16024     getNodes : function(start, end){
16025         var ns = this.nodes;
16026         start = start || 0;
16027         end = typeof end == "undefined" ? ns.length - 1 : end;
16028         var nodes = [];
16029         if(start <= end){
16030             for(var i = start; i <= end; i++){
16031                 nodes.push(ns[i]);
16032             }
16033         } else{
16034             for(var i = start; i >= end; i--){
16035                 nodes.push(ns[i]);
16036             }
16037         }
16038         return nodes;
16039     },
16040
16041     /**
16042      * Finds the index of the passed node
16043      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16044      * @return {Number} The index of the node or -1
16045      */
16046     indexOf : function(node){
16047         node = this.getNode(node);
16048         if(typeof node.nodeIndex == "number"){
16049             return node.nodeIndex;
16050         }
16051         var ns = this.nodes;
16052         for(var i = 0, len = ns.length; i < len; i++){
16053             if(ns[i] == node){
16054                 return i;
16055             }
16056         }
16057         return -1;
16058     }
16059 });
16060 /*
16061  * - LGPL
16062  *
16063  * based on jquery fullcalendar
16064  * 
16065  */
16066
16067 Roo.bootstrap = Roo.bootstrap || {};
16068 /**
16069  * @class Roo.bootstrap.Calendar
16070  * @extends Roo.bootstrap.Component
16071  * Bootstrap Calendar class
16072  * @cfg {Boolean} loadMask (true|false) default false
16073  * @cfg {Object} header generate the user specific header of the calendar, default false
16074
16075  * @constructor
16076  * Create a new Container
16077  * @param {Object} config The config object
16078  */
16079
16080
16081
16082 Roo.bootstrap.Calendar = function(config){
16083     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16084      this.addEvents({
16085         /**
16086              * @event select
16087              * Fires when a date is selected
16088              * @param {DatePicker} this
16089              * @param {Date} date The selected date
16090              */
16091         'select': true,
16092         /**
16093              * @event monthchange
16094              * Fires when the displayed month changes 
16095              * @param {DatePicker} this
16096              * @param {Date} date The selected month
16097              */
16098         'monthchange': true,
16099         /**
16100              * @event evententer
16101              * Fires when mouse over an event
16102              * @param {Calendar} this
16103              * @param {event} Event
16104              */
16105         'evententer': true,
16106         /**
16107              * @event eventleave
16108              * Fires when the mouse leaves an
16109              * @param {Calendar} this
16110              * @param {event}
16111              */
16112         'eventleave': true,
16113         /**
16114              * @event eventclick
16115              * Fires when the mouse click an
16116              * @param {Calendar} this
16117              * @param {event}
16118              */
16119         'eventclick': true
16120         
16121     });
16122
16123 };
16124
16125 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16126     
16127      /**
16128      * @cfg {Number} startDay
16129      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16130      */
16131     startDay : 0,
16132     
16133     loadMask : false,
16134     
16135     header : false,
16136       
16137     getAutoCreate : function(){
16138         
16139         
16140         var fc_button = function(name, corner, style, content ) {
16141             return Roo.apply({},{
16142                 tag : 'span',
16143                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16144                          (corner.length ?
16145                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16146                             ''
16147                         ),
16148                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16149                 unselectable: 'on'
16150             });
16151         };
16152         
16153         var header = {};
16154         
16155         if(!this.header){
16156             header = {
16157                 tag : 'table',
16158                 cls : 'fc-header',
16159                 style : 'width:100%',
16160                 cn : [
16161                     {
16162                         tag: 'tr',
16163                         cn : [
16164                             {
16165                                 tag : 'td',
16166                                 cls : 'fc-header-left',
16167                                 cn : [
16168                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16169                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16170                                     { tag: 'span', cls: 'fc-header-space' },
16171                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16172
16173
16174                                 ]
16175                             },
16176
16177                             {
16178                                 tag : 'td',
16179                                 cls : 'fc-header-center',
16180                                 cn : [
16181                                     {
16182                                         tag: 'span',
16183                                         cls: 'fc-header-title',
16184                                         cn : {
16185                                             tag: 'H2',
16186                                             html : 'month / year'
16187                                         }
16188                                     }
16189
16190                                 ]
16191                             },
16192                             {
16193                                 tag : 'td',
16194                                 cls : 'fc-header-right',
16195                                 cn : [
16196                               /*      fc_button('month', 'left', '', 'month' ),
16197                                     fc_button('week', '', '', 'week' ),
16198                                     fc_button('day', 'right', '', 'day' )
16199                                 */    
16200
16201                                 ]
16202                             }
16203
16204                         ]
16205                     }
16206                 ]
16207             };
16208         }
16209         
16210         header = this.header;
16211         
16212        
16213         var cal_heads = function() {
16214             var ret = [];
16215             // fixme - handle this.
16216             
16217             for (var i =0; i < Date.dayNames.length; i++) {
16218                 var d = Date.dayNames[i];
16219                 ret.push({
16220                     tag: 'th',
16221                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16222                     html : d.substring(0,3)
16223                 });
16224                 
16225             }
16226             ret[0].cls += ' fc-first';
16227             ret[6].cls += ' fc-last';
16228             return ret;
16229         };
16230         var cal_cell = function(n) {
16231             return  {
16232                 tag: 'td',
16233                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16234                 cn : [
16235                     {
16236                         cn : [
16237                             {
16238                                 cls: 'fc-day-number',
16239                                 html: 'D'
16240                             },
16241                             {
16242                                 cls: 'fc-day-content',
16243                              
16244                                 cn : [
16245                                      {
16246                                         style: 'position: relative;' // height: 17px;
16247                                     }
16248                                 ]
16249                             }
16250                             
16251                             
16252                         ]
16253                     }
16254                 ]
16255                 
16256             }
16257         };
16258         var cal_rows = function() {
16259             
16260             var ret = [];
16261             for (var r = 0; r < 6; r++) {
16262                 var row= {
16263                     tag : 'tr',
16264                     cls : 'fc-week',
16265                     cn : []
16266                 };
16267                 
16268                 for (var i =0; i < Date.dayNames.length; i++) {
16269                     var d = Date.dayNames[i];
16270                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16271
16272                 }
16273                 row.cn[0].cls+=' fc-first';
16274                 row.cn[0].cn[0].style = 'min-height:90px';
16275                 row.cn[6].cls+=' fc-last';
16276                 ret.push(row);
16277                 
16278             }
16279             ret[0].cls += ' fc-first';
16280             ret[4].cls += ' fc-prev-last';
16281             ret[5].cls += ' fc-last';
16282             return ret;
16283             
16284         };
16285         
16286         var cal_table = {
16287             tag: 'table',
16288             cls: 'fc-border-separate',
16289             style : 'width:100%',
16290             cellspacing  : 0,
16291             cn : [
16292                 { 
16293                     tag: 'thead',
16294                     cn : [
16295                         { 
16296                             tag: 'tr',
16297                             cls : 'fc-first fc-last',
16298                             cn : cal_heads()
16299                         }
16300                     ]
16301                 },
16302                 { 
16303                     tag: 'tbody',
16304                     cn : cal_rows()
16305                 }
16306                   
16307             ]
16308         };
16309          
16310          var cfg = {
16311             cls : 'fc fc-ltr',
16312             cn : [
16313                 header,
16314                 {
16315                     cls : 'fc-content',
16316                     style : "position: relative;",
16317                     cn : [
16318                         {
16319                             cls : 'fc-view fc-view-month fc-grid',
16320                             style : 'position: relative',
16321                             unselectable : 'on',
16322                             cn : [
16323                                 {
16324                                     cls : 'fc-event-container',
16325                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16326                                 },
16327                                 cal_table
16328                             ]
16329                         }
16330                     ]
16331     
16332                 }
16333            ] 
16334             
16335         };
16336         
16337          
16338         
16339         return cfg;
16340     },
16341     
16342     
16343     initEvents : function()
16344     {
16345         if(!this.store){
16346             throw "can not find store for calendar";
16347         }
16348         
16349         var mark = {
16350             tag: "div",
16351             cls:"x-dlg-mask",
16352             style: "text-align:center",
16353             cn: [
16354                 {
16355                     tag: "div",
16356                     style: "background-color:white;width:50%;margin:250 auto",
16357                     cn: [
16358                         {
16359                             tag: "img",
16360                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16361                         },
16362                         {
16363                             tag: "span",
16364                             html: "Loading"
16365                         }
16366                         
16367                     ]
16368                 }
16369             ]
16370         };
16371         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16372         
16373         var size = this.el.select('.fc-content', true).first().getSize();
16374         this.maskEl.setSize(size.width, size.height);
16375         this.maskEl.enableDisplayMode("block");
16376         if(!this.loadMask){
16377             this.maskEl.hide();
16378         }
16379         
16380         this.store = Roo.factory(this.store, Roo.data);
16381         this.store.on('load', this.onLoad, this);
16382         this.store.on('beforeload', this.onBeforeLoad, this);
16383         
16384         this.resize();
16385         
16386         this.cells = this.el.select('.fc-day',true);
16387         //Roo.log(this.cells);
16388         this.textNodes = this.el.query('.fc-day-number');
16389         this.cells.addClassOnOver('fc-state-hover');
16390         
16391         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16392         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16393         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16394         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16395         
16396         this.on('monthchange', this.onMonthChange, this);
16397         
16398         this.update(new Date().clearTime());
16399     },
16400     
16401     resize : function() {
16402         var sz  = this.el.getSize();
16403         
16404         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16405         this.el.select('.fc-day-content div',true).setHeight(34);
16406     },
16407     
16408     
16409     // private
16410     showPrevMonth : function(e){
16411         this.update(this.activeDate.add("mo", -1));
16412     },
16413     showToday : function(e){
16414         this.update(new Date().clearTime());
16415     },
16416     // private
16417     showNextMonth : function(e){
16418         this.update(this.activeDate.add("mo", 1));
16419     },
16420
16421     // private
16422     showPrevYear : function(){
16423         this.update(this.activeDate.add("y", -1));
16424     },
16425
16426     // private
16427     showNextYear : function(){
16428         this.update(this.activeDate.add("y", 1));
16429     },
16430
16431     
16432    // private
16433     update : function(date)
16434     {
16435         var vd = this.activeDate;
16436         this.activeDate = date;
16437 //        if(vd && this.el){
16438 //            var t = date.getTime();
16439 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16440 //                Roo.log('using add remove');
16441 //                
16442 //                this.fireEvent('monthchange', this, date);
16443 //                
16444 //                this.cells.removeClass("fc-state-highlight");
16445 //                this.cells.each(function(c){
16446 //                   if(c.dateValue == t){
16447 //                       c.addClass("fc-state-highlight");
16448 //                       setTimeout(function(){
16449 //                            try{c.dom.firstChild.focus();}catch(e){}
16450 //                       }, 50);
16451 //                       return false;
16452 //                   }
16453 //                   return true;
16454 //                });
16455 //                return;
16456 //            }
16457 //        }
16458         
16459         var days = date.getDaysInMonth();
16460         
16461         var firstOfMonth = date.getFirstDateOfMonth();
16462         var startingPos = firstOfMonth.getDay()-this.startDay;
16463         
16464         if(startingPos < this.startDay){
16465             startingPos += 7;
16466         }
16467         
16468         var pm = date.add(Date.MONTH, -1);
16469         var prevStart = pm.getDaysInMonth()-startingPos;
16470 //        
16471         this.cells = this.el.select('.fc-day',true);
16472         this.textNodes = this.el.query('.fc-day-number');
16473         this.cells.addClassOnOver('fc-state-hover');
16474         
16475         var cells = this.cells.elements;
16476         var textEls = this.textNodes;
16477         
16478         Roo.each(cells, function(cell){
16479             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16480         });
16481         
16482         days += startingPos;
16483
16484         // convert everything to numbers so it's fast
16485         var day = 86400000;
16486         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16487         //Roo.log(d);
16488         //Roo.log(pm);
16489         //Roo.log(prevStart);
16490         
16491         var today = new Date().clearTime().getTime();
16492         var sel = date.clearTime().getTime();
16493         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16494         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16495         var ddMatch = this.disabledDatesRE;
16496         var ddText = this.disabledDatesText;
16497         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16498         var ddaysText = this.disabledDaysText;
16499         var format = this.format;
16500         
16501         var setCellClass = function(cal, cell){
16502             cell.row = 0;
16503             cell.events = [];
16504             cell.more = [];
16505             //Roo.log('set Cell Class');
16506             cell.title = "";
16507             var t = d.getTime();
16508             
16509             //Roo.log(d);
16510             
16511             cell.dateValue = t;
16512             if(t == today){
16513                 cell.className += " fc-today";
16514                 cell.className += " fc-state-highlight";
16515                 cell.title = cal.todayText;
16516             }
16517             if(t == sel){
16518                 // disable highlight in other month..
16519                 //cell.className += " fc-state-highlight";
16520                 
16521             }
16522             // disabling
16523             if(t < min) {
16524                 cell.className = " fc-state-disabled";
16525                 cell.title = cal.minText;
16526                 return;
16527             }
16528             if(t > max) {
16529                 cell.className = " fc-state-disabled";
16530                 cell.title = cal.maxText;
16531                 return;
16532             }
16533             if(ddays){
16534                 if(ddays.indexOf(d.getDay()) != -1){
16535                     cell.title = ddaysText;
16536                     cell.className = " fc-state-disabled";
16537                 }
16538             }
16539             if(ddMatch && format){
16540                 var fvalue = d.dateFormat(format);
16541                 if(ddMatch.test(fvalue)){
16542                     cell.title = ddText.replace("%0", fvalue);
16543                     cell.className = " fc-state-disabled";
16544                 }
16545             }
16546             
16547             if (!cell.initialClassName) {
16548                 cell.initialClassName = cell.dom.className;
16549             }
16550             
16551             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16552         };
16553
16554         var i = 0;
16555         
16556         for(; i < startingPos; i++) {
16557             textEls[i].innerHTML = (++prevStart);
16558             d.setDate(d.getDate()+1);
16559             
16560             cells[i].className = "fc-past fc-other-month";
16561             setCellClass(this, cells[i]);
16562         }
16563         
16564         var intDay = 0;
16565         
16566         for(; i < days; i++){
16567             intDay = i - startingPos + 1;
16568             textEls[i].innerHTML = (intDay);
16569             d.setDate(d.getDate()+1);
16570             
16571             cells[i].className = ''; // "x-date-active";
16572             setCellClass(this, cells[i]);
16573         }
16574         var extraDays = 0;
16575         
16576         for(; i < 42; i++) {
16577             textEls[i].innerHTML = (++extraDays);
16578             d.setDate(d.getDate()+1);
16579             
16580             cells[i].className = "fc-future fc-other-month";
16581             setCellClass(this, cells[i]);
16582         }
16583         
16584         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16585         
16586         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16587         
16588         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16589         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16590         
16591         if(totalRows != 6){
16592             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16593             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16594         }
16595         
16596         this.fireEvent('monthchange', this, date);
16597         
16598         
16599         /*
16600         if(!this.internalRender){
16601             var main = this.el.dom.firstChild;
16602             var w = main.offsetWidth;
16603             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16604             Roo.fly(main).setWidth(w);
16605             this.internalRender = true;
16606             // opera does not respect the auto grow header center column
16607             // then, after it gets a width opera refuses to recalculate
16608             // without a second pass
16609             if(Roo.isOpera && !this.secondPass){
16610                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16611                 this.secondPass = true;
16612                 this.update.defer(10, this, [date]);
16613             }
16614         }
16615         */
16616         
16617     },
16618     
16619     findCell : function(dt) {
16620         dt = dt.clearTime().getTime();
16621         var ret = false;
16622         this.cells.each(function(c){
16623             //Roo.log("check " +c.dateValue + '?=' + dt);
16624             if(c.dateValue == dt){
16625                 ret = c;
16626                 return false;
16627             }
16628             return true;
16629         });
16630         
16631         return ret;
16632     },
16633     
16634     findCells : function(ev) {
16635         var s = ev.start.clone().clearTime().getTime();
16636        // Roo.log(s);
16637         var e= ev.end.clone().clearTime().getTime();
16638        // Roo.log(e);
16639         var ret = [];
16640         this.cells.each(function(c){
16641              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16642             
16643             if(c.dateValue > e){
16644                 return ;
16645             }
16646             if(c.dateValue < s){
16647                 return ;
16648             }
16649             ret.push(c);
16650         });
16651         
16652         return ret;    
16653     },
16654     
16655 //    findBestRow: function(cells)
16656 //    {
16657 //        var ret = 0;
16658 //        
16659 //        for (var i =0 ; i < cells.length;i++) {
16660 //            ret  = Math.max(cells[i].rows || 0,ret);
16661 //        }
16662 //        return ret;
16663 //        
16664 //    },
16665     
16666     
16667     addItem : function(ev)
16668     {
16669         // look for vertical location slot in
16670         var cells = this.findCells(ev);
16671         
16672 //        ev.row = this.findBestRow(cells);
16673         
16674         // work out the location.
16675         
16676         var crow = false;
16677         var rows = [];
16678         for(var i =0; i < cells.length; i++) {
16679             
16680             cells[i].row = cells[0].row;
16681             
16682             if(i == 0){
16683                 cells[i].row = cells[i].row + 1;
16684             }
16685             
16686             if (!crow) {
16687                 crow = {
16688                     start : cells[i],
16689                     end :  cells[i]
16690                 };
16691                 continue;
16692             }
16693             if (crow.start.getY() == cells[i].getY()) {
16694                 // on same row.
16695                 crow.end = cells[i];
16696                 continue;
16697             }
16698             // different row.
16699             rows.push(crow);
16700             crow = {
16701                 start: cells[i],
16702                 end : cells[i]
16703             };
16704             
16705         }
16706         
16707         rows.push(crow);
16708         ev.els = [];
16709         ev.rows = rows;
16710         ev.cells = cells;
16711         
16712         cells[0].events.push(ev);
16713         
16714         this.calevents.push(ev);
16715     },
16716     
16717     clearEvents: function() {
16718         
16719         if(!this.calevents){
16720             return;
16721         }
16722         
16723         Roo.each(this.cells.elements, function(c){
16724             c.row = 0;
16725             c.events = [];
16726             c.more = [];
16727         });
16728         
16729         Roo.each(this.calevents, function(e) {
16730             Roo.each(e.els, function(el) {
16731                 el.un('mouseenter' ,this.onEventEnter, this);
16732                 el.un('mouseleave' ,this.onEventLeave, this);
16733                 el.remove();
16734             },this);
16735         },this);
16736         
16737         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16738             e.remove();
16739         });
16740         
16741     },
16742     
16743     renderEvents: function()
16744     {   
16745         var _this = this;
16746         
16747         this.cells.each(function(c) {
16748             
16749             if(c.row < 5){
16750                 return;
16751             }
16752             
16753             var ev = c.events;
16754             
16755             var r = 4;
16756             if(c.row != c.events.length){
16757                 r = 4 - (4 - (c.row - c.events.length));
16758             }
16759             
16760             c.events = ev.slice(0, r);
16761             c.more = ev.slice(r);
16762             
16763             if(c.more.length && c.more.length == 1){
16764                 c.events.push(c.more.pop());
16765             }
16766             
16767             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16768             
16769         });
16770             
16771         this.cells.each(function(c) {
16772             
16773             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16774             
16775             
16776             for (var e = 0; e < c.events.length; e++){
16777                 var ev = c.events[e];
16778                 var rows = ev.rows;
16779                 
16780                 for(var i = 0; i < rows.length; i++) {
16781                 
16782                     // how many rows should it span..
16783
16784                     var  cfg = {
16785                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16786                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16787
16788                         unselectable : "on",
16789                         cn : [
16790                             {
16791                                 cls: 'fc-event-inner',
16792                                 cn : [
16793     //                                {
16794     //                                  tag:'span',
16795     //                                  cls: 'fc-event-time',
16796     //                                  html : cells.length > 1 ? '' : ev.time
16797     //                                },
16798                                     {
16799                                       tag:'span',
16800                                       cls: 'fc-event-title',
16801                                       html : String.format('{0}', ev.title)
16802                                     }
16803
16804
16805                                 ]
16806                             },
16807                             {
16808                                 cls: 'ui-resizable-handle ui-resizable-e',
16809                                 html : '&nbsp;&nbsp;&nbsp'
16810                             }
16811
16812                         ]
16813                     };
16814
16815                     if (i == 0) {
16816                         cfg.cls += ' fc-event-start';
16817                     }
16818                     if ((i+1) == rows.length) {
16819                         cfg.cls += ' fc-event-end';
16820                     }
16821
16822                     var ctr = _this.el.select('.fc-event-container',true).first();
16823                     var cg = ctr.createChild(cfg);
16824
16825                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16826                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16827
16828                     var r = (c.more.length) ? 1 : 0;
16829                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16830                     cg.setWidth(ebox.right - sbox.x -2);
16831
16832                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16833                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16834                     cg.on('click', _this.onEventClick, _this, ev);
16835
16836                     ev.els.push(cg);
16837                     
16838                 }
16839                 
16840             }
16841             
16842             
16843             if(c.more.length){
16844                 var  cfg = {
16845                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16846                     style : 'position: absolute',
16847                     unselectable : "on",
16848                     cn : [
16849                         {
16850                             cls: 'fc-event-inner',
16851                             cn : [
16852                                 {
16853                                   tag:'span',
16854                                   cls: 'fc-event-title',
16855                                   html : 'More'
16856                                 }
16857
16858
16859                             ]
16860                         },
16861                         {
16862                             cls: 'ui-resizable-handle ui-resizable-e',
16863                             html : '&nbsp;&nbsp;&nbsp'
16864                         }
16865
16866                     ]
16867                 };
16868
16869                 var ctr = _this.el.select('.fc-event-container',true).first();
16870                 var cg = ctr.createChild(cfg);
16871
16872                 var sbox = c.select('.fc-day-content',true).first().getBox();
16873                 var ebox = c.select('.fc-day-content',true).first().getBox();
16874                 //Roo.log(cg);
16875                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16876                 cg.setWidth(ebox.right - sbox.x -2);
16877
16878                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16879                 
16880             }
16881             
16882         });
16883         
16884         
16885         
16886     },
16887     
16888     onEventEnter: function (e, el,event,d) {
16889         this.fireEvent('evententer', this, el, event);
16890     },
16891     
16892     onEventLeave: function (e, el,event,d) {
16893         this.fireEvent('eventleave', this, el, event);
16894     },
16895     
16896     onEventClick: function (e, el,event,d) {
16897         this.fireEvent('eventclick', this, el, event);
16898     },
16899     
16900     onMonthChange: function () {
16901         this.store.load();
16902     },
16903     
16904     onMoreEventClick: function(e, el, more)
16905     {
16906         var _this = this;
16907         
16908         this.calpopover.placement = 'right';
16909         this.calpopover.setTitle('More');
16910         
16911         this.calpopover.setContent('');
16912         
16913         var ctr = this.calpopover.el.select('.popover-content', true).first();
16914         
16915         Roo.each(more, function(m){
16916             var cfg = {
16917                 cls : 'fc-event-hori fc-event-draggable',
16918                 html : m.title
16919             };
16920             var cg = ctr.createChild(cfg);
16921             
16922             cg.on('click', _this.onEventClick, _this, m);
16923         });
16924         
16925         this.calpopover.show(el);
16926         
16927         
16928     },
16929     
16930     onLoad: function () 
16931     {   
16932         this.calevents = [];
16933         var cal = this;
16934         
16935         if(this.store.getCount() > 0){
16936             this.store.data.each(function(d){
16937                cal.addItem({
16938                     id : d.data.id,
16939                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16940                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16941                     time : d.data.start_time,
16942                     title : d.data.title,
16943                     description : d.data.description,
16944                     venue : d.data.venue
16945                 });
16946             });
16947         }
16948         
16949         this.renderEvents();
16950         
16951         if(this.calevents.length && this.loadMask){
16952             this.maskEl.hide();
16953         }
16954     },
16955     
16956     onBeforeLoad: function()
16957     {
16958         this.clearEvents();
16959         if(this.loadMask){
16960             this.maskEl.show();
16961         }
16962     }
16963 });
16964
16965  
16966  /*
16967  * - LGPL
16968  *
16969  * element
16970  * 
16971  */
16972
16973 /**
16974  * @class Roo.bootstrap.Popover
16975  * @extends Roo.bootstrap.Component
16976  * Bootstrap Popover class
16977  * @cfg {String} html contents of the popover   (or false to use children..)
16978  * @cfg {String} title of popover (or false to hide)
16979  * @cfg {String} placement how it is placed
16980  * @cfg {String} trigger click || hover (or false to trigger manually)
16981  * @cfg {String} over what (parent or false to trigger manually.)
16982  * @cfg {Number} delay - delay before showing
16983  
16984  * @constructor
16985  * Create a new Popover
16986  * @param {Object} config The config object
16987  */
16988
16989 Roo.bootstrap.Popover = function(config){
16990     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16991     
16992     this.addEvents({
16993         // raw events
16994          /**
16995          * @event show
16996          * After the popover show
16997          * 
16998          * @param {Roo.bootstrap.Popover} this
16999          */
17000         "show" : true,
17001         /**
17002          * @event hide
17003          * After the popover hide
17004          * 
17005          * @param {Roo.bootstrap.Popover} this
17006          */
17007         "hide" : true
17008     });
17009 };
17010
17011 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17012     
17013     title: 'Fill in a title',
17014     html: false,
17015     
17016     placement : 'right',
17017     trigger : 'hover', // hover
17018     
17019     delay : 0,
17020     
17021     over: 'parent',
17022     
17023     can_build_overlaid : false,
17024     
17025     getChildContainer : function()
17026     {
17027         return this.el.select('.popover-content',true).first();
17028     },
17029     
17030     getAutoCreate : function(){
17031          
17032         var cfg = {
17033            cls : 'popover roo-dynamic',
17034            style: 'display:block',
17035            cn : [
17036                 {
17037                     cls : 'arrow'
17038                 },
17039                 {
17040                     cls : 'popover-inner',
17041                     cn : [
17042                         {
17043                             tag: 'h3',
17044                             cls: 'popover-title',
17045                             html : this.title
17046                         },
17047                         {
17048                             cls : 'popover-content',
17049                             html : this.html
17050                         }
17051                     ]
17052                     
17053                 }
17054            ]
17055         };
17056         
17057         return cfg;
17058     },
17059     setTitle: function(str)
17060     {
17061         this.title = str;
17062         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17063     },
17064     setContent: function(str)
17065     {
17066         this.html = str;
17067         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17068     },
17069     // as it get's added to the bottom of the page.
17070     onRender : function(ct, position)
17071     {
17072         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17073         if(!this.el){
17074             var cfg = Roo.apply({},  this.getAutoCreate());
17075             cfg.id = Roo.id();
17076             
17077             if (this.cls) {
17078                 cfg.cls += ' ' + this.cls;
17079             }
17080             if (this.style) {
17081                 cfg.style = this.style;
17082             }
17083             //Roo.log("adding to ");
17084             this.el = Roo.get(document.body).createChild(cfg, position);
17085 //            Roo.log(this.el);
17086         }
17087         this.initEvents();
17088     },
17089     
17090     initEvents : function()
17091     {
17092         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17093         this.el.enableDisplayMode('block');
17094         this.el.hide();
17095         if (this.over === false) {
17096             return; 
17097         }
17098         if (this.triggers === false) {
17099             return;
17100         }
17101         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17102         var triggers = this.trigger ? this.trigger.split(' ') : [];
17103         Roo.each(triggers, function(trigger) {
17104         
17105             if (trigger == 'click') {
17106                 on_el.on('click', this.toggle, this);
17107             } else if (trigger != 'manual') {
17108                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17109                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17110       
17111                 on_el.on(eventIn  ,this.enter, this);
17112                 on_el.on(eventOut, this.leave, this);
17113             }
17114         }, this);
17115         
17116     },
17117     
17118     
17119     // private
17120     timeout : null,
17121     hoverState : null,
17122     
17123     toggle : function () {
17124         this.hoverState == 'in' ? this.leave() : this.enter();
17125     },
17126     
17127     enter : function () {
17128         
17129         clearTimeout(this.timeout);
17130     
17131         this.hoverState = 'in';
17132     
17133         if (!this.delay || !this.delay.show) {
17134             this.show();
17135             return;
17136         }
17137         var _t = this;
17138         this.timeout = setTimeout(function () {
17139             if (_t.hoverState == 'in') {
17140                 _t.show();
17141             }
17142         }, this.delay.show)
17143     },
17144     
17145     leave : function() {
17146         clearTimeout(this.timeout);
17147     
17148         this.hoverState = 'out';
17149     
17150         if (!this.delay || !this.delay.hide) {
17151             this.hide();
17152             return;
17153         }
17154         var _t = this;
17155         this.timeout = setTimeout(function () {
17156             if (_t.hoverState == 'out') {
17157                 _t.hide();
17158             }
17159         }, this.delay.hide)
17160     },
17161     
17162     show : function (on_el)
17163     {
17164         if (!on_el) {
17165             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17166         }
17167         
17168         // set content.
17169         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17170         if (this.html !== false) {
17171             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17172         }
17173         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17174         if (!this.title.length) {
17175             this.el.select('.popover-title',true).hide();
17176         }
17177         
17178         var placement = typeof this.placement == 'function' ?
17179             this.placement.call(this, this.el, on_el) :
17180             this.placement;
17181             
17182         var autoToken = /\s?auto?\s?/i;
17183         var autoPlace = autoToken.test(placement);
17184         if (autoPlace) {
17185             placement = placement.replace(autoToken, '') || 'top';
17186         }
17187         
17188         //this.el.detach()
17189         //this.el.setXY([0,0]);
17190         this.el.show();
17191         this.el.dom.style.display='block';
17192         this.el.addClass(placement);
17193         
17194         //this.el.appendTo(on_el);
17195         
17196         var p = this.getPosition();
17197         var box = this.el.getBox();
17198         
17199         if (autoPlace) {
17200             // fixme..
17201         }
17202         var align = Roo.bootstrap.Popover.alignment[placement];
17203         this.el.alignTo(on_el, align[0],align[1]);
17204         //var arrow = this.el.select('.arrow',true).first();
17205         //arrow.set(align[2], 
17206         
17207         this.el.addClass('in');
17208         
17209         
17210         if (this.el.hasClass('fade')) {
17211             // fade it?
17212         }
17213         
17214         this.hoverState = 'in';
17215         
17216         this.fireEvent('show', this);
17217         
17218     },
17219     hide : function()
17220     {
17221         this.el.setXY([0,0]);
17222         this.el.removeClass('in');
17223         this.el.hide();
17224         this.hoverState = null;
17225         
17226         this.fireEvent('hide', this);
17227     }
17228     
17229 });
17230
17231 Roo.bootstrap.Popover.alignment = {
17232     'left' : ['r-l', [-10,0], 'right'],
17233     'right' : ['l-r', [10,0], 'left'],
17234     'bottom' : ['t-b', [0,10], 'top'],
17235     'top' : [ 'b-t', [0,-10], 'bottom']
17236 };
17237
17238  /*
17239  * - LGPL
17240  *
17241  * Progress
17242  * 
17243  */
17244
17245 /**
17246  * @class Roo.bootstrap.Progress
17247  * @extends Roo.bootstrap.Component
17248  * Bootstrap Progress class
17249  * @cfg {Boolean} striped striped of the progress bar
17250  * @cfg {Boolean} active animated of the progress bar
17251  * 
17252  * 
17253  * @constructor
17254  * Create a new Progress
17255  * @param {Object} config The config object
17256  */
17257
17258 Roo.bootstrap.Progress = function(config){
17259     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17260 };
17261
17262 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17263     
17264     striped : false,
17265     active: false,
17266     
17267     getAutoCreate : function(){
17268         var cfg = {
17269             tag: 'div',
17270             cls: 'progress'
17271         };
17272         
17273         
17274         if(this.striped){
17275             cfg.cls += ' progress-striped';
17276         }
17277       
17278         if(this.active){
17279             cfg.cls += ' active';
17280         }
17281         
17282         
17283         return cfg;
17284     }
17285    
17286 });
17287
17288  
17289
17290  /*
17291  * - LGPL
17292  *
17293  * ProgressBar
17294  * 
17295  */
17296
17297 /**
17298  * @class Roo.bootstrap.ProgressBar
17299  * @extends Roo.bootstrap.Component
17300  * Bootstrap ProgressBar class
17301  * @cfg {Number} aria_valuenow aria-value now
17302  * @cfg {Number} aria_valuemin aria-value min
17303  * @cfg {Number} aria_valuemax aria-value max
17304  * @cfg {String} label label for the progress bar
17305  * @cfg {String} panel (success | info | warning | danger )
17306  * @cfg {String} role role of the progress bar
17307  * @cfg {String} sr_only text
17308  * 
17309  * 
17310  * @constructor
17311  * Create a new ProgressBar
17312  * @param {Object} config The config object
17313  */
17314
17315 Roo.bootstrap.ProgressBar = function(config){
17316     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17317 };
17318
17319 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17320     
17321     aria_valuenow : 0,
17322     aria_valuemin : 0,
17323     aria_valuemax : 100,
17324     label : false,
17325     panel : false,
17326     role : false,
17327     sr_only: false,
17328     
17329     getAutoCreate : function()
17330     {
17331         
17332         var cfg = {
17333             tag: 'div',
17334             cls: 'progress-bar',
17335             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17336         };
17337         
17338         if(this.sr_only){
17339             cfg.cn = {
17340                 tag: 'span',
17341                 cls: 'sr-only',
17342                 html: this.sr_only
17343             }
17344         }
17345         
17346         if(this.role){
17347             cfg.role = this.role;
17348         }
17349         
17350         if(this.aria_valuenow){
17351             cfg['aria-valuenow'] = this.aria_valuenow;
17352         }
17353         
17354         if(this.aria_valuemin){
17355             cfg['aria-valuemin'] = this.aria_valuemin;
17356         }
17357         
17358         if(this.aria_valuemax){
17359             cfg['aria-valuemax'] = this.aria_valuemax;
17360         }
17361         
17362         if(this.label && !this.sr_only){
17363             cfg.html = this.label;
17364         }
17365         
17366         if(this.panel){
17367             cfg.cls += ' progress-bar-' + this.panel;
17368         }
17369         
17370         return cfg;
17371     },
17372     
17373     update : function(aria_valuenow)
17374     {
17375         this.aria_valuenow = aria_valuenow;
17376         
17377         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17378     }
17379    
17380 });
17381
17382  
17383
17384  /*
17385  * - LGPL
17386  *
17387  * column
17388  * 
17389  */
17390
17391 /**
17392  * @class Roo.bootstrap.TabGroup
17393  * @extends Roo.bootstrap.Column
17394  * Bootstrap Column class
17395  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17396  * @cfg {Boolean} carousel true to make the group behave like a carousel
17397  * @cfg {Boolean} bullets show bullets for the panels
17398  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17399  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17400  * @cfg {Boolean} showarrow (true|false) show arrow default true
17401  * 
17402  * @constructor
17403  * Create a new TabGroup
17404  * @param {Object} config The config object
17405  */
17406
17407 Roo.bootstrap.TabGroup = function(config){
17408     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17409     if (!this.navId) {
17410         this.navId = Roo.id();
17411     }
17412     this.tabs = [];
17413     Roo.bootstrap.TabGroup.register(this);
17414     
17415 };
17416
17417 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17418     
17419     carousel : false,
17420     transition : false,
17421     bullets : 0,
17422     timer : 0,
17423     autoslide : false,
17424     slideFn : false,
17425     slideOnTouch : false,
17426     showarrow : true,
17427     
17428     getAutoCreate : function()
17429     {
17430         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17431         
17432         cfg.cls += ' tab-content';
17433         
17434         if (this.carousel) {
17435             cfg.cls += ' carousel slide';
17436             
17437             cfg.cn = [{
17438                cls : 'carousel-inner',
17439                cn : []
17440             }];
17441         
17442             if(this.bullets  && !Roo.isTouch){
17443                 
17444                 var bullets = {
17445                     cls : 'carousel-bullets',
17446                     cn : []
17447                 };
17448                
17449                 if(this.bullets_cls){
17450                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17451                 }
17452                 
17453                 bullets.cn.push({
17454                     cls : 'clear'
17455                 });
17456                 
17457                 cfg.cn[0].cn.push(bullets);
17458             }
17459             
17460             if(this.showarrow){
17461                 cfg.cn[0].cn.push({
17462                     tag : 'div',
17463                     class : 'carousel-arrow',
17464                     cn : [
17465                         {
17466                             tag : 'div',
17467                             class : 'carousel-prev',
17468                             cn : [
17469                                 {
17470                                     tag : 'i',
17471                                     class : 'fa fa-chevron-left'
17472                                 }
17473                             ]
17474                         },
17475                         {
17476                             tag : 'div',
17477                             class : 'carousel-next',
17478                             cn : [
17479                                 {
17480                                     tag : 'i',
17481                                     class : 'fa fa-chevron-right'
17482                                 }
17483                             ]
17484                         }
17485                     ]
17486                 });
17487             }
17488             
17489         }
17490         
17491         return cfg;
17492     },
17493     
17494     initEvents:  function()
17495     {
17496 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17497 //            this.el.on("touchstart", this.onTouchStart, this);
17498 //        }
17499         
17500         if(this.autoslide){
17501             var _this = this;
17502             
17503             this.slideFn = window.setInterval(function() {
17504                 _this.showPanelNext();
17505             }, this.timer);
17506         }
17507         
17508         if(this.showarrow){
17509             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17510             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17511         }
17512         
17513         
17514     },
17515     
17516 //    onTouchStart : function(e, el, o)
17517 //    {
17518 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17519 //            return;
17520 //        }
17521 //        
17522 //        this.showPanelNext();
17523 //    },
17524     
17525     
17526     getChildContainer : function()
17527     {
17528         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17529     },
17530     
17531     /**
17532     * register a Navigation item
17533     * @param {Roo.bootstrap.NavItem} the navitem to add
17534     */
17535     register : function(item)
17536     {
17537         this.tabs.push( item);
17538         item.navId = this.navId; // not really needed..
17539         this.addBullet();
17540     
17541     },
17542     
17543     getActivePanel : function()
17544     {
17545         var r = false;
17546         Roo.each(this.tabs, function(t) {
17547             if (t.active) {
17548                 r = t;
17549                 return false;
17550             }
17551             return null;
17552         });
17553         return r;
17554         
17555     },
17556     getPanelByName : function(n)
17557     {
17558         var r = false;
17559         Roo.each(this.tabs, function(t) {
17560             if (t.tabId == n) {
17561                 r = t;
17562                 return false;
17563             }
17564             return null;
17565         });
17566         return r;
17567     },
17568     indexOfPanel : function(p)
17569     {
17570         var r = false;
17571         Roo.each(this.tabs, function(t,i) {
17572             if (t.tabId == p.tabId) {
17573                 r = i;
17574                 return false;
17575             }
17576             return null;
17577         });
17578         return r;
17579     },
17580     /**
17581      * show a specific panel
17582      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17583      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17584      */
17585     showPanel : function (pan)
17586     {
17587         if(this.transition || typeof(pan) == 'undefined'){
17588             Roo.log("waiting for the transitionend");
17589             return;
17590         }
17591         
17592         if (typeof(pan) == 'number') {
17593             pan = this.tabs[pan];
17594         }
17595         
17596         if (typeof(pan) == 'string') {
17597             pan = this.getPanelByName(pan);
17598         }
17599         
17600         var cur = this.getActivePanel();
17601         
17602         if(!pan || !cur){
17603             Roo.log('pan or acitve pan is undefined');
17604             return false;
17605         }
17606         
17607         if (pan.tabId == this.getActivePanel().tabId) {
17608             return true;
17609         }
17610         
17611         if (false === cur.fireEvent('beforedeactivate')) {
17612             return false;
17613         }
17614         
17615         if(this.bullets > 0 && !Roo.isTouch){
17616             this.setActiveBullet(this.indexOfPanel(pan));
17617         }
17618         
17619         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17620             
17621             this.transition = true;
17622             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17623             var lr = dir == 'next' ? 'left' : 'right';
17624             pan.el.addClass(dir); // or prev
17625             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17626             cur.el.addClass(lr); // or right
17627             pan.el.addClass(lr);
17628             
17629             var _this = this;
17630             cur.el.on('transitionend', function() {
17631                 Roo.log("trans end?");
17632                 
17633                 pan.el.removeClass([lr,dir]);
17634                 pan.setActive(true);
17635                 
17636                 cur.el.removeClass([lr]);
17637                 cur.setActive(false);
17638                 
17639                 _this.transition = false;
17640                 
17641             }, this, { single:  true } );
17642             
17643             return true;
17644         }
17645         
17646         cur.setActive(false);
17647         pan.setActive(true);
17648         
17649         return true;
17650         
17651     },
17652     showPanelNext : function()
17653     {
17654         var i = this.indexOfPanel(this.getActivePanel());
17655         
17656         if (i >= this.tabs.length - 1 && !this.autoslide) {
17657             return;
17658         }
17659         
17660         if (i >= this.tabs.length - 1 && this.autoslide) {
17661             i = -1;
17662         }
17663         
17664         this.showPanel(this.tabs[i+1]);
17665     },
17666     
17667     showPanelPrev : function()
17668     {
17669         var i = this.indexOfPanel(this.getActivePanel());
17670         
17671         if (i  < 1 && !this.autoslide) {
17672             return;
17673         }
17674         
17675         if (i < 1 && this.autoslide) {
17676             i = this.tabs.length;
17677         }
17678         
17679         this.showPanel(this.tabs[i-1]);
17680     },
17681     
17682     
17683     addBullet: function()
17684     {
17685         if(!this.bullets || Roo.isTouch){
17686             return;
17687         }
17688         var ctr = this.el.select('.carousel-bullets',true).first();
17689         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17690         var bullet = ctr.createChild({
17691             cls : 'bullet bullet-' + i
17692         },ctr.dom.lastChild);
17693         
17694         
17695         var _this = this;
17696         
17697         bullet.on('click', (function(e, el, o, ii, t){
17698
17699             e.preventDefault();
17700
17701             this.showPanel(ii);
17702
17703             if(this.autoslide && this.slideFn){
17704                 clearInterval(this.slideFn);
17705                 this.slideFn = window.setInterval(function() {
17706                     _this.showPanelNext();
17707                 }, this.timer);
17708             }
17709
17710         }).createDelegate(this, [i, bullet], true));
17711                 
17712         
17713     },
17714      
17715     setActiveBullet : function(i)
17716     {
17717         if(Roo.isTouch){
17718             return;
17719         }
17720         
17721         Roo.each(this.el.select('.bullet', true).elements, function(el){
17722             el.removeClass('selected');
17723         });
17724
17725         var bullet = this.el.select('.bullet-' + i, true).first();
17726         
17727         if(!bullet){
17728             return;
17729         }
17730         
17731         bullet.addClass('selected');
17732     }
17733     
17734     
17735   
17736 });
17737
17738  
17739
17740  
17741  
17742 Roo.apply(Roo.bootstrap.TabGroup, {
17743     
17744     groups: {},
17745      /**
17746     * register a Navigation Group
17747     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17748     */
17749     register : function(navgrp)
17750     {
17751         this.groups[navgrp.navId] = navgrp;
17752         
17753     },
17754     /**
17755     * fetch a Navigation Group based on the navigation ID
17756     * if one does not exist , it will get created.
17757     * @param {string} the navgroup to add
17758     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17759     */
17760     get: function(navId) {
17761         if (typeof(this.groups[navId]) == 'undefined') {
17762             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17763         }
17764         return this.groups[navId] ;
17765     }
17766     
17767     
17768     
17769 });
17770
17771  /*
17772  * - LGPL
17773  *
17774  * TabPanel
17775  * 
17776  */
17777
17778 /**
17779  * @class Roo.bootstrap.TabPanel
17780  * @extends Roo.bootstrap.Component
17781  * Bootstrap TabPanel class
17782  * @cfg {Boolean} active panel active
17783  * @cfg {String} html panel content
17784  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17785  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17786  * @cfg {String} href click to link..
17787  * 
17788  * 
17789  * @constructor
17790  * Create a new TabPanel
17791  * @param {Object} config The config object
17792  */
17793
17794 Roo.bootstrap.TabPanel = function(config){
17795     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17796     this.addEvents({
17797         /**
17798              * @event changed
17799              * Fires when the active status changes
17800              * @param {Roo.bootstrap.TabPanel} this
17801              * @param {Boolean} state the new state
17802             
17803          */
17804         'changed': true,
17805         /**
17806              * @event beforedeactivate
17807              * Fires before a tab is de-activated - can be used to do validation on a form.
17808              * @param {Roo.bootstrap.TabPanel} this
17809              * @return {Boolean} false if there is an error
17810             
17811          */
17812         'beforedeactivate': true
17813      });
17814     
17815     this.tabId = this.tabId || Roo.id();
17816   
17817 };
17818
17819 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17820     
17821     active: false,
17822     html: false,
17823     tabId: false,
17824     navId : false,
17825     href : '',
17826     
17827     getAutoCreate : function(){
17828         var cfg = {
17829             tag: 'div',
17830             // item is needed for carousel - not sure if it has any effect otherwise
17831             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17832             html: this.html || ''
17833         };
17834         
17835         if(this.active){
17836             cfg.cls += ' active';
17837         }
17838         
17839         if(this.tabId){
17840             cfg.tabId = this.tabId;
17841         }
17842         
17843         
17844         return cfg;
17845     },
17846     
17847     initEvents:  function()
17848     {
17849         var p = this.parent();
17850         
17851         this.navId = this.navId || p.navId;
17852         
17853         if (typeof(this.navId) != 'undefined') {
17854             // not really needed.. but just in case.. parent should be a NavGroup.
17855             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17856             
17857             tg.register(this);
17858             
17859             var i = tg.tabs.length - 1;
17860             
17861             if(this.active && tg.bullets > 0 && i < tg.bullets){
17862                 tg.setActiveBullet(i);
17863             }
17864         }
17865         
17866         this.el.on('click', this.onClick, this);
17867         
17868         if(Roo.isTouch){
17869             this.el.on("touchstart", this.onTouchStart, this);
17870             this.el.on("touchmove", this.onTouchMove, this);
17871             this.el.on("touchend", this.onTouchEnd, this);
17872         }
17873         
17874     },
17875     
17876     onRender : function(ct, position)
17877     {
17878         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17879     },
17880     
17881     setActive : function(state)
17882     {
17883         Roo.log("panel - set active " + this.tabId + "=" + state);
17884         
17885         this.active = state;
17886         if (!state) {
17887             this.el.removeClass('active');
17888             
17889         } else  if (!this.el.hasClass('active')) {
17890             this.el.addClass('active');
17891         }
17892         
17893         this.fireEvent('changed', this, state);
17894     },
17895     
17896     onClick : function(e)
17897     {
17898         e.preventDefault();
17899         
17900         if(!this.href.length){
17901             return;
17902         }
17903         
17904         window.location.href = this.href;
17905     },
17906     
17907     startX : 0,
17908     startY : 0,
17909     endX : 0,
17910     endY : 0,
17911     swiping : false,
17912     
17913     onTouchStart : function(e)
17914     {
17915         this.swiping = false;
17916         
17917         this.startX = e.browserEvent.touches[0].clientX;
17918         this.startY = e.browserEvent.touches[0].clientY;
17919     },
17920     
17921     onTouchMove : function(e)
17922     {
17923         this.swiping = true;
17924         
17925         this.endX = e.browserEvent.touches[0].clientX;
17926         this.endY = e.browserEvent.touches[0].clientY;
17927     },
17928     
17929     onTouchEnd : function(e)
17930     {
17931         if(!this.swiping){
17932             this.onClick(e);
17933             return;
17934         }
17935         
17936         var tabGroup = this.parent();
17937         
17938         if(this.endX > this.startX){ // swiping right
17939             tabGroup.showPanelPrev();
17940             return;
17941         }
17942         
17943         if(this.startX > this.endX){ // swiping left
17944             tabGroup.showPanelNext();
17945             return;
17946         }
17947     }
17948     
17949     
17950 });
17951  
17952
17953  
17954
17955  /*
17956  * - LGPL
17957  *
17958  * DateField
17959  * 
17960  */
17961
17962 /**
17963  * @class Roo.bootstrap.DateField
17964  * @extends Roo.bootstrap.Input
17965  * Bootstrap DateField class
17966  * @cfg {Number} weekStart default 0
17967  * @cfg {String} viewMode default empty, (months|years)
17968  * @cfg {String} minViewMode default empty, (months|years)
17969  * @cfg {Number} startDate default -Infinity
17970  * @cfg {Number} endDate default Infinity
17971  * @cfg {Boolean} todayHighlight default false
17972  * @cfg {Boolean} todayBtn default false
17973  * @cfg {Boolean} calendarWeeks default false
17974  * @cfg {Object} daysOfWeekDisabled default empty
17975  * @cfg {Boolean} singleMode default false (true | false)
17976  * 
17977  * @cfg {Boolean} keyboardNavigation default true
17978  * @cfg {String} language default en
17979  * 
17980  * @constructor
17981  * Create a new DateField
17982  * @param {Object} config The config object
17983  */
17984
17985 Roo.bootstrap.DateField = function(config){
17986     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17987      this.addEvents({
17988             /**
17989              * @event show
17990              * Fires when this field show.
17991              * @param {Roo.bootstrap.DateField} this
17992              * @param {Mixed} date The date value
17993              */
17994             show : true,
17995             /**
17996              * @event show
17997              * Fires when this field hide.
17998              * @param {Roo.bootstrap.DateField} this
17999              * @param {Mixed} date The date value
18000              */
18001             hide : true,
18002             /**
18003              * @event select
18004              * Fires when select a date.
18005              * @param {Roo.bootstrap.DateField} this
18006              * @param {Mixed} date The date value
18007              */
18008             select : true,
18009             /**
18010              * @event beforeselect
18011              * Fires when before select a date.
18012              * @param {Roo.bootstrap.DateField} this
18013              * @param {Mixed} date The date value
18014              */
18015             beforeselect : true
18016         });
18017 };
18018
18019 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18020     
18021     /**
18022      * @cfg {String} format
18023      * The default date format string which can be overriden for localization support.  The format must be
18024      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18025      */
18026     format : "m/d/y",
18027     /**
18028      * @cfg {String} altFormats
18029      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18030      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18031      */
18032     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18033     
18034     weekStart : 0,
18035     
18036     viewMode : '',
18037     
18038     minViewMode : '',
18039     
18040     todayHighlight : false,
18041     
18042     todayBtn: false,
18043     
18044     language: 'en',
18045     
18046     keyboardNavigation: true,
18047     
18048     calendarWeeks: false,
18049     
18050     startDate: -Infinity,
18051     
18052     endDate: Infinity,
18053     
18054     daysOfWeekDisabled: [],
18055     
18056     _events: [],
18057     
18058     singleMode : false,
18059     
18060     UTCDate: function()
18061     {
18062         return new Date(Date.UTC.apply(Date, arguments));
18063     },
18064     
18065     UTCToday: function()
18066     {
18067         var today = new Date();
18068         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18069     },
18070     
18071     getDate: function() {
18072             var d = this.getUTCDate();
18073             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18074     },
18075     
18076     getUTCDate: function() {
18077             return this.date;
18078     },
18079     
18080     setDate: function(d) {
18081             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18082     },
18083     
18084     setUTCDate: function(d) {
18085             this.date = d;
18086             this.setValue(this.formatDate(this.date));
18087     },
18088         
18089     onRender: function(ct, position)
18090     {
18091         
18092         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18093         
18094         this.language = this.language || 'en';
18095         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18096         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18097         
18098         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18099         this.format = this.format || 'm/d/y';
18100         this.isInline = false;
18101         this.isInput = true;
18102         this.component = this.el.select('.add-on', true).first() || false;
18103         this.component = (this.component && this.component.length === 0) ? false : this.component;
18104         this.hasInput = this.component && this.inputEl().length;
18105         
18106         if (typeof(this.minViewMode === 'string')) {
18107             switch (this.minViewMode) {
18108                 case 'months':
18109                     this.minViewMode = 1;
18110                     break;
18111                 case 'years':
18112                     this.minViewMode = 2;
18113                     break;
18114                 default:
18115                     this.minViewMode = 0;
18116                     break;
18117             }
18118         }
18119         
18120         if (typeof(this.viewMode === 'string')) {
18121             switch (this.viewMode) {
18122                 case 'months':
18123                     this.viewMode = 1;
18124                     break;
18125                 case 'years':
18126                     this.viewMode = 2;
18127                     break;
18128                 default:
18129                     this.viewMode = 0;
18130                     break;
18131             }
18132         }
18133                 
18134         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18135         
18136 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18137         
18138         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18139         
18140         this.picker().on('mousedown', this.onMousedown, this);
18141         this.picker().on('click', this.onClick, this);
18142         
18143         this.picker().addClass('datepicker-dropdown');
18144         
18145         this.startViewMode = this.viewMode;
18146         
18147         if(this.singleMode){
18148             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18149                 v.setVisibilityMode(Roo.Element.DISPLAY);
18150                 v.hide();
18151             });
18152             
18153             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18154                 v.setStyle('width', '189px');
18155             });
18156         }
18157         
18158         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18159             if(!this.calendarWeeks){
18160                 v.remove();
18161                 return;
18162             }
18163             
18164             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18165             v.attr('colspan', function(i, val){
18166                 return parseInt(val) + 1;
18167             });
18168         });
18169                         
18170         
18171         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18172         
18173         this.setStartDate(this.startDate);
18174         this.setEndDate(this.endDate);
18175         
18176         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18177         
18178         this.fillDow();
18179         this.fillMonths();
18180         this.update();
18181         this.showMode();
18182         
18183         if(this.isInline) {
18184             this.show();
18185         }
18186     },
18187     
18188     picker : function()
18189     {
18190         return this.pickerEl;
18191 //        return this.el.select('.datepicker', true).first();
18192     },
18193     
18194     fillDow: function()
18195     {
18196         var dowCnt = this.weekStart;
18197         
18198         var dow = {
18199             tag: 'tr',
18200             cn: [
18201                 
18202             ]
18203         };
18204         
18205         if(this.calendarWeeks){
18206             dow.cn.push({
18207                 tag: 'th',
18208                 cls: 'cw',
18209                 html: '&nbsp;'
18210             })
18211         }
18212         
18213         while (dowCnt < this.weekStart + 7) {
18214             dow.cn.push({
18215                 tag: 'th',
18216                 cls: 'dow',
18217                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18218             });
18219         }
18220         
18221         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18222     },
18223     
18224     fillMonths: function()
18225     {    
18226         var i = 0;
18227         var months = this.picker().select('>.datepicker-months td', true).first();
18228         
18229         months.dom.innerHTML = '';
18230         
18231         while (i < 12) {
18232             var month = {
18233                 tag: 'span',
18234                 cls: 'month',
18235                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18236             };
18237             
18238             months.createChild(month);
18239         }
18240         
18241     },
18242     
18243     update: function()
18244     {
18245         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;
18246         
18247         if (this.date < this.startDate) {
18248             this.viewDate = new Date(this.startDate);
18249         } else if (this.date > this.endDate) {
18250             this.viewDate = new Date(this.endDate);
18251         } else {
18252             this.viewDate = new Date(this.date);
18253         }
18254         
18255         this.fill();
18256     },
18257     
18258     fill: function() 
18259     {
18260         var d = new Date(this.viewDate),
18261                 year = d.getUTCFullYear(),
18262                 month = d.getUTCMonth(),
18263                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18264                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18265                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18266                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18267                 currentDate = this.date && this.date.valueOf(),
18268                 today = this.UTCToday();
18269         
18270         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18271         
18272 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18273         
18274 //        this.picker.select('>tfoot th.today').
18275 //                                              .text(dates[this.language].today)
18276 //                                              .toggle(this.todayBtn !== false);
18277     
18278         this.updateNavArrows();
18279         this.fillMonths();
18280                                                 
18281         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18282         
18283         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18284          
18285         prevMonth.setUTCDate(day);
18286         
18287         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18288         
18289         var nextMonth = new Date(prevMonth);
18290         
18291         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18292         
18293         nextMonth = nextMonth.valueOf();
18294         
18295         var fillMonths = false;
18296         
18297         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18298         
18299         while(prevMonth.valueOf() < nextMonth) {
18300             var clsName = '';
18301             
18302             if (prevMonth.getUTCDay() === this.weekStart) {
18303                 if(fillMonths){
18304                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18305                 }
18306                     
18307                 fillMonths = {
18308                     tag: 'tr',
18309                     cn: []
18310                 };
18311                 
18312                 if(this.calendarWeeks){
18313                     // ISO 8601: First week contains first thursday.
18314                     // ISO also states week starts on Monday, but we can be more abstract here.
18315                     var
18316                     // Start of current week: based on weekstart/current date
18317                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18318                     // Thursday of this week
18319                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18320                     // First Thursday of year, year from thursday
18321                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18322                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18323                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18324                     
18325                     fillMonths.cn.push({
18326                         tag: 'td',
18327                         cls: 'cw',
18328                         html: calWeek
18329                     });
18330                 }
18331             }
18332             
18333             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18334                 clsName += ' old';
18335             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18336                 clsName += ' new';
18337             }
18338             if (this.todayHighlight &&
18339                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18340                 prevMonth.getUTCMonth() == today.getMonth() &&
18341                 prevMonth.getUTCDate() == today.getDate()) {
18342                 clsName += ' today';
18343             }
18344             
18345             if (currentDate && prevMonth.valueOf() === currentDate) {
18346                 clsName += ' active';
18347             }
18348             
18349             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18350                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18351                     clsName += ' disabled';
18352             }
18353             
18354             fillMonths.cn.push({
18355                 tag: 'td',
18356                 cls: 'day ' + clsName,
18357                 html: prevMonth.getDate()
18358             });
18359             
18360             prevMonth.setDate(prevMonth.getDate()+1);
18361         }
18362           
18363         var currentYear = this.date && this.date.getUTCFullYear();
18364         var currentMonth = this.date && this.date.getUTCMonth();
18365         
18366         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18367         
18368         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18369             v.removeClass('active');
18370             
18371             if(currentYear === year && k === currentMonth){
18372                 v.addClass('active');
18373             }
18374             
18375             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18376                 v.addClass('disabled');
18377             }
18378             
18379         });
18380         
18381         
18382         year = parseInt(year/10, 10) * 10;
18383         
18384         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18385         
18386         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18387         
18388         year -= 1;
18389         for (var i = -1; i < 11; i++) {
18390             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18391                 tag: 'span',
18392                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18393                 html: year
18394             });
18395             
18396             year += 1;
18397         }
18398     },
18399     
18400     showMode: function(dir) 
18401     {
18402         if (dir) {
18403             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18404         }
18405         
18406         Roo.each(this.picker().select('>div',true).elements, function(v){
18407             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18408             v.hide();
18409         });
18410         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18411     },
18412     
18413     place: function()
18414     {
18415         if(this.isInline) {
18416             return;
18417         }
18418         
18419         this.picker().removeClass(['bottom', 'top']);
18420         
18421         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18422             /*
18423              * place to the top of element!
18424              *
18425              */
18426             
18427             this.picker().addClass('top');
18428             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18429             
18430             return;
18431         }
18432         
18433         this.picker().addClass('bottom');
18434         
18435         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18436     },
18437     
18438     parseDate : function(value)
18439     {
18440         if(!value || value instanceof Date){
18441             return value;
18442         }
18443         var v = Date.parseDate(value, this.format);
18444         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18445             v = Date.parseDate(value, 'Y-m-d');
18446         }
18447         if(!v && this.altFormats){
18448             if(!this.altFormatsArray){
18449                 this.altFormatsArray = this.altFormats.split("|");
18450             }
18451             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18452                 v = Date.parseDate(value, this.altFormatsArray[i]);
18453             }
18454         }
18455         return v;
18456     },
18457     
18458     formatDate : function(date, fmt)
18459     {   
18460         return (!date || !(date instanceof Date)) ?
18461         date : date.dateFormat(fmt || this.format);
18462     },
18463     
18464     onFocus : function()
18465     {
18466         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18467         this.show();
18468     },
18469     
18470     onBlur : function()
18471     {
18472         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18473         
18474         var d = this.inputEl().getValue();
18475         
18476         this.setValue(d);
18477                 
18478         this.hide();
18479     },
18480     
18481     show : function()
18482     {
18483         this.picker().show();
18484         this.update();
18485         this.place();
18486         
18487         this.fireEvent('show', this, this.date);
18488     },
18489     
18490     hide : function()
18491     {
18492         if(this.isInline) {
18493             return;
18494         }
18495         this.picker().hide();
18496         this.viewMode = this.startViewMode;
18497         this.showMode();
18498         
18499         this.fireEvent('hide', this, this.date);
18500         
18501     },
18502     
18503     onMousedown: function(e)
18504     {
18505         e.stopPropagation();
18506         e.preventDefault();
18507     },
18508     
18509     keyup: function(e)
18510     {
18511         Roo.bootstrap.DateField.superclass.keyup.call(this);
18512         this.update();
18513     },
18514
18515     setValue: function(v)
18516     {
18517         if(this.fireEvent('beforeselect', this, v) !== false){
18518             var d = new Date(this.parseDate(v) ).clearTime();
18519         
18520             if(isNaN(d.getTime())){
18521                 this.date = this.viewDate = '';
18522                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18523                 return;
18524             }
18525
18526             v = this.formatDate(d);
18527
18528             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18529
18530             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18531
18532             this.update();
18533
18534             this.fireEvent('select', this, this.date);
18535         }
18536     },
18537     
18538     getValue: function()
18539     {
18540         return this.formatDate(this.date);
18541     },
18542     
18543     fireKey: function(e)
18544     {
18545         if (!this.picker().isVisible()){
18546             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18547                 this.show();
18548             }
18549             return;
18550         }
18551         
18552         var dateChanged = false,
18553         dir, day, month,
18554         newDate, newViewDate;
18555         
18556         switch(e.keyCode){
18557             case 27: // escape
18558                 this.hide();
18559                 e.preventDefault();
18560                 break;
18561             case 37: // left
18562             case 39: // right
18563                 if (!this.keyboardNavigation) {
18564                     break;
18565                 }
18566                 dir = e.keyCode == 37 ? -1 : 1;
18567                 
18568                 if (e.ctrlKey){
18569                     newDate = this.moveYear(this.date, dir);
18570                     newViewDate = this.moveYear(this.viewDate, dir);
18571                 } else if (e.shiftKey){
18572                     newDate = this.moveMonth(this.date, dir);
18573                     newViewDate = this.moveMonth(this.viewDate, dir);
18574                 } else {
18575                     newDate = new Date(this.date);
18576                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18577                     newViewDate = new Date(this.viewDate);
18578                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18579                 }
18580                 if (this.dateWithinRange(newDate)){
18581                     this.date = newDate;
18582                     this.viewDate = newViewDate;
18583                     this.setValue(this.formatDate(this.date));
18584 //                    this.update();
18585                     e.preventDefault();
18586                     dateChanged = true;
18587                 }
18588                 break;
18589             case 38: // up
18590             case 40: // down
18591                 if (!this.keyboardNavigation) {
18592                     break;
18593                 }
18594                 dir = e.keyCode == 38 ? -1 : 1;
18595                 if (e.ctrlKey){
18596                     newDate = this.moveYear(this.date, dir);
18597                     newViewDate = this.moveYear(this.viewDate, dir);
18598                 } else if (e.shiftKey){
18599                     newDate = this.moveMonth(this.date, dir);
18600                     newViewDate = this.moveMonth(this.viewDate, dir);
18601                 } else {
18602                     newDate = new Date(this.date);
18603                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18604                     newViewDate = new Date(this.viewDate);
18605                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18606                 }
18607                 if (this.dateWithinRange(newDate)){
18608                     this.date = newDate;
18609                     this.viewDate = newViewDate;
18610                     this.setValue(this.formatDate(this.date));
18611 //                    this.update();
18612                     e.preventDefault();
18613                     dateChanged = true;
18614                 }
18615                 break;
18616             case 13: // enter
18617                 this.setValue(this.formatDate(this.date));
18618                 this.hide();
18619                 e.preventDefault();
18620                 break;
18621             case 9: // tab
18622                 this.setValue(this.formatDate(this.date));
18623                 this.hide();
18624                 break;
18625             case 16: // shift
18626             case 17: // ctrl
18627             case 18: // alt
18628                 break;
18629             default :
18630                 this.hide();
18631                 
18632         }
18633     },
18634     
18635     
18636     onClick: function(e) 
18637     {
18638         e.stopPropagation();
18639         e.preventDefault();
18640         
18641         var target = e.getTarget();
18642         
18643         if(target.nodeName.toLowerCase() === 'i'){
18644             target = Roo.get(target).dom.parentNode;
18645         }
18646         
18647         var nodeName = target.nodeName;
18648         var className = target.className;
18649         var html = target.innerHTML;
18650         //Roo.log(nodeName);
18651         
18652         switch(nodeName.toLowerCase()) {
18653             case 'th':
18654                 switch(className) {
18655                     case 'switch':
18656                         this.showMode(1);
18657                         break;
18658                     case 'prev':
18659                     case 'next':
18660                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18661                         switch(this.viewMode){
18662                                 case 0:
18663                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18664                                         break;
18665                                 case 1:
18666                                 case 2:
18667                                         this.viewDate = this.moveYear(this.viewDate, dir);
18668                                         break;
18669                         }
18670                         this.fill();
18671                         break;
18672                     case 'today':
18673                         var date = new Date();
18674                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18675 //                        this.fill()
18676                         this.setValue(this.formatDate(this.date));
18677                         
18678                         this.hide();
18679                         break;
18680                 }
18681                 break;
18682             case 'span':
18683                 if (className.indexOf('disabled') < 0) {
18684                     this.viewDate.setUTCDate(1);
18685                     if (className.indexOf('month') > -1) {
18686                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18687                     } else {
18688                         var year = parseInt(html, 10) || 0;
18689                         this.viewDate.setUTCFullYear(year);
18690                         
18691                     }
18692                     
18693                     if(this.singleMode){
18694                         this.setValue(this.formatDate(this.viewDate));
18695                         this.hide();
18696                         return;
18697                     }
18698                     
18699                     this.showMode(-1);
18700                     this.fill();
18701                 }
18702                 break;
18703                 
18704             case 'td':
18705                 //Roo.log(className);
18706                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18707                     var day = parseInt(html, 10) || 1;
18708                     var year = this.viewDate.getUTCFullYear(),
18709                         month = this.viewDate.getUTCMonth();
18710
18711                     if (className.indexOf('old') > -1) {
18712                         if(month === 0 ){
18713                             month = 11;
18714                             year -= 1;
18715                         }else{
18716                             month -= 1;
18717                         }
18718                     } else if (className.indexOf('new') > -1) {
18719                         if (month == 11) {
18720                             month = 0;
18721                             year += 1;
18722                         } else {
18723                             month += 1;
18724                         }
18725                     }
18726                     //Roo.log([year,month,day]);
18727                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18728                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18729 //                    this.fill();
18730                     //Roo.log(this.formatDate(this.date));
18731                     this.setValue(this.formatDate(this.date));
18732                     this.hide();
18733                 }
18734                 break;
18735         }
18736     },
18737     
18738     setStartDate: function(startDate)
18739     {
18740         this.startDate = startDate || -Infinity;
18741         if (this.startDate !== -Infinity) {
18742             this.startDate = this.parseDate(this.startDate);
18743         }
18744         this.update();
18745         this.updateNavArrows();
18746     },
18747
18748     setEndDate: function(endDate)
18749     {
18750         this.endDate = endDate || Infinity;
18751         if (this.endDate !== Infinity) {
18752             this.endDate = this.parseDate(this.endDate);
18753         }
18754         this.update();
18755         this.updateNavArrows();
18756     },
18757     
18758     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18759     {
18760         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18761         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18762             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18763         }
18764         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18765             return parseInt(d, 10);
18766         });
18767         this.update();
18768         this.updateNavArrows();
18769     },
18770     
18771     updateNavArrows: function() 
18772     {
18773         if(this.singleMode){
18774             return;
18775         }
18776         
18777         var d = new Date(this.viewDate),
18778         year = d.getUTCFullYear(),
18779         month = d.getUTCMonth();
18780         
18781         Roo.each(this.picker().select('.prev', true).elements, function(v){
18782             v.show();
18783             switch (this.viewMode) {
18784                 case 0:
18785
18786                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18787                         v.hide();
18788                     }
18789                     break;
18790                 case 1:
18791                 case 2:
18792                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18793                         v.hide();
18794                     }
18795                     break;
18796             }
18797         });
18798         
18799         Roo.each(this.picker().select('.next', true).elements, function(v){
18800             v.show();
18801             switch (this.viewMode) {
18802                 case 0:
18803
18804                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18805                         v.hide();
18806                     }
18807                     break;
18808                 case 1:
18809                 case 2:
18810                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18811                         v.hide();
18812                     }
18813                     break;
18814             }
18815         })
18816     },
18817     
18818     moveMonth: function(date, dir)
18819     {
18820         if (!dir) {
18821             return date;
18822         }
18823         var new_date = new Date(date.valueOf()),
18824         day = new_date.getUTCDate(),
18825         month = new_date.getUTCMonth(),
18826         mag = Math.abs(dir),
18827         new_month, test;
18828         dir = dir > 0 ? 1 : -1;
18829         if (mag == 1){
18830             test = dir == -1
18831             // If going back one month, make sure month is not current month
18832             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18833             ? function(){
18834                 return new_date.getUTCMonth() == month;
18835             }
18836             // If going forward one month, make sure month is as expected
18837             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18838             : function(){
18839                 return new_date.getUTCMonth() != new_month;
18840             };
18841             new_month = month + dir;
18842             new_date.setUTCMonth(new_month);
18843             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18844             if (new_month < 0 || new_month > 11) {
18845                 new_month = (new_month + 12) % 12;
18846             }
18847         } else {
18848             // For magnitudes >1, move one month at a time...
18849             for (var i=0; i<mag; i++) {
18850                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18851                 new_date = this.moveMonth(new_date, dir);
18852             }
18853             // ...then reset the day, keeping it in the new month
18854             new_month = new_date.getUTCMonth();
18855             new_date.setUTCDate(day);
18856             test = function(){
18857                 return new_month != new_date.getUTCMonth();
18858             };
18859         }
18860         // Common date-resetting loop -- if date is beyond end of month, make it
18861         // end of month
18862         while (test()){
18863             new_date.setUTCDate(--day);
18864             new_date.setUTCMonth(new_month);
18865         }
18866         return new_date;
18867     },
18868
18869     moveYear: function(date, dir)
18870     {
18871         return this.moveMonth(date, dir*12);
18872     },
18873
18874     dateWithinRange: function(date)
18875     {
18876         return date >= this.startDate && date <= this.endDate;
18877     },
18878
18879     
18880     remove: function() 
18881     {
18882         this.picker().remove();
18883     },
18884     
18885     validateValue : function(value)
18886     {
18887         if(value.length < 1)  {
18888             if(this.allowBlank){
18889                 return true;
18890             }
18891             return false;
18892         }
18893         
18894         if(value.length < this.minLength){
18895             return false;
18896         }
18897         if(value.length > this.maxLength){
18898             return false;
18899         }
18900         if(this.vtype){
18901             var vt = Roo.form.VTypes;
18902             if(!vt[this.vtype](value, this)){
18903                 return false;
18904             }
18905         }
18906         if(typeof this.validator == "function"){
18907             var msg = this.validator(value);
18908             if(msg !== true){
18909                 return false;
18910             }
18911         }
18912         
18913         if(this.regex && !this.regex.test(value)){
18914             return false;
18915         }
18916         
18917         if(typeof(this.parseDate(value)) == 'undefined'){
18918             return false;
18919         }
18920         
18921         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18922             return false;
18923         }      
18924         
18925         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18926             return false;
18927         } 
18928         
18929         
18930         return true;
18931     }
18932    
18933 });
18934
18935 Roo.apply(Roo.bootstrap.DateField,  {
18936     
18937     head : {
18938         tag: 'thead',
18939         cn: [
18940         {
18941             tag: 'tr',
18942             cn: [
18943             {
18944                 tag: 'th',
18945                 cls: 'prev',
18946                 html: '<i class="fa fa-arrow-left"/>'
18947             },
18948             {
18949                 tag: 'th',
18950                 cls: 'switch',
18951                 colspan: '5'
18952             },
18953             {
18954                 tag: 'th',
18955                 cls: 'next',
18956                 html: '<i class="fa fa-arrow-right"/>'
18957             }
18958
18959             ]
18960         }
18961         ]
18962     },
18963     
18964     content : {
18965         tag: 'tbody',
18966         cn: [
18967         {
18968             tag: 'tr',
18969             cn: [
18970             {
18971                 tag: 'td',
18972                 colspan: '7'
18973             }
18974             ]
18975         }
18976         ]
18977     },
18978     
18979     footer : {
18980         tag: 'tfoot',
18981         cn: [
18982         {
18983             tag: 'tr',
18984             cn: [
18985             {
18986                 tag: 'th',
18987                 colspan: '7',
18988                 cls: 'today'
18989             }
18990                     
18991             ]
18992         }
18993         ]
18994     },
18995     
18996     dates:{
18997         en: {
18998             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18999             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19000             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19001             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19002             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19003             today: "Today"
19004         }
19005     },
19006     
19007     modes: [
19008     {
19009         clsName: 'days',
19010         navFnc: 'Month',
19011         navStep: 1
19012     },
19013     {
19014         clsName: 'months',
19015         navFnc: 'FullYear',
19016         navStep: 1
19017     },
19018     {
19019         clsName: 'years',
19020         navFnc: 'FullYear',
19021         navStep: 10
19022     }]
19023 });
19024
19025 Roo.apply(Roo.bootstrap.DateField,  {
19026   
19027     template : {
19028         tag: 'div',
19029         cls: 'datepicker dropdown-menu roo-dynamic',
19030         cn: [
19031         {
19032             tag: 'div',
19033             cls: 'datepicker-days',
19034             cn: [
19035             {
19036                 tag: 'table',
19037                 cls: 'table-condensed',
19038                 cn:[
19039                 Roo.bootstrap.DateField.head,
19040                 {
19041                     tag: 'tbody'
19042                 },
19043                 Roo.bootstrap.DateField.footer
19044                 ]
19045             }
19046             ]
19047         },
19048         {
19049             tag: 'div',
19050             cls: 'datepicker-months',
19051             cn: [
19052             {
19053                 tag: 'table',
19054                 cls: 'table-condensed',
19055                 cn:[
19056                 Roo.bootstrap.DateField.head,
19057                 Roo.bootstrap.DateField.content,
19058                 Roo.bootstrap.DateField.footer
19059                 ]
19060             }
19061             ]
19062         },
19063         {
19064             tag: 'div',
19065             cls: 'datepicker-years',
19066             cn: [
19067             {
19068                 tag: 'table',
19069                 cls: 'table-condensed',
19070                 cn:[
19071                 Roo.bootstrap.DateField.head,
19072                 Roo.bootstrap.DateField.content,
19073                 Roo.bootstrap.DateField.footer
19074                 ]
19075             }
19076             ]
19077         }
19078         ]
19079     }
19080 });
19081
19082  
19083
19084  /*
19085  * - LGPL
19086  *
19087  * TimeField
19088  * 
19089  */
19090
19091 /**
19092  * @class Roo.bootstrap.TimeField
19093  * @extends Roo.bootstrap.Input
19094  * Bootstrap DateField class
19095  * 
19096  * 
19097  * @constructor
19098  * Create a new TimeField
19099  * @param {Object} config The config object
19100  */
19101
19102 Roo.bootstrap.TimeField = function(config){
19103     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19104     this.addEvents({
19105             /**
19106              * @event show
19107              * Fires when this field show.
19108              * @param {Roo.bootstrap.DateField} thisthis
19109              * @param {Mixed} date The date value
19110              */
19111             show : true,
19112             /**
19113              * @event show
19114              * Fires when this field hide.
19115              * @param {Roo.bootstrap.DateField} this
19116              * @param {Mixed} date The date value
19117              */
19118             hide : true,
19119             /**
19120              * @event select
19121              * Fires when select a date.
19122              * @param {Roo.bootstrap.DateField} this
19123              * @param {Mixed} date The date value
19124              */
19125             select : true
19126         });
19127 };
19128
19129 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19130     
19131     /**
19132      * @cfg {String} format
19133      * The default time format string which can be overriden for localization support.  The format must be
19134      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19135      */
19136     format : "H:i",
19137        
19138     onRender: function(ct, position)
19139     {
19140         
19141         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19142                 
19143         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19144         
19145         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19146         
19147         this.pop = this.picker().select('>.datepicker-time',true).first();
19148         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19149         
19150         this.picker().on('mousedown', this.onMousedown, this);
19151         this.picker().on('click', this.onClick, this);
19152         
19153         this.picker().addClass('datepicker-dropdown');
19154     
19155         this.fillTime();
19156         this.update();
19157             
19158         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19159         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19160         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19161         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19162         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19163         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19164
19165     },
19166     
19167     fireKey: function(e){
19168         if (!this.picker().isVisible()){
19169             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19170                 this.show();
19171             }
19172             return;
19173         }
19174
19175         e.preventDefault();
19176         
19177         switch(e.keyCode){
19178             case 27: // escape
19179                 this.hide();
19180                 break;
19181             case 37: // left
19182             case 39: // right
19183                 this.onTogglePeriod();
19184                 break;
19185             case 38: // up
19186                 this.onIncrementMinutes();
19187                 break;
19188             case 40: // down
19189                 this.onDecrementMinutes();
19190                 break;
19191             case 13: // enter
19192             case 9: // tab
19193                 this.setTime();
19194                 break;
19195         }
19196     },
19197     
19198     onClick: function(e) {
19199         e.stopPropagation();
19200         e.preventDefault();
19201     },
19202     
19203     picker : function()
19204     {
19205         return this.el.select('.datepicker', true).first();
19206     },
19207     
19208     fillTime: function()
19209     {    
19210         var time = this.pop.select('tbody', true).first();
19211         
19212         time.dom.innerHTML = '';
19213         
19214         time.createChild({
19215             tag: 'tr',
19216             cn: [
19217                 {
19218                     tag: 'td',
19219                     cn: [
19220                         {
19221                             tag: 'a',
19222                             href: '#',
19223                             cls: 'btn',
19224                             cn: [
19225                                 {
19226                                     tag: 'span',
19227                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19228                                 }
19229                             ]
19230                         } 
19231                     ]
19232                 },
19233                 {
19234                     tag: 'td',
19235                     cls: 'separator'
19236                 },
19237                 {
19238                     tag: 'td',
19239                     cn: [
19240                         {
19241                             tag: 'a',
19242                             href: '#',
19243                             cls: 'btn',
19244                             cn: [
19245                                 {
19246                                     tag: 'span',
19247                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19248                                 }
19249                             ]
19250                         }
19251                     ]
19252                 },
19253                 {
19254                     tag: 'td',
19255                     cls: 'separator'
19256                 }
19257             ]
19258         });
19259         
19260         time.createChild({
19261             tag: 'tr',
19262             cn: [
19263                 {
19264                     tag: 'td',
19265                     cn: [
19266                         {
19267                             tag: 'span',
19268                             cls: 'timepicker-hour',
19269                             html: '00'
19270                         }  
19271                     ]
19272                 },
19273                 {
19274                     tag: 'td',
19275                     cls: 'separator',
19276                     html: ':'
19277                 },
19278                 {
19279                     tag: 'td',
19280                     cn: [
19281                         {
19282                             tag: 'span',
19283                             cls: 'timepicker-minute',
19284                             html: '00'
19285                         }  
19286                     ]
19287                 },
19288                 {
19289                     tag: 'td',
19290                     cls: 'separator'
19291                 },
19292                 {
19293                     tag: 'td',
19294                     cn: [
19295                         {
19296                             tag: 'button',
19297                             type: 'button',
19298                             cls: 'btn btn-primary period',
19299                             html: 'AM'
19300                             
19301                         }
19302                     ]
19303                 }
19304             ]
19305         });
19306         
19307         time.createChild({
19308             tag: 'tr',
19309             cn: [
19310                 {
19311                     tag: 'td',
19312                     cn: [
19313                         {
19314                             tag: 'a',
19315                             href: '#',
19316                             cls: 'btn',
19317                             cn: [
19318                                 {
19319                                     tag: 'span',
19320                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19321                                 }
19322                             ]
19323                         }
19324                     ]
19325                 },
19326                 {
19327                     tag: 'td',
19328                     cls: 'separator'
19329                 },
19330                 {
19331                     tag: 'td',
19332                     cn: [
19333                         {
19334                             tag: 'a',
19335                             href: '#',
19336                             cls: 'btn',
19337                             cn: [
19338                                 {
19339                                     tag: 'span',
19340                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19341                                 }
19342                             ]
19343                         }
19344                     ]
19345                 },
19346                 {
19347                     tag: 'td',
19348                     cls: 'separator'
19349                 }
19350             ]
19351         });
19352         
19353     },
19354     
19355     update: function()
19356     {
19357         
19358         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19359         
19360         this.fill();
19361     },
19362     
19363     fill: function() 
19364     {
19365         var hours = this.time.getHours();
19366         var minutes = this.time.getMinutes();
19367         var period = 'AM';
19368         
19369         if(hours > 11){
19370             period = 'PM';
19371         }
19372         
19373         if(hours == 0){
19374             hours = 12;
19375         }
19376         
19377         
19378         if(hours > 12){
19379             hours = hours - 12;
19380         }
19381         
19382         if(hours < 10){
19383             hours = '0' + hours;
19384         }
19385         
19386         if(minutes < 10){
19387             minutes = '0' + minutes;
19388         }
19389         
19390         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19391         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19392         this.pop.select('button', true).first().dom.innerHTML = period;
19393         
19394     },
19395     
19396     place: function()
19397     {   
19398         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19399         
19400         var cls = ['bottom'];
19401         
19402         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19403             cls.pop();
19404             cls.push('top');
19405         }
19406         
19407         cls.push('right');
19408         
19409         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19410             cls.pop();
19411             cls.push('left');
19412         }
19413         
19414         this.picker().addClass(cls.join('-'));
19415         
19416         var _this = this;
19417         
19418         Roo.each(cls, function(c){
19419             if(c == 'bottom'){
19420                 _this.picker().setTop(_this.inputEl().getHeight());
19421                 return;
19422             }
19423             if(c == 'top'){
19424                 _this.picker().setTop(0 - _this.picker().getHeight());
19425                 return;
19426             }
19427             
19428             if(c == 'left'){
19429                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19430                 return;
19431             }
19432             if(c == 'right'){
19433                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19434                 return;
19435             }
19436         });
19437         
19438     },
19439   
19440     onFocus : function()
19441     {
19442         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19443         this.show();
19444     },
19445     
19446     onBlur : function()
19447     {
19448         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19449         this.hide();
19450     },
19451     
19452     show : function()
19453     {
19454         this.picker().show();
19455         this.pop.show();
19456         this.update();
19457         this.place();
19458         
19459         this.fireEvent('show', this, this.date);
19460     },
19461     
19462     hide : function()
19463     {
19464         this.picker().hide();
19465         this.pop.hide();
19466         
19467         this.fireEvent('hide', this, this.date);
19468     },
19469     
19470     setTime : function()
19471     {
19472         this.hide();
19473         this.setValue(this.time.format(this.format));
19474         
19475         this.fireEvent('select', this, this.date);
19476         
19477         
19478     },
19479     
19480     onMousedown: function(e){
19481         e.stopPropagation();
19482         e.preventDefault();
19483     },
19484     
19485     onIncrementHours: function()
19486     {
19487         Roo.log('onIncrementHours');
19488         this.time = this.time.add(Date.HOUR, 1);
19489         this.update();
19490         
19491     },
19492     
19493     onDecrementHours: function()
19494     {
19495         Roo.log('onDecrementHours');
19496         this.time = this.time.add(Date.HOUR, -1);
19497         this.update();
19498     },
19499     
19500     onIncrementMinutes: function()
19501     {
19502         Roo.log('onIncrementMinutes');
19503         this.time = this.time.add(Date.MINUTE, 1);
19504         this.update();
19505     },
19506     
19507     onDecrementMinutes: function()
19508     {
19509         Roo.log('onDecrementMinutes');
19510         this.time = this.time.add(Date.MINUTE, -1);
19511         this.update();
19512     },
19513     
19514     onTogglePeriod: function()
19515     {
19516         Roo.log('onTogglePeriod');
19517         this.time = this.time.add(Date.HOUR, 12);
19518         this.update();
19519     }
19520     
19521    
19522 });
19523
19524 Roo.apply(Roo.bootstrap.TimeField,  {
19525     
19526     content : {
19527         tag: 'tbody',
19528         cn: [
19529             {
19530                 tag: 'tr',
19531                 cn: [
19532                 {
19533                     tag: 'td',
19534                     colspan: '7'
19535                 }
19536                 ]
19537             }
19538         ]
19539     },
19540     
19541     footer : {
19542         tag: 'tfoot',
19543         cn: [
19544             {
19545                 tag: 'tr',
19546                 cn: [
19547                 {
19548                     tag: 'th',
19549                     colspan: '7',
19550                     cls: '',
19551                     cn: [
19552                         {
19553                             tag: 'button',
19554                             cls: 'btn btn-info ok',
19555                             html: 'OK'
19556                         }
19557                     ]
19558                 }
19559
19560                 ]
19561             }
19562         ]
19563     }
19564 });
19565
19566 Roo.apply(Roo.bootstrap.TimeField,  {
19567   
19568     template : {
19569         tag: 'div',
19570         cls: 'datepicker dropdown-menu',
19571         cn: [
19572             {
19573                 tag: 'div',
19574                 cls: 'datepicker-time',
19575                 cn: [
19576                 {
19577                     tag: 'table',
19578                     cls: 'table-condensed',
19579                     cn:[
19580                     Roo.bootstrap.TimeField.content,
19581                     Roo.bootstrap.TimeField.footer
19582                     ]
19583                 }
19584                 ]
19585             }
19586         ]
19587     }
19588 });
19589
19590  
19591
19592  /*
19593  * - LGPL
19594  *
19595  * MonthField
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.MonthField
19601  * @extends Roo.bootstrap.Input
19602  * Bootstrap MonthField class
19603  * 
19604  * @cfg {String} language default en
19605  * 
19606  * @constructor
19607  * Create a new MonthField
19608  * @param {Object} config The config object
19609  */
19610
19611 Roo.bootstrap.MonthField = function(config){
19612     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19613     
19614     this.addEvents({
19615         /**
19616          * @event show
19617          * Fires when this field show.
19618          * @param {Roo.bootstrap.MonthField} this
19619          * @param {Mixed} date The date value
19620          */
19621         show : true,
19622         /**
19623          * @event show
19624          * Fires when this field hide.
19625          * @param {Roo.bootstrap.MonthField} this
19626          * @param {Mixed} date The date value
19627          */
19628         hide : true,
19629         /**
19630          * @event select
19631          * Fires when select a date.
19632          * @param {Roo.bootstrap.MonthField} this
19633          * @param {String} oldvalue The old value
19634          * @param {String} newvalue The new value
19635          */
19636         select : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19641     
19642     onRender: function(ct, position)
19643     {
19644         
19645         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19646         
19647         this.language = this.language || 'en';
19648         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19649         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19650         
19651         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19652         this.isInline = false;
19653         this.isInput = true;
19654         this.component = this.el.select('.add-on', true).first() || false;
19655         this.component = (this.component && this.component.length === 0) ? false : this.component;
19656         this.hasInput = this.component && this.inputEL().length;
19657         
19658         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19659         
19660         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19661         
19662         this.picker().on('mousedown', this.onMousedown, this);
19663         this.picker().on('click', this.onClick, this);
19664         
19665         this.picker().addClass('datepicker-dropdown');
19666         
19667         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19668             v.setStyle('width', '189px');
19669         });
19670         
19671         this.fillMonths();
19672         
19673         this.update();
19674         
19675         if(this.isInline) {
19676             this.show();
19677         }
19678         
19679     },
19680     
19681     setValue: function(v, suppressEvent)
19682     {   
19683         var o = this.getValue();
19684         
19685         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19686         
19687         this.update();
19688
19689         if(suppressEvent !== true){
19690             this.fireEvent('select', this, o, v);
19691         }
19692         
19693     },
19694     
19695     getValue: function()
19696     {
19697         return this.value;
19698     },
19699     
19700     onClick: function(e) 
19701     {
19702         e.stopPropagation();
19703         e.preventDefault();
19704         
19705         var target = e.getTarget();
19706         
19707         if(target.nodeName.toLowerCase() === 'i'){
19708             target = Roo.get(target).dom.parentNode;
19709         }
19710         
19711         var nodeName = target.nodeName;
19712         var className = target.className;
19713         var html = target.innerHTML;
19714         
19715         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19716             return;
19717         }
19718         
19719         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19720         
19721         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19722         
19723         this.hide();
19724                         
19725     },
19726     
19727     picker : function()
19728     {
19729         return this.pickerEl;
19730     },
19731     
19732     fillMonths: function()
19733     {    
19734         var i = 0;
19735         var months = this.picker().select('>.datepicker-months td', true).first();
19736         
19737         months.dom.innerHTML = '';
19738         
19739         while (i < 12) {
19740             var month = {
19741                 tag: 'span',
19742                 cls: 'month',
19743                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19744             };
19745             
19746             months.createChild(month);
19747         }
19748         
19749     },
19750     
19751     update: function()
19752     {
19753         var _this = this;
19754         
19755         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19756             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19757         }
19758         
19759         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19760             e.removeClass('active');
19761             
19762             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19763                 e.addClass('active');
19764             }
19765         })
19766     },
19767     
19768     place: function()
19769     {
19770         if(this.isInline) {
19771             return;
19772         }
19773         
19774         this.picker().removeClass(['bottom', 'top']);
19775         
19776         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19777             /*
19778              * place to the top of element!
19779              *
19780              */
19781             
19782             this.picker().addClass('top');
19783             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19784             
19785             return;
19786         }
19787         
19788         this.picker().addClass('bottom');
19789         
19790         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19791     },
19792     
19793     onFocus : function()
19794     {
19795         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19796         this.show();
19797     },
19798     
19799     onBlur : function()
19800     {
19801         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19802         
19803         var d = this.inputEl().getValue();
19804         
19805         this.setValue(d);
19806                 
19807         this.hide();
19808     },
19809     
19810     show : function()
19811     {
19812         this.picker().show();
19813         this.picker().select('>.datepicker-months', true).first().show();
19814         this.update();
19815         this.place();
19816         
19817         this.fireEvent('show', this, this.date);
19818     },
19819     
19820     hide : function()
19821     {
19822         if(this.isInline) {
19823             return;
19824         }
19825         this.picker().hide();
19826         this.fireEvent('hide', this, this.date);
19827         
19828     },
19829     
19830     onMousedown: function(e)
19831     {
19832         e.stopPropagation();
19833         e.preventDefault();
19834     },
19835     
19836     keyup: function(e)
19837     {
19838         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19839         this.update();
19840     },
19841
19842     fireKey: function(e)
19843     {
19844         if (!this.picker().isVisible()){
19845             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19846                 this.show();
19847             }
19848             return;
19849         }
19850         
19851         var dir;
19852         
19853         switch(e.keyCode){
19854             case 27: // escape
19855                 this.hide();
19856                 e.preventDefault();
19857                 break;
19858             case 37: // left
19859             case 39: // right
19860                 dir = e.keyCode == 37 ? -1 : 1;
19861                 
19862                 this.vIndex = this.vIndex + dir;
19863                 
19864                 if(this.vIndex < 0){
19865                     this.vIndex = 0;
19866                 }
19867                 
19868                 if(this.vIndex > 11){
19869                     this.vIndex = 11;
19870                 }
19871                 
19872                 if(isNaN(this.vIndex)){
19873                     this.vIndex = 0;
19874                 }
19875                 
19876                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19877                 
19878                 break;
19879             case 38: // up
19880             case 40: // down
19881                 
19882                 dir = e.keyCode == 38 ? -1 : 1;
19883                 
19884                 this.vIndex = this.vIndex + dir * 4;
19885                 
19886                 if(this.vIndex < 0){
19887                     this.vIndex = 0;
19888                 }
19889                 
19890                 if(this.vIndex > 11){
19891                     this.vIndex = 11;
19892                 }
19893                 
19894                 if(isNaN(this.vIndex)){
19895                     this.vIndex = 0;
19896                 }
19897                 
19898                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19899                 break;
19900                 
19901             case 13: // enter
19902                 
19903                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19904                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19905                 }
19906                 
19907                 this.hide();
19908                 e.preventDefault();
19909                 break;
19910             case 9: // tab
19911                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19912                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19913                 }
19914                 this.hide();
19915                 break;
19916             case 16: // shift
19917             case 17: // ctrl
19918             case 18: // alt
19919                 break;
19920             default :
19921                 this.hide();
19922                 
19923         }
19924     },
19925     
19926     remove: function() 
19927     {
19928         this.picker().remove();
19929     }
19930    
19931 });
19932
19933 Roo.apply(Roo.bootstrap.MonthField,  {
19934     
19935     content : {
19936         tag: 'tbody',
19937         cn: [
19938         {
19939             tag: 'tr',
19940             cn: [
19941             {
19942                 tag: 'td',
19943                 colspan: '7'
19944             }
19945             ]
19946         }
19947         ]
19948     },
19949     
19950     dates:{
19951         en: {
19952             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19953             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19954         }
19955     }
19956 });
19957
19958 Roo.apply(Roo.bootstrap.MonthField,  {
19959   
19960     template : {
19961         tag: 'div',
19962         cls: 'datepicker dropdown-menu roo-dynamic',
19963         cn: [
19964             {
19965                 tag: 'div',
19966                 cls: 'datepicker-months',
19967                 cn: [
19968                 {
19969                     tag: 'table',
19970                     cls: 'table-condensed',
19971                     cn:[
19972                         Roo.bootstrap.DateField.content
19973                     ]
19974                 }
19975                 ]
19976             }
19977         ]
19978     }
19979 });
19980
19981  
19982
19983  
19984  /*
19985  * - LGPL
19986  *
19987  * CheckBox
19988  * 
19989  */
19990
19991 /**
19992  * @class Roo.bootstrap.CheckBox
19993  * @extends Roo.bootstrap.Input
19994  * Bootstrap CheckBox class
19995  * 
19996  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19997  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19998  * @cfg {String} boxLabel The text that appears beside the checkbox
19999  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20000  * @cfg {Boolean} checked initnal the element
20001  * @cfg {Boolean} inline inline the element (default false)
20002  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20003  * 
20004  * @constructor
20005  * Create a new CheckBox
20006  * @param {Object} config The config object
20007  */
20008
20009 Roo.bootstrap.CheckBox = function(config){
20010     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20011    
20012     this.addEvents({
20013         /**
20014         * @event check
20015         * Fires when the element is checked or unchecked.
20016         * @param {Roo.bootstrap.CheckBox} this This input
20017         * @param {Boolean} checked The new checked value
20018         */
20019        check : true
20020     });
20021     
20022 };
20023
20024 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20025   
20026     inputType: 'checkbox',
20027     inputValue: 1,
20028     valueOff: 0,
20029     boxLabel: false,
20030     checked: false,
20031     weight : false,
20032     inline: false,
20033     
20034     getAutoCreate : function()
20035     {
20036         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20037         
20038         var id = Roo.id();
20039         
20040         var cfg = {};
20041         
20042         cfg.cls = 'form-group ' + this.inputType; //input-group
20043         
20044         if(this.inline){
20045             cfg.cls += ' ' + this.inputType + '-inline';
20046         }
20047         
20048         var input =  {
20049             tag: 'input',
20050             id : id,
20051             type : this.inputType,
20052             value : this.inputValue,
20053             cls : 'roo-' + this.inputType, //'form-box',
20054             placeholder : this.placeholder || ''
20055             
20056         };
20057         
20058         if(this.inputType != 'radio'){
20059             var hidden =  {
20060                 tag: 'input',
20061                 type : 'hidden',
20062                 cls : 'roo-hidden-value',
20063                 value : this.checked ? this.valueOff : this.inputValue
20064             };
20065         }
20066         
20067             
20068         if (this.weight) { // Validity check?
20069             cfg.cls += " " + this.inputType + "-" + this.weight;
20070         }
20071         
20072         if (this.disabled) {
20073             input.disabled=true;
20074         }
20075         
20076         if(this.checked){
20077             input.checked = this.checked;
20078             
20079         }
20080         
20081         
20082         if (this.name) {
20083             
20084             input.name = this.name;
20085             
20086             if(this.inputType != 'radio'){
20087                 hidden.name = this.name;
20088                 input.name = '_hidden_' + this.name;
20089             }
20090         }
20091         
20092         if (this.size) {
20093             input.cls += ' input-' + this.size;
20094         }
20095         
20096         var settings=this;
20097         
20098         ['xs','sm','md','lg'].map(function(size){
20099             if (settings[size]) {
20100                 cfg.cls += ' col-' + size + '-' + settings[size];
20101             }
20102         });
20103         
20104         var inputblock = input;
20105          
20106         if (this.before || this.after) {
20107             
20108             inputblock = {
20109                 cls : 'input-group',
20110                 cn :  [] 
20111             };
20112             
20113             if (this.before) {
20114                 inputblock.cn.push({
20115                     tag :'span',
20116                     cls : 'input-group-addon',
20117                     html : this.before
20118                 });
20119             }
20120             
20121             inputblock.cn.push(input);
20122             
20123             if(this.inputType != 'radio'){
20124                 inputblock.cn.push(hidden);
20125             }
20126             
20127             if (this.after) {
20128                 inputblock.cn.push({
20129                     tag :'span',
20130                     cls : 'input-group-addon',
20131                     html : this.after
20132                 });
20133             }
20134             
20135         }
20136         
20137         if (align ==='left' && this.fieldLabel.length) {
20138 //                Roo.log("left and has label");
20139             cfg.cn = [
20140                 {
20141                     tag: 'label',
20142                     'for' :  id,
20143                     cls : 'control-label',
20144                     html : this.fieldLabel
20145
20146                 },
20147                 {
20148                     cls : "", 
20149                     cn: [
20150                         inputblock
20151                     ]
20152                 }
20153             ];
20154             
20155             if(this.labelWidth > 12){
20156                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20157             }
20158             
20159             if(this.labelWidth < 13 && this.labelmd == 0){
20160                 this.labelmd = this.labelWidth;
20161             }
20162             
20163             if(this.labellg > 0){
20164                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20165                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20166             }
20167             
20168             if(this.labelmd > 0){
20169                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20170                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20171             }
20172             
20173             if(this.labelsm > 0){
20174                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20175                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20176             }
20177             
20178             if(this.labelxs > 0){
20179                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20180                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20181             }
20182             
20183         } else if ( this.fieldLabel.length) {
20184 //                Roo.log(" label");
20185                 cfg.cn = [
20186                    
20187                     {
20188                         tag: this.boxLabel ? 'span' : 'label',
20189                         'for': id,
20190                         cls: 'control-label box-input-label',
20191                         //cls : 'input-group-addon',
20192                         html : this.fieldLabel
20193                         
20194                     },
20195                     
20196                     inputblock
20197                     
20198                 ];
20199
20200         } else {
20201             
20202 //                Roo.log(" no label && no align");
20203                 cfg.cn = [  inputblock ] ;
20204                 
20205                 
20206         }
20207         
20208         if(this.boxLabel){
20209              var boxLabelCfg = {
20210                 tag: 'label',
20211                 //'for': id, // box label is handled by onclick - so no for...
20212                 cls: 'box-label',
20213                 html: this.boxLabel
20214             };
20215             
20216             if(this.tooltip){
20217                 boxLabelCfg.tooltip = this.tooltip;
20218             }
20219              
20220             cfg.cn.push(boxLabelCfg);
20221         }
20222         
20223         if(this.inputType != 'radio'){
20224             cfg.cn.push(hidden);
20225         }
20226         
20227         return cfg;
20228         
20229     },
20230     
20231     /**
20232      * return the real input element.
20233      */
20234     inputEl: function ()
20235     {
20236         return this.el.select('input.roo-' + this.inputType,true).first();
20237     },
20238     hiddenEl: function ()
20239     {
20240         return this.el.select('input.roo-hidden-value',true).first();
20241     },
20242     
20243     labelEl: function()
20244     {
20245         return this.el.select('label.control-label',true).first();
20246     },
20247     /* depricated... */
20248     
20249     label: function()
20250     {
20251         return this.labelEl();
20252     },
20253     
20254     boxLabelEl: function()
20255     {
20256         return this.el.select('label.box-label',true).first();
20257     },
20258     
20259     initEvents : function()
20260     {
20261 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20262         
20263         this.inputEl().on('click', this.onClick,  this);
20264         
20265         if (this.boxLabel) { 
20266             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20267         }
20268         
20269         this.startValue = this.getValue();
20270         
20271         if(this.groupId){
20272             Roo.bootstrap.CheckBox.register(this);
20273         }
20274     },
20275     
20276     onClick : function()
20277     {   
20278         this.setChecked(!this.checked);
20279     },
20280     
20281     setChecked : function(state,suppressEvent)
20282     {
20283         this.startValue = this.getValue();
20284
20285         if(this.inputType == 'radio'){
20286             
20287             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20288                 e.dom.checked = false;
20289             });
20290             
20291             this.inputEl().dom.checked = true;
20292             
20293             this.inputEl().dom.value = this.inputValue;
20294             
20295             if(suppressEvent !== true){
20296                 this.fireEvent('check', this, true);
20297             }
20298             
20299             this.validate();
20300             
20301             return;
20302         }
20303         
20304         this.checked = state;
20305         
20306         this.inputEl().dom.checked = state;
20307         
20308         
20309         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20310         
20311         if(suppressEvent !== true){
20312             this.fireEvent('check', this, state);
20313         }
20314         
20315         this.validate();
20316     },
20317     
20318     getValue : function()
20319     {
20320         if(this.inputType == 'radio'){
20321             return this.getGroupValue();
20322         }
20323         
20324         return this.hiddenEl().dom.value;
20325         
20326     },
20327     
20328     getGroupValue : function()
20329     {
20330         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20331             return '';
20332         }
20333         
20334         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20335     },
20336     
20337     setValue : function(v,suppressEvent)
20338     {
20339         if(this.inputType == 'radio'){
20340             this.setGroupValue(v, suppressEvent);
20341             return;
20342         }
20343         
20344         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20345         
20346         this.validate();
20347     },
20348     
20349     setGroupValue : function(v, suppressEvent)
20350     {
20351         this.startValue = this.getValue();
20352         
20353         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20354             e.dom.checked = false;
20355             
20356             if(e.dom.value == v){
20357                 e.dom.checked = true;
20358             }
20359         });
20360         
20361         if(suppressEvent !== true){
20362             this.fireEvent('check', this, true);
20363         }
20364
20365         this.validate();
20366         
20367         return;
20368     },
20369     
20370     validate : function()
20371     {
20372         if(
20373                 this.disabled || 
20374                 (this.inputType == 'radio' && this.validateRadio()) ||
20375                 (this.inputType == 'checkbox' && this.validateCheckbox())
20376         ){
20377             this.markValid();
20378             return true;
20379         }
20380         
20381         this.markInvalid();
20382         return false;
20383     },
20384     
20385     validateRadio : function()
20386     {
20387         if(this.allowBlank){
20388             return true;
20389         }
20390         
20391         var valid = false;
20392         
20393         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20394             if(!e.dom.checked){
20395                 return;
20396             }
20397             
20398             valid = true;
20399             
20400             return false;
20401         });
20402         
20403         return valid;
20404     },
20405     
20406     validateCheckbox : function()
20407     {
20408         if(!this.groupId){
20409             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20410             //return (this.getValue() == this.inputValue) ? true : false;
20411         }
20412         
20413         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20414         
20415         if(!group){
20416             return false;
20417         }
20418         
20419         var r = false;
20420         
20421         for(var i in group){
20422             if(r){
20423                 break;
20424             }
20425             
20426             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20427         }
20428         
20429         return r;
20430     },
20431     
20432     /**
20433      * Mark this field as valid
20434      */
20435     markValid : function()
20436     {
20437         var _this = this;
20438         
20439         this.fireEvent('valid', this);
20440         
20441         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20442         
20443         if(this.groupId){
20444             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20445         }
20446         
20447         if(label){
20448             label.markValid();
20449         }
20450
20451         if(this.inputType == 'radio'){
20452             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20453                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20454                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20455             });
20456             
20457             return;
20458         }
20459
20460         if(!this.groupId){
20461             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20462             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20463             return;
20464         }
20465         
20466         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20467         
20468         if(!group){
20469             return;
20470         }
20471         
20472         for(var i in group){
20473             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20474             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20475         }
20476     },
20477     
20478      /**
20479      * Mark this field as invalid
20480      * @param {String} msg The validation message
20481      */
20482     markInvalid : function(msg)
20483     {
20484         if(this.allowBlank){
20485             return;
20486         }
20487         
20488         var _this = this;
20489         
20490         this.fireEvent('invalid', this, msg);
20491         
20492         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20493         
20494         if(this.groupId){
20495             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20496         }
20497         
20498         if(label){
20499             label.markInvalid();
20500         }
20501             
20502         if(this.inputType == 'radio'){
20503             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20504                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20505                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20506             });
20507             
20508             return;
20509         }
20510         
20511         if(!this.groupId){
20512             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20513             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20514             return;
20515         }
20516         
20517         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20518         
20519         if(!group){
20520             return;
20521         }
20522         
20523         for(var i in group){
20524             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20525             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20526         }
20527         
20528     },
20529     
20530     clearInvalid : function()
20531     {
20532         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20533         
20534         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20535         
20536         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20537         
20538         if (label) {
20539             label.iconEl.removeClass(label.validClass);
20540             label.iconEl.removeClass(label.invalidClass);
20541         }
20542     },
20543     
20544     disable : function()
20545     {
20546         if(this.inputType != 'radio'){
20547             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20548             return;
20549         }
20550         
20551         var _this = this;
20552         
20553         if(this.rendered){
20554             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20555                 _this.getActionEl().addClass(this.disabledClass);
20556                 e.dom.disabled = true;
20557             });
20558         }
20559         
20560         this.disabled = true;
20561         this.fireEvent("disable", this);
20562         return this;
20563     },
20564
20565     enable : function()
20566     {
20567         if(this.inputType != 'radio'){
20568             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20569             return;
20570         }
20571         
20572         var _this = this;
20573         
20574         if(this.rendered){
20575             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20576                 _this.getActionEl().removeClass(this.disabledClass);
20577                 e.dom.disabled = false;
20578             });
20579         }
20580         
20581         this.disabled = false;
20582         this.fireEvent("enable", this);
20583         return this;
20584     }
20585
20586 });
20587
20588 Roo.apply(Roo.bootstrap.CheckBox, {
20589     
20590     groups: {},
20591     
20592      /**
20593     * register a CheckBox Group
20594     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20595     */
20596     register : function(checkbox)
20597     {
20598         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20599             this.groups[checkbox.groupId] = {};
20600         }
20601         
20602         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20603             return;
20604         }
20605         
20606         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20607         
20608     },
20609     /**
20610     * fetch a CheckBox Group based on the group ID
20611     * @param {string} the group ID
20612     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20613     */
20614     get: function(groupId) {
20615         if (typeof(this.groups[groupId]) == 'undefined') {
20616             return false;
20617         }
20618         
20619         return this.groups[groupId] ;
20620     }
20621     
20622     
20623 });
20624 /*
20625  * - LGPL
20626  *
20627  * RadioItem
20628  * 
20629  */
20630
20631 /**
20632  * @class Roo.bootstrap.Radio
20633  * @extends Roo.bootstrap.Component
20634  * Bootstrap Radio class
20635  * @cfg {String} boxLabel - the label associated
20636  * @cfg {String} value - the value of radio
20637  * 
20638  * @constructor
20639  * Create a new Radio
20640  * @param {Object} config The config object
20641  */
20642 Roo.bootstrap.Radio = function(config){
20643     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20644     
20645 };
20646
20647 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20648     
20649     boxLabel : '',
20650     
20651     value : '',
20652     
20653     getAutoCreate : function()
20654     {
20655         var cfg = {
20656             tag : 'div',
20657             cls : 'form-group radio',
20658             cn : [
20659                 {
20660                     tag : 'label',
20661                     cls : 'box-label',
20662                     html : this.boxLabel
20663                 }
20664             ]
20665         };
20666         
20667         return cfg;
20668     },
20669     
20670     initEvents : function() 
20671     {
20672         this.parent().register(this);
20673         
20674         this.el.on('click', this.onClick, this);
20675         
20676     },
20677     
20678     onClick : function()
20679     {
20680         this.setChecked(true);
20681     },
20682     
20683     setChecked : function(state, suppressEvent)
20684     {
20685         this.parent().setValue(this.value, suppressEvent);
20686         
20687     }
20688     
20689 });
20690  
20691
20692  /*
20693  * - LGPL
20694  *
20695  * Input
20696  * 
20697  */
20698
20699 /**
20700  * @class Roo.bootstrap.SecurePass
20701  * @extends Roo.bootstrap.Input
20702  * Bootstrap SecurePass class
20703  *
20704  * 
20705  * @constructor
20706  * Create a new SecurePass
20707  * @param {Object} config The config object
20708  */
20709  
20710 Roo.bootstrap.SecurePass = function (config) {
20711     // these go here, so the translation tool can replace them..
20712     this.errors = {
20713         PwdEmpty: "Please type a password, and then retype it to confirm.",
20714         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20715         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20716         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20717         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20718         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20719         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20720         TooWeak: "Your password is Too Weak."
20721     },
20722     this.meterLabel = "Password strength:";
20723     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20724     this.meterClass = ["roo-password-meter-tooweak", 
20725                        "roo-password-meter-weak", 
20726                        "roo-password-meter-medium", 
20727                        "roo-password-meter-strong", 
20728                        "roo-password-meter-grey"],    
20729     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20730 }
20731
20732 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20733     /**
20734      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20735      * {
20736      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20737      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20738      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20739      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20740      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20741      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20742      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20743      * })
20744      */
20745     // private
20746     
20747     meterWidth: 300,
20748     errorMsg :'',    
20749     errors: {},
20750     imageRoot: '/',
20751     /**
20752      * @cfg {String/Object} Label for the strength meter (defaults to
20753      * 'Password strength:')
20754      */
20755     // private
20756     meterLabel: '',
20757     /**
20758      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20759      * ['Weak', 'Medium', 'Strong'])
20760      */
20761     // private    
20762     pwdStrengths: [],    
20763     // private
20764     strength: 0,
20765     // private
20766     _lastPwd: null,
20767     // private
20768     kCapitalLetter: 0,
20769     kSmallLetter: 1,
20770     kDigit: 2,
20771     kPunctuation: 3,
20772     
20773     insecure: false,
20774     // private
20775     initEvents: function () {
20776         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20777
20778         if (this.el.is('input[type=password]') && Roo.isSafari) {
20779             this.el.on('keydown', this.SafariOnKeyDown, this);
20780         }
20781
20782         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20783     },
20784     // private
20785     onRender: function (ct, position) {
20786         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20787         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20788         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20789
20790         this.trigger.createChild({
20791                    cn: [
20792                     {
20793                     //id: 'PwdMeter',
20794                     tag: 'div',
20795                     cls: 'roo-password-meter-grey col-xs-12',
20796                     style: {
20797                         //width: 0,
20798                         //width: this.meterWidth + 'px'                                                
20799                         }
20800                     },
20801                     {                            
20802                          cls: 'roo-password-meter-text'                          
20803                     }
20804                 ]            
20805         });
20806
20807         /*
20808         this.trigger.createChild({
20809             tag: 'div',
20810             cls: 'roo-password-meter-container col-xs-12',
20811             style: {               
20812                 //width: this.meterWidth + 'px'
20813             },
20814             cn: {
20815                 tag: 'div',
20816                 cls: 'roo-password-meter-grey',
20817                 style: {
20818                     //width: this.meterWidth + 'px'                                        
20819                 },
20820                 cn: [
20821                     {
20822                     //id: 'PwdMeter',
20823                     tag: 'div',
20824                     cls: 'roo-password-meter-grey col-xs-12',
20825                     style: {
20826                         //width: 0,
20827                         //width: this.meterWidth + 'px'                                                
20828                         }
20829                     },
20830                     {                            
20831                          cls: 'roo-password-meter-text'                          
20832                     }
20833                 ]                
20834             }
20835         });
20836         */
20837         if (this.hideTrigger) {
20838             this.trigger.setDisplayed(false);
20839         }
20840         this.setSize(this.width || '', this.height || '');
20841     },
20842     // private
20843     onDestroy: function () {
20844         if (this.trigger) {
20845             this.trigger.removeAllListeners();
20846             this.trigger.remove();
20847         }
20848         if (this.wrap) {
20849             this.wrap.remove();
20850         }
20851         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20852     },
20853     // private
20854     checkStrength: function () {
20855         var pwd = this.inputEl().getValue();
20856         if (pwd == this._lastPwd) {
20857             return;
20858         }
20859
20860         var strength;
20861         if (this.ClientSideStrongPassword(pwd)) {
20862             strength = 3;
20863         } else if (this.ClientSideMediumPassword(pwd)) {
20864             strength = 2;
20865         } else if (this.ClientSideWeakPassword(pwd)) {
20866             strength = 1;
20867         } else {
20868             strength = 0;
20869         }
20870         
20871         console.log('strength1: ' + strength);
20872         
20873         //var pm = this.trigger.child('div/div/div').dom;
20874         var pm = this.trigger.child('div/div');
20875         pm.removeClass(this.meterClass);
20876         pm.addClass(this.meterClass[strength]);
20877                 
20878         
20879         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20880                 
20881         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20882         
20883         this._lastPwd = pwd;
20884     },
20885     reset: function () {
20886         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20887         
20888         this._lastPwd = '';
20889         
20890         var pm = this.trigger.child('div/div');
20891         pm.removeClass(this.meterClass);
20892         pm.addClass('roo-password-meter-grey');        
20893         
20894         
20895         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20896         
20897         pt.innerHTML = '';
20898         this.inputEl().dom.type='password';
20899     },
20900     // private
20901     validateValue: function (value) {
20902         
20903         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20904             return false;
20905         }
20906         if (value.length == 0) {
20907             if (this.allowBlank) {
20908                 this.clearInvalid();
20909                 return true;
20910             }
20911
20912             this.markInvalid(this.errors.PwdEmpty);
20913             this.errorMsg = this.errors.PwdEmpty;
20914             return false;
20915         }
20916         
20917         if(this.insecure){
20918             return true;
20919         }
20920         
20921         if ('[\x21-\x7e]*'.match(value)) {
20922             this.markInvalid(this.errors.PwdBadChar);
20923             this.errorMsg = this.errors.PwdBadChar;
20924             return false;
20925         }
20926         if (value.length < 6) {
20927             this.markInvalid(this.errors.PwdShort);
20928             this.errorMsg = this.errors.PwdShort;
20929             return false;
20930         }
20931         if (value.length > 16) {
20932             this.markInvalid(this.errors.PwdLong);
20933             this.errorMsg = this.errors.PwdLong;
20934             return false;
20935         }
20936         var strength;
20937         if (this.ClientSideStrongPassword(value)) {
20938             strength = 3;
20939         } else if (this.ClientSideMediumPassword(value)) {
20940             strength = 2;
20941         } else if (this.ClientSideWeakPassword(value)) {
20942             strength = 1;
20943         } else {
20944             strength = 0;
20945         }
20946
20947         
20948         if (strength < 2) {
20949             //this.markInvalid(this.errors.TooWeak);
20950             this.errorMsg = this.errors.TooWeak;
20951             //return false;
20952         }
20953         
20954         
20955         console.log('strength2: ' + strength);
20956         
20957         //var pm = this.trigger.child('div/div/div').dom;
20958         
20959         var pm = this.trigger.child('div/div');
20960         pm.removeClass(this.meterClass);
20961         pm.addClass(this.meterClass[strength]);
20962                 
20963         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20964                 
20965         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20966         
20967         this.errorMsg = ''; 
20968         return true;
20969     },
20970     // private
20971     CharacterSetChecks: function (type) {
20972         this.type = type;
20973         this.fResult = false;
20974     },
20975     // private
20976     isctype: function (character, type) {
20977         switch (type) { //why needed break after return in js ? very odd bug
20978             case this.kCapitalLetter:
20979                 if (character >= 'A' && character <= 'Z') {
20980                     return true;
20981                 }
20982                 break;
20983             case this.kSmallLetter:
20984                 if (character >= 'a' && character <= 'z') {
20985                     return true;
20986                 }
20987                 break;
20988             case this.kDigit:
20989                 if (character >= '0' && character <= '9') {
20990                     return true;
20991                 }
20992                 break;
20993             case this.kPunctuation:
20994                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
20995                     return true;
20996                 }
20997                 break;
20998             default:
20999                 return false;
21000         }
21001
21002     },
21003     // private
21004     IsLongEnough: function (pwd, size) {
21005         return !(pwd == null || isNaN(size) || pwd.length < size);
21006     },
21007     // private
21008     SpansEnoughCharacterSets: function (word, nb) {
21009         if (!this.IsLongEnough(word, nb))
21010         {
21011             return false;
21012         }
21013
21014         var characterSetChecks = new Array(
21015                 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21016                 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation));
21017         for (var index = 0; index < word.length; ++index) {
21018             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21019                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21020                     characterSetChecks[nCharSet].fResult = true;
21021                     break;
21022                 }
21023             }
21024         }
21025
21026         var nCharSets = 0;
21027         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21028             if (characterSetChecks[nCharSet].fResult) {
21029                 ++nCharSets;
21030             }
21031         }
21032
21033         if (nCharSets < nb) {
21034             return false;
21035         }
21036         return true;
21037     },
21038     // private
21039     ClientSideStrongPassword: function (pwd) {
21040         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21041     },
21042     // private
21043     ClientSideMediumPassword: function (pwd) {
21044         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21045     },
21046     // private
21047     ClientSideWeakPassword: function (pwd) {
21048         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21049     }
21050           
21051 })//<script type="text/javascript">
21052
21053 /*
21054  * Based  Ext JS Library 1.1.1
21055  * Copyright(c) 2006-2007, Ext JS, LLC.
21056  * LGPL
21057  *
21058  */
21059  
21060 /**
21061  * @class Roo.HtmlEditorCore
21062  * @extends Roo.Component
21063  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21064  *
21065  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21066  */
21067
21068 Roo.HtmlEditorCore = function(config){
21069     
21070     
21071     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21072     
21073     
21074     this.addEvents({
21075         /**
21076          * @event initialize
21077          * Fires when the editor is fully initialized (including the iframe)
21078          * @param {Roo.HtmlEditorCore} this
21079          */
21080         initialize: true,
21081         /**
21082          * @event activate
21083          * Fires when the editor is first receives the focus. Any insertion must wait
21084          * until after this event.
21085          * @param {Roo.HtmlEditorCore} this
21086          */
21087         activate: true,
21088          /**
21089          * @event beforesync
21090          * Fires before the textarea is updated with content from the editor iframe. Return false
21091          * to cancel the sync.
21092          * @param {Roo.HtmlEditorCore} this
21093          * @param {String} html
21094          */
21095         beforesync: true,
21096          /**
21097          * @event beforepush
21098          * Fires before the iframe editor is updated with content from the textarea. Return false
21099          * to cancel the push.
21100          * @param {Roo.HtmlEditorCore} this
21101          * @param {String} html
21102          */
21103         beforepush: true,
21104          /**
21105          * @event sync
21106          * Fires when the textarea is updated with content from the editor iframe.
21107          * @param {Roo.HtmlEditorCore} this
21108          * @param {String} html
21109          */
21110         sync: true,
21111          /**
21112          * @event push
21113          * Fires when the iframe editor is updated with content from the textarea.
21114          * @param {Roo.HtmlEditorCore} this
21115          * @param {String} html
21116          */
21117         push: true,
21118         
21119         /**
21120          * @event editorevent
21121          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21122          * @param {Roo.HtmlEditorCore} this
21123          */
21124         editorevent: true
21125         
21126     });
21127     
21128     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21129     
21130     // defaults : white / black...
21131     this.applyBlacklists();
21132     
21133     
21134     
21135 };
21136
21137
21138 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21139
21140
21141      /**
21142      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21143      */
21144     
21145     owner : false,
21146     
21147      /**
21148      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21149      *                        Roo.resizable.
21150      */
21151     resizable : false,
21152      /**
21153      * @cfg {Number} height (in pixels)
21154      */   
21155     height: 300,
21156    /**
21157      * @cfg {Number} width (in pixels)
21158      */   
21159     width: 500,
21160     
21161     /**
21162      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21163      * 
21164      */
21165     stylesheets: false,
21166     
21167     // id of frame..
21168     frameId: false,
21169     
21170     // private properties
21171     validationEvent : false,
21172     deferHeight: true,
21173     initialized : false,
21174     activated : false,
21175     sourceEditMode : false,
21176     onFocus : Roo.emptyFn,
21177     iframePad:3,
21178     hideMode:'offsets',
21179     
21180     clearUp: true,
21181     
21182     // blacklist + whitelisted elements..
21183     black: false,
21184     white: false,
21185      
21186     
21187
21188     /**
21189      * Protected method that will not generally be called directly. It
21190      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21191      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21192      */
21193     getDocMarkup : function(){
21194         // body styles..
21195         var st = '';
21196         
21197         // inherit styels from page...?? 
21198         if (this.stylesheets === false) {
21199             
21200             Roo.get(document.head).select('style').each(function(node) {
21201                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21202             });
21203             
21204             Roo.get(document.head).select('link').each(function(node) { 
21205                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21206             });
21207             
21208         } else if (!this.stylesheets.length) {
21209                 // simple..
21210                 st = '<style type="text/css">' +
21211                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21212                    '</style>';
21213         } else { 
21214             
21215         }
21216         
21217         st +=  '<style type="text/css">' +
21218             'IMG { cursor: pointer } ' +
21219         '</style>';
21220
21221         
21222         return '<html><head>' + st  +
21223             //<style type="text/css">' +
21224             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21225             //'</style>' +
21226             ' </head><body class="roo-htmleditor-body"></body></html>';
21227     },
21228
21229     // private
21230     onRender : function(ct, position)
21231     {
21232         var _t = this;
21233         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21234         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21235         
21236         
21237         this.el.dom.style.border = '0 none';
21238         this.el.dom.setAttribute('tabIndex', -1);
21239         this.el.addClass('x-hidden hide');
21240         
21241         
21242         
21243         if(Roo.isIE){ // fix IE 1px bogus margin
21244             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21245         }
21246        
21247         
21248         this.frameId = Roo.id();
21249         
21250          
21251         
21252         var iframe = this.owner.wrap.createChild({
21253             tag: 'iframe',
21254             cls: 'form-control', // bootstrap..
21255             id: this.frameId,
21256             name: this.frameId,
21257             frameBorder : 'no',
21258             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21259         }, this.el
21260         );
21261         
21262         
21263         this.iframe = iframe.dom;
21264
21265          this.assignDocWin();
21266         
21267         this.doc.designMode = 'on';
21268        
21269         this.doc.open();
21270         this.doc.write(this.getDocMarkup());
21271         this.doc.close();
21272
21273         
21274         var task = { // must defer to wait for browser to be ready
21275             run : function(){
21276                 //console.log("run task?" + this.doc.readyState);
21277                 this.assignDocWin();
21278                 if(this.doc.body || this.doc.readyState == 'complete'){
21279                     try {
21280                         this.doc.designMode="on";
21281                     } catch (e) {
21282                         return;
21283                     }
21284                     Roo.TaskMgr.stop(task);
21285                     this.initEditor.defer(10, this);
21286                 }
21287             },
21288             interval : 10,
21289             duration: 10000,
21290             scope: this
21291         };
21292         Roo.TaskMgr.start(task);
21293
21294     },
21295
21296     // private
21297     onResize : function(w, h)
21298     {
21299          Roo.log('resize: ' +w + ',' + h );
21300         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21301         if(!this.iframe){
21302             return;
21303         }
21304         if(typeof w == 'number'){
21305             
21306             this.iframe.style.width = w + 'px';
21307         }
21308         if(typeof h == 'number'){
21309             
21310             this.iframe.style.height = h + 'px';
21311             if(this.doc){
21312                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21313             }
21314         }
21315         
21316     },
21317
21318     /**
21319      * Toggles the editor between standard and source edit mode.
21320      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21321      */
21322     toggleSourceEdit : function(sourceEditMode){
21323         
21324         this.sourceEditMode = sourceEditMode === true;
21325         
21326         if(this.sourceEditMode){
21327  
21328             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21329             
21330         }else{
21331             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21332             //this.iframe.className = '';
21333             this.deferFocus();
21334         }
21335         //this.setSize(this.owner.wrap.getSize());
21336         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21337     },
21338
21339     
21340   
21341
21342     /**
21343      * Protected method that will not generally be called directly. If you need/want
21344      * custom HTML cleanup, this is the method you should override.
21345      * @param {String} html The HTML to be cleaned
21346      * return {String} The cleaned HTML
21347      */
21348     cleanHtml : function(html){
21349         html = String(html);
21350         if(html.length > 5){
21351             if(Roo.isSafari){ // strip safari nonsense
21352                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21353             }
21354         }
21355         if(html == '&nbsp;'){
21356             html = '';
21357         }
21358         return html;
21359     },
21360
21361     /**
21362      * HTML Editor -> Textarea
21363      * Protected method that will not generally be called directly. Syncs the contents
21364      * of the editor iframe with the textarea.
21365      */
21366     syncValue : function(){
21367         if(this.initialized){
21368             var bd = (this.doc.body || this.doc.documentElement);
21369             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21370             var html = bd.innerHTML;
21371             if(Roo.isSafari){
21372                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21373                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21374                 if(m && m[1]){
21375                     html = '<div style="'+m[0]+'">' + html + '</div>';
21376                 }
21377             }
21378             html = this.cleanHtml(html);
21379             // fix up the special chars.. normaly like back quotes in word...
21380             // however we do not want to do this with chinese..
21381             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21382                 var cc = b.charCodeAt();
21383                 if (
21384                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21385                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21386                     (cc >= 0xf900 && cc < 0xfb00 )
21387                 ) {
21388                         return b;
21389                 }
21390                 return "&#"+cc+";" 
21391             });
21392             if(this.owner.fireEvent('beforesync', this, html) !== false){
21393                 this.el.dom.value = html;
21394                 this.owner.fireEvent('sync', this, html);
21395             }
21396         }
21397     },
21398
21399     /**
21400      * Protected method that will not generally be called directly. Pushes the value of the textarea
21401      * into the iframe editor.
21402      */
21403     pushValue : function(){
21404         if(this.initialized){
21405             var v = this.el.dom.value.trim();
21406             
21407 //            if(v.length < 1){
21408 //                v = '&#160;';
21409 //            }
21410             
21411             if(this.owner.fireEvent('beforepush', this, v) !== false){
21412                 var d = (this.doc.body || this.doc.documentElement);
21413                 d.innerHTML = v;
21414                 this.cleanUpPaste();
21415                 this.el.dom.value = d.innerHTML;
21416                 this.owner.fireEvent('push', this, v);
21417             }
21418         }
21419     },
21420
21421     // private
21422     deferFocus : function(){
21423         this.focus.defer(10, this);
21424     },
21425
21426     // doc'ed in Field
21427     focus : function(){
21428         if(this.win && !this.sourceEditMode){
21429             this.win.focus();
21430         }else{
21431             this.el.focus();
21432         }
21433     },
21434     
21435     assignDocWin: function()
21436     {
21437         var iframe = this.iframe;
21438         
21439          if(Roo.isIE){
21440             this.doc = iframe.contentWindow.document;
21441             this.win = iframe.contentWindow;
21442         } else {
21443 //            if (!Roo.get(this.frameId)) {
21444 //                return;
21445 //            }
21446 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21447 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21448             
21449             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21450                 return;
21451             }
21452             
21453             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21454             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21455         }
21456     },
21457     
21458     // private
21459     initEditor : function(){
21460         //console.log("INIT EDITOR");
21461         this.assignDocWin();
21462         
21463         
21464         
21465         this.doc.designMode="on";
21466         this.doc.open();
21467         this.doc.write(this.getDocMarkup());
21468         this.doc.close();
21469         
21470         var dbody = (this.doc.body || this.doc.documentElement);
21471         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21472         // this copies styles from the containing element into thsi one..
21473         // not sure why we need all of this..
21474         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21475         
21476         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21477         //ss['background-attachment'] = 'fixed'; // w3c
21478         dbody.bgProperties = 'fixed'; // ie
21479         //Roo.DomHelper.applyStyles(dbody, ss);
21480         Roo.EventManager.on(this.doc, {
21481             //'mousedown': this.onEditorEvent,
21482             'mouseup': this.onEditorEvent,
21483             'dblclick': this.onEditorEvent,
21484             'click': this.onEditorEvent,
21485             'keyup': this.onEditorEvent,
21486             buffer:100,
21487             scope: this
21488         });
21489         if(Roo.isGecko){
21490             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21491         }
21492         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21493             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21494         }
21495         this.initialized = true;
21496
21497         this.owner.fireEvent('initialize', this);
21498         this.pushValue();
21499     },
21500
21501     // private
21502     onDestroy : function(){
21503         
21504         
21505         
21506         if(this.rendered){
21507             
21508             //for (var i =0; i < this.toolbars.length;i++) {
21509             //    // fixme - ask toolbars for heights?
21510             //    this.toolbars[i].onDestroy();
21511            // }
21512             
21513             //this.wrap.dom.innerHTML = '';
21514             //this.wrap.remove();
21515         }
21516     },
21517
21518     // private
21519     onFirstFocus : function(){
21520         
21521         this.assignDocWin();
21522         
21523         
21524         this.activated = true;
21525          
21526     
21527         if(Roo.isGecko){ // prevent silly gecko errors
21528             this.win.focus();
21529             var s = this.win.getSelection();
21530             if(!s.focusNode || s.focusNode.nodeType != 3){
21531                 var r = s.getRangeAt(0);
21532                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21533                 r.collapse(true);
21534                 this.deferFocus();
21535             }
21536             try{
21537                 this.execCmd('useCSS', true);
21538                 this.execCmd('styleWithCSS', false);
21539             }catch(e){}
21540         }
21541         this.owner.fireEvent('activate', this);
21542     },
21543
21544     // private
21545     adjustFont: function(btn){
21546         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21547         //if(Roo.isSafari){ // safari
21548         //    adjust *= 2;
21549        // }
21550         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21551         if(Roo.isSafari){ // safari
21552             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21553             v =  (v < 10) ? 10 : v;
21554             v =  (v > 48) ? 48 : v;
21555             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21556             
21557         }
21558         
21559         
21560         v = Math.max(1, v+adjust);
21561         
21562         this.execCmd('FontSize', v  );
21563     },
21564
21565     onEditorEvent : function(e)
21566     {
21567         this.owner.fireEvent('editorevent', this, e);
21568       //  this.updateToolbar();
21569         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21570     },
21571
21572     insertTag : function(tg)
21573     {
21574         // could be a bit smarter... -> wrap the current selected tRoo..
21575         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21576             
21577             range = this.createRange(this.getSelection());
21578             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21579             wrappingNode.appendChild(range.extractContents());
21580             range.insertNode(wrappingNode);
21581
21582             return;
21583             
21584             
21585             
21586         }
21587         this.execCmd("formatblock",   tg);
21588         
21589     },
21590     
21591     insertText : function(txt)
21592     {
21593         
21594         
21595         var range = this.createRange();
21596         range.deleteContents();
21597                //alert(Sender.getAttribute('label'));
21598                
21599         range.insertNode(this.doc.createTextNode(txt));
21600     } ,
21601     
21602      
21603
21604     /**
21605      * Executes a Midas editor command on the editor document and performs necessary focus and
21606      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21607      * @param {String} cmd The Midas command
21608      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21609      */
21610     relayCmd : function(cmd, value){
21611         this.win.focus();
21612         this.execCmd(cmd, value);
21613         this.owner.fireEvent('editorevent', this);
21614         //this.updateToolbar();
21615         this.owner.deferFocus();
21616     },
21617
21618     /**
21619      * Executes a Midas editor command directly on the editor document.
21620      * For visual commands, you should use {@link #relayCmd} instead.
21621      * <b>This should only be called after the editor is initialized.</b>
21622      * @param {String} cmd The Midas command
21623      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21624      */
21625     execCmd : function(cmd, value){
21626         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21627         this.syncValue();
21628     },
21629  
21630  
21631    
21632     /**
21633      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21634      * to insert tRoo.
21635      * @param {String} text | dom node.. 
21636      */
21637     insertAtCursor : function(text)
21638     {
21639         
21640         
21641         
21642         if(!this.activated){
21643             return;
21644         }
21645         /*
21646         if(Roo.isIE){
21647             this.win.focus();
21648             var r = this.doc.selection.createRange();
21649             if(r){
21650                 r.collapse(true);
21651                 r.pasteHTML(text);
21652                 this.syncValue();
21653                 this.deferFocus();
21654             
21655             }
21656             return;
21657         }
21658         */
21659         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21660             this.win.focus();
21661             
21662             
21663             // from jquery ui (MIT licenced)
21664             var range, node;
21665             var win = this.win;
21666             
21667             if (win.getSelection && win.getSelection().getRangeAt) {
21668                 range = win.getSelection().getRangeAt(0);
21669                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21670                 range.insertNode(node);
21671             } else if (win.document.selection && win.document.selection.createRange) {
21672                 // no firefox support
21673                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21674                 win.document.selection.createRange().pasteHTML(txt);
21675             } else {
21676                 // no firefox support
21677                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21678                 this.execCmd('InsertHTML', txt);
21679             } 
21680             
21681             this.syncValue();
21682             
21683             this.deferFocus();
21684         }
21685     },
21686  // private
21687     mozKeyPress : function(e){
21688         if(e.ctrlKey){
21689             var c = e.getCharCode(), cmd;
21690           
21691             if(c > 0){
21692                 c = String.fromCharCode(c).toLowerCase();
21693                 switch(c){
21694                     case 'b':
21695                         cmd = 'bold';
21696                         break;
21697                     case 'i':
21698                         cmd = 'italic';
21699                         break;
21700                     
21701                     case 'u':
21702                         cmd = 'underline';
21703                         break;
21704                     
21705                     case 'v':
21706                         this.cleanUpPaste.defer(100, this);
21707                         return;
21708                         
21709                 }
21710                 if(cmd){
21711                     this.win.focus();
21712                     this.execCmd(cmd);
21713                     this.deferFocus();
21714                     e.preventDefault();
21715                 }
21716                 
21717             }
21718         }
21719     },
21720
21721     // private
21722     fixKeys : function(){ // load time branching for fastest keydown performance
21723         if(Roo.isIE){
21724             return function(e){
21725                 var k = e.getKey(), r;
21726                 if(k == e.TAB){
21727                     e.stopEvent();
21728                     r = this.doc.selection.createRange();
21729                     if(r){
21730                         r.collapse(true);
21731                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21732                         this.deferFocus();
21733                     }
21734                     return;
21735                 }
21736                 
21737                 if(k == e.ENTER){
21738                     r = this.doc.selection.createRange();
21739                     if(r){
21740                         var target = r.parentElement();
21741                         if(!target || target.tagName.toLowerCase() != 'li'){
21742                             e.stopEvent();
21743                             r.pasteHTML('<br />');
21744                             r.collapse(false);
21745                             r.select();
21746                         }
21747                     }
21748                 }
21749                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21750                     this.cleanUpPaste.defer(100, this);
21751                     return;
21752                 }
21753                 
21754                 
21755             };
21756         }else if(Roo.isOpera){
21757             return function(e){
21758                 var k = e.getKey();
21759                 if(k == e.TAB){
21760                     e.stopEvent();
21761                     this.win.focus();
21762                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21763                     this.deferFocus();
21764                 }
21765                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21766                     this.cleanUpPaste.defer(100, this);
21767                     return;
21768                 }
21769                 
21770             };
21771         }else if(Roo.isSafari){
21772             return function(e){
21773                 var k = e.getKey();
21774                 
21775                 if(k == e.TAB){
21776                     e.stopEvent();
21777                     this.execCmd('InsertText','\t');
21778                     this.deferFocus();
21779                     return;
21780                 }
21781                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21782                     this.cleanUpPaste.defer(100, this);
21783                     return;
21784                 }
21785                 
21786              };
21787         }
21788     }(),
21789     
21790     getAllAncestors: function()
21791     {
21792         var p = this.getSelectedNode();
21793         var a = [];
21794         if (!p) {
21795             a.push(p); // push blank onto stack..
21796             p = this.getParentElement();
21797         }
21798         
21799         
21800         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21801             a.push(p);
21802             p = p.parentNode;
21803         }
21804         a.push(this.doc.body);
21805         return a;
21806     },
21807     lastSel : false,
21808     lastSelNode : false,
21809     
21810     
21811     getSelection : function() 
21812     {
21813         this.assignDocWin();
21814         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21815     },
21816     
21817     getSelectedNode: function() 
21818     {
21819         // this may only work on Gecko!!!
21820         
21821         // should we cache this!!!!
21822         
21823         
21824         
21825          
21826         var range = this.createRange(this.getSelection()).cloneRange();
21827         
21828         if (Roo.isIE) {
21829             var parent = range.parentElement();
21830             while (true) {
21831                 var testRange = range.duplicate();
21832                 testRange.moveToElementText(parent);
21833                 if (testRange.inRange(range)) {
21834                     break;
21835                 }
21836                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21837                     break;
21838                 }
21839                 parent = parent.parentElement;
21840             }
21841             return parent;
21842         }
21843         
21844         // is ancestor a text element.
21845         var ac =  range.commonAncestorContainer;
21846         if (ac.nodeType == 3) {
21847             ac = ac.parentNode;
21848         }
21849         
21850         var ar = ac.childNodes;
21851          
21852         var nodes = [];
21853         var other_nodes = [];
21854         var has_other_nodes = false;
21855         for (var i=0;i<ar.length;i++) {
21856             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21857                 continue;
21858             }
21859             // fullly contained node.
21860             
21861             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21862                 nodes.push(ar[i]);
21863                 continue;
21864             }
21865             
21866             // probably selected..
21867             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21868                 other_nodes.push(ar[i]);
21869                 continue;
21870             }
21871             // outer..
21872             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21873                 continue;
21874             }
21875             
21876             
21877             has_other_nodes = true;
21878         }
21879         if (!nodes.length && other_nodes.length) {
21880             nodes= other_nodes;
21881         }
21882         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21883             return false;
21884         }
21885         
21886         return nodes[0];
21887     },
21888     createRange: function(sel)
21889     {
21890         // this has strange effects when using with 
21891         // top toolbar - not sure if it's a great idea.
21892         //this.editor.contentWindow.focus();
21893         if (typeof sel != "undefined") {
21894             try {
21895                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21896             } catch(e) {
21897                 return this.doc.createRange();
21898             }
21899         } else {
21900             return this.doc.createRange();
21901         }
21902     },
21903     getParentElement: function()
21904     {
21905         
21906         this.assignDocWin();
21907         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21908         
21909         var range = this.createRange(sel);
21910          
21911         try {
21912             var p = range.commonAncestorContainer;
21913             while (p.nodeType == 3) { // text node
21914                 p = p.parentNode;
21915             }
21916             return p;
21917         } catch (e) {
21918             return null;
21919         }
21920     
21921     },
21922     /***
21923      *
21924      * Range intersection.. the hard stuff...
21925      *  '-1' = before
21926      *  '0' = hits..
21927      *  '1' = after.
21928      *         [ -- selected range --- ]
21929      *   [fail]                        [fail]
21930      *
21931      *    basically..
21932      *      if end is before start or  hits it. fail.
21933      *      if start is after end or hits it fail.
21934      *
21935      *   if either hits (but other is outside. - then it's not 
21936      *   
21937      *    
21938      **/
21939     
21940     
21941     // @see http://www.thismuchiknow.co.uk/?p=64.
21942     rangeIntersectsNode : function(range, node)
21943     {
21944         var nodeRange = node.ownerDocument.createRange();
21945         try {
21946             nodeRange.selectNode(node);
21947         } catch (e) {
21948             nodeRange.selectNodeContents(node);
21949         }
21950     
21951         var rangeStartRange = range.cloneRange();
21952         rangeStartRange.collapse(true);
21953     
21954         var rangeEndRange = range.cloneRange();
21955         rangeEndRange.collapse(false);
21956     
21957         var nodeStartRange = nodeRange.cloneRange();
21958         nodeStartRange.collapse(true);
21959     
21960         var nodeEndRange = nodeRange.cloneRange();
21961         nodeEndRange.collapse(false);
21962     
21963         return rangeStartRange.compareBoundaryPoints(
21964                  Range.START_TO_START, nodeEndRange) == -1 &&
21965                rangeEndRange.compareBoundaryPoints(
21966                  Range.START_TO_START, nodeStartRange) == 1;
21967         
21968          
21969     },
21970     rangeCompareNode : function(range, node)
21971     {
21972         var nodeRange = node.ownerDocument.createRange();
21973         try {
21974             nodeRange.selectNode(node);
21975         } catch (e) {
21976             nodeRange.selectNodeContents(node);
21977         }
21978         
21979         
21980         range.collapse(true);
21981     
21982         nodeRange.collapse(true);
21983      
21984         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21985         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21986          
21987         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21988         
21989         var nodeIsBefore   =  ss == 1;
21990         var nodeIsAfter    = ee == -1;
21991         
21992         if (nodeIsBefore && nodeIsAfter) {
21993             return 0; // outer
21994         }
21995         if (!nodeIsBefore && nodeIsAfter) {
21996             return 1; //right trailed.
21997         }
21998         
21999         if (nodeIsBefore && !nodeIsAfter) {
22000             return 2;  // left trailed.
22001         }
22002         // fully contined.
22003         return 3;
22004     },
22005
22006     // private? - in a new class?
22007     cleanUpPaste :  function()
22008     {
22009         // cleans up the whole document..
22010         Roo.log('cleanuppaste');
22011         
22012         this.cleanUpChildren(this.doc.body);
22013         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22014         if (clean != this.doc.body.innerHTML) {
22015             this.doc.body.innerHTML = clean;
22016         }
22017         
22018     },
22019     
22020     cleanWordChars : function(input) {// change the chars to hex code
22021         var he = Roo.HtmlEditorCore;
22022         
22023         var output = input;
22024         Roo.each(he.swapCodes, function(sw) { 
22025             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22026             
22027             output = output.replace(swapper, sw[1]);
22028         });
22029         
22030         return output;
22031     },
22032     
22033     
22034     cleanUpChildren : function (n)
22035     {
22036         if (!n.childNodes.length) {
22037             return;
22038         }
22039         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22040            this.cleanUpChild(n.childNodes[i]);
22041         }
22042     },
22043     
22044     
22045         
22046     
22047     cleanUpChild : function (node)
22048     {
22049         var ed = this;
22050         //console.log(node);
22051         if (node.nodeName == "#text") {
22052             // clean up silly Windows -- stuff?
22053             return; 
22054         }
22055         if (node.nodeName == "#comment") {
22056             node.parentNode.removeChild(node);
22057             // clean up silly Windows -- stuff?
22058             return; 
22059         }
22060         var lcname = node.tagName.toLowerCase();
22061         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22062         // whitelist of tags..
22063         
22064         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22065             // remove node.
22066             node.parentNode.removeChild(node);
22067             return;
22068             
22069         }
22070         
22071         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22072         
22073         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22074         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22075         
22076         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22077         //    remove_keep_children = true;
22078         //}
22079         
22080         if (remove_keep_children) {
22081             this.cleanUpChildren(node);
22082             // inserts everything just before this node...
22083             while (node.childNodes.length) {
22084                 var cn = node.childNodes[0];
22085                 node.removeChild(cn);
22086                 node.parentNode.insertBefore(cn, node);
22087             }
22088             node.parentNode.removeChild(node);
22089             return;
22090         }
22091         
22092         if (!node.attributes || !node.attributes.length) {
22093             this.cleanUpChildren(node);
22094             return;
22095         }
22096         
22097         function cleanAttr(n,v)
22098         {
22099             
22100             if (v.match(/^\./) || v.match(/^\//)) {
22101                 return;
22102             }
22103             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22104                 return;
22105             }
22106             if (v.match(/^#/)) {
22107                 return;
22108             }
22109 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22110             node.removeAttribute(n);
22111             
22112         }
22113         
22114         var cwhite = this.cwhite;
22115         var cblack = this.cblack;
22116             
22117         function cleanStyle(n,v)
22118         {
22119             if (v.match(/expression/)) { //XSS?? should we even bother..
22120                 node.removeAttribute(n);
22121                 return;
22122             }
22123             
22124             var parts = v.split(/;/);
22125             var clean = [];
22126             
22127             Roo.each(parts, function(p) {
22128                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22129                 if (!p.length) {
22130                     return true;
22131                 }
22132                 var l = p.split(':').shift().replace(/\s+/g,'');
22133                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22134                 
22135                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22136 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22137                     //node.removeAttribute(n);
22138                     return true;
22139                 }
22140                 //Roo.log()
22141                 // only allow 'c whitelisted system attributes'
22142                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22143 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22144                     //node.removeAttribute(n);
22145                     return true;
22146                 }
22147                 
22148                 
22149                  
22150                 
22151                 clean.push(p);
22152                 return true;
22153             });
22154             if (clean.length) { 
22155                 node.setAttribute(n, clean.join(';'));
22156             } else {
22157                 node.removeAttribute(n);
22158             }
22159             
22160         }
22161         
22162         
22163         for (var i = node.attributes.length-1; i > -1 ; i--) {
22164             var a = node.attributes[i];
22165             //console.log(a);
22166             
22167             if (a.name.toLowerCase().substr(0,2)=='on')  {
22168                 node.removeAttribute(a.name);
22169                 continue;
22170             }
22171             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22172                 node.removeAttribute(a.name);
22173                 continue;
22174             }
22175             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22176                 cleanAttr(a.name,a.value); // fixme..
22177                 continue;
22178             }
22179             if (a.name == 'style') {
22180                 cleanStyle(a.name,a.value);
22181                 continue;
22182             }
22183             /// clean up MS crap..
22184             // tecnically this should be a list of valid class'es..
22185             
22186             
22187             if (a.name == 'class') {
22188                 if (a.value.match(/^Mso/)) {
22189                     node.className = '';
22190                 }
22191                 
22192                 if (a.value.match(/body/)) {
22193                     node.className = '';
22194                 }
22195                 continue;
22196             }
22197             
22198             // style cleanup!?
22199             // class cleanup?
22200             
22201         }
22202         
22203         
22204         this.cleanUpChildren(node);
22205         
22206         
22207     },
22208     
22209     /**
22210      * Clean up MS wordisms...
22211      */
22212     cleanWord : function(node)
22213     {
22214         
22215         
22216         if (!node) {
22217             this.cleanWord(this.doc.body);
22218             return;
22219         }
22220         if (node.nodeName == "#text") {
22221             // clean up silly Windows -- stuff?
22222             return; 
22223         }
22224         if (node.nodeName == "#comment") {
22225             node.parentNode.removeChild(node);
22226             // clean up silly Windows -- stuff?
22227             return; 
22228         }
22229         
22230         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22231             node.parentNode.removeChild(node);
22232             return;
22233         }
22234         
22235         // remove - but keep children..
22236         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22237             while (node.childNodes.length) {
22238                 var cn = node.childNodes[0];
22239                 node.removeChild(cn);
22240                 node.parentNode.insertBefore(cn, node);
22241             }
22242             node.parentNode.removeChild(node);
22243             this.iterateChildren(node, this.cleanWord);
22244             return;
22245         }
22246         // clean styles
22247         if (node.className.length) {
22248             
22249             var cn = node.className.split(/\W+/);
22250             var cna = [];
22251             Roo.each(cn, function(cls) {
22252                 if (cls.match(/Mso[a-zA-Z]+/)) {
22253                     return;
22254                 }
22255                 cna.push(cls);
22256             });
22257             node.className = cna.length ? cna.join(' ') : '';
22258             if (!cna.length) {
22259                 node.removeAttribute("class");
22260             }
22261         }
22262         
22263         if (node.hasAttribute("lang")) {
22264             node.removeAttribute("lang");
22265         }
22266         
22267         if (node.hasAttribute("style")) {
22268             
22269             var styles = node.getAttribute("style").split(";");
22270             var nstyle = [];
22271             Roo.each(styles, function(s) {
22272                 if (!s.match(/:/)) {
22273                     return;
22274                 }
22275                 var kv = s.split(":");
22276                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22277                     return;
22278                 }
22279                 // what ever is left... we allow.
22280                 nstyle.push(s);
22281             });
22282             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22283             if (!nstyle.length) {
22284                 node.removeAttribute('style');
22285             }
22286         }
22287         this.iterateChildren(node, this.cleanWord);
22288         
22289         
22290         
22291     },
22292     /**
22293      * iterateChildren of a Node, calling fn each time, using this as the scole..
22294      * @param {DomNode} node node to iterate children of.
22295      * @param {Function} fn method of this class to call on each item.
22296      */
22297     iterateChildren : function(node, fn)
22298     {
22299         if (!node.childNodes.length) {
22300                 return;
22301         }
22302         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22303            fn.call(this, node.childNodes[i])
22304         }
22305     },
22306     
22307     
22308     /**
22309      * cleanTableWidths.
22310      *
22311      * Quite often pasting from word etc.. results in tables with column and widths.
22312      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22313      *
22314      */
22315     cleanTableWidths : function(node)
22316     {
22317          
22318          
22319         if (!node) {
22320             this.cleanTableWidths(this.doc.body);
22321             return;
22322         }
22323         
22324         // ignore list...
22325         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22326             return; 
22327         }
22328         Roo.log(node.tagName);
22329         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22330             this.iterateChildren(node, this.cleanTableWidths);
22331             return;
22332         }
22333         if (node.hasAttribute('width')) {
22334             node.removeAttribute('width');
22335         }
22336         
22337          
22338         if (node.hasAttribute("style")) {
22339             // pretty basic...
22340             
22341             var styles = node.getAttribute("style").split(";");
22342             var nstyle = [];
22343             Roo.each(styles, function(s) {
22344                 if (!s.match(/:/)) {
22345                     return;
22346                 }
22347                 var kv = s.split(":");
22348                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22349                     return;
22350                 }
22351                 // what ever is left... we allow.
22352                 nstyle.push(s);
22353             });
22354             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22355             if (!nstyle.length) {
22356                 node.removeAttribute('style');
22357             }
22358         }
22359         
22360         this.iterateChildren(node, this.cleanTableWidths);
22361         
22362         
22363     },
22364     
22365     
22366     
22367     
22368     domToHTML : function(currentElement, depth, nopadtext) {
22369         
22370         depth = depth || 0;
22371         nopadtext = nopadtext || false;
22372     
22373         if (!currentElement) {
22374             return this.domToHTML(this.doc.body);
22375         }
22376         
22377         //Roo.log(currentElement);
22378         var j;
22379         var allText = false;
22380         var nodeName = currentElement.nodeName;
22381         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22382         
22383         if  (nodeName == '#text') {
22384             
22385             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22386         }
22387         
22388         
22389         var ret = '';
22390         if (nodeName != 'BODY') {
22391              
22392             var i = 0;
22393             // Prints the node tagName, such as <A>, <IMG>, etc
22394             if (tagName) {
22395                 var attr = [];
22396                 for(i = 0; i < currentElement.attributes.length;i++) {
22397                     // quoting?
22398                     var aname = currentElement.attributes.item(i).name;
22399                     if (!currentElement.attributes.item(i).value.length) {
22400                         continue;
22401                     }
22402                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22403                 }
22404                 
22405                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22406             } 
22407             else {
22408                 
22409                 // eack
22410             }
22411         } else {
22412             tagName = false;
22413         }
22414         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22415             return ret;
22416         }
22417         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22418             nopadtext = true;
22419         }
22420         
22421         
22422         // Traverse the tree
22423         i = 0;
22424         var currentElementChild = currentElement.childNodes.item(i);
22425         var allText = true;
22426         var innerHTML  = '';
22427         lastnode = '';
22428         while (currentElementChild) {
22429             // Formatting code (indent the tree so it looks nice on the screen)
22430             var nopad = nopadtext;
22431             if (lastnode == 'SPAN') {
22432                 nopad  = true;
22433             }
22434             // text
22435             if  (currentElementChild.nodeName == '#text') {
22436                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22437                 toadd = nopadtext ? toadd : toadd.trim();
22438                 if (!nopad && toadd.length > 80) {
22439                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22440                 }
22441                 innerHTML  += toadd;
22442                 
22443                 i++;
22444                 currentElementChild = currentElement.childNodes.item(i);
22445                 lastNode = '';
22446                 continue;
22447             }
22448             allText = false;
22449             
22450             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22451                 
22452             // Recursively traverse the tree structure of the child node
22453             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22454             lastnode = currentElementChild.nodeName;
22455             i++;
22456             currentElementChild=currentElement.childNodes.item(i);
22457         }
22458         
22459         ret += innerHTML;
22460         
22461         if (!allText) {
22462                 // The remaining code is mostly for formatting the tree
22463             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22464         }
22465         
22466         
22467         if (tagName) {
22468             ret+= "</"+tagName+">";
22469         }
22470         return ret;
22471         
22472     },
22473         
22474     applyBlacklists : function()
22475     {
22476         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22477         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22478         
22479         this.white = [];
22480         this.black = [];
22481         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22482             if (b.indexOf(tag) > -1) {
22483                 return;
22484             }
22485             this.white.push(tag);
22486             
22487         }, this);
22488         
22489         Roo.each(w, function(tag) {
22490             if (b.indexOf(tag) > -1) {
22491                 return;
22492             }
22493             if (this.white.indexOf(tag) > -1) {
22494                 return;
22495             }
22496             this.white.push(tag);
22497             
22498         }, this);
22499         
22500         
22501         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22502             if (w.indexOf(tag) > -1) {
22503                 return;
22504             }
22505             this.black.push(tag);
22506             
22507         }, this);
22508         
22509         Roo.each(b, function(tag) {
22510             if (w.indexOf(tag) > -1) {
22511                 return;
22512             }
22513             if (this.black.indexOf(tag) > -1) {
22514                 return;
22515             }
22516             this.black.push(tag);
22517             
22518         }, this);
22519         
22520         
22521         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22522         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22523         
22524         this.cwhite = [];
22525         this.cblack = [];
22526         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22527             if (b.indexOf(tag) > -1) {
22528                 return;
22529             }
22530             this.cwhite.push(tag);
22531             
22532         }, this);
22533         
22534         Roo.each(w, function(tag) {
22535             if (b.indexOf(tag) > -1) {
22536                 return;
22537             }
22538             if (this.cwhite.indexOf(tag) > -1) {
22539                 return;
22540             }
22541             this.cwhite.push(tag);
22542             
22543         }, this);
22544         
22545         
22546         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22547             if (w.indexOf(tag) > -1) {
22548                 return;
22549             }
22550             this.cblack.push(tag);
22551             
22552         }, this);
22553         
22554         Roo.each(b, function(tag) {
22555             if (w.indexOf(tag) > -1) {
22556                 return;
22557             }
22558             if (this.cblack.indexOf(tag) > -1) {
22559                 return;
22560             }
22561             this.cblack.push(tag);
22562             
22563         }, this);
22564     },
22565     
22566     setStylesheets : function(stylesheets)
22567     {
22568         if(typeof(stylesheets) == 'string'){
22569             Roo.get(this.iframe.contentDocument.head).createChild({
22570                 tag : 'link',
22571                 rel : 'stylesheet',
22572                 type : 'text/css',
22573                 href : stylesheets
22574             });
22575             
22576             return;
22577         }
22578         var _this = this;
22579      
22580         Roo.each(stylesheets, function(s) {
22581             if(!s.length){
22582                 return;
22583             }
22584             
22585             Roo.get(_this.iframe.contentDocument.head).createChild({
22586                 tag : 'link',
22587                 rel : 'stylesheet',
22588                 type : 'text/css',
22589                 href : s
22590             });
22591         });
22592
22593         
22594     },
22595     
22596     removeStylesheets : function()
22597     {
22598         var _this = this;
22599         
22600         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22601             s.remove();
22602         });
22603     }
22604     
22605     // hide stuff that is not compatible
22606     /**
22607      * @event blur
22608      * @hide
22609      */
22610     /**
22611      * @event change
22612      * @hide
22613      */
22614     /**
22615      * @event focus
22616      * @hide
22617      */
22618     /**
22619      * @event specialkey
22620      * @hide
22621      */
22622     /**
22623      * @cfg {String} fieldClass @hide
22624      */
22625     /**
22626      * @cfg {String} focusClass @hide
22627      */
22628     /**
22629      * @cfg {String} autoCreate @hide
22630      */
22631     /**
22632      * @cfg {String} inputType @hide
22633      */
22634     /**
22635      * @cfg {String} invalidClass @hide
22636      */
22637     /**
22638      * @cfg {String} invalidText @hide
22639      */
22640     /**
22641      * @cfg {String} msgFx @hide
22642      */
22643     /**
22644      * @cfg {String} validateOnBlur @hide
22645      */
22646 });
22647
22648 Roo.HtmlEditorCore.white = [
22649         'area', 'br', 'img', 'input', 'hr', 'wbr',
22650         
22651        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22652        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22653        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22654        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22655        'table',   'ul',         'xmp', 
22656        
22657        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22658       'thead',   'tr', 
22659      
22660       'dir', 'menu', 'ol', 'ul', 'dl',
22661        
22662       'embed',  'object'
22663 ];
22664
22665
22666 Roo.HtmlEditorCore.black = [
22667     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22668         'applet', // 
22669         'base',   'basefont', 'bgsound', 'blink',  'body', 
22670         'frame',  'frameset', 'head',    'html',   'ilayer', 
22671         'iframe', 'layer',  'link',     'meta',    'object',   
22672         'script', 'style' ,'title',  'xml' // clean later..
22673 ];
22674 Roo.HtmlEditorCore.clean = [
22675     'script', 'style', 'title', 'xml'
22676 ];
22677 Roo.HtmlEditorCore.remove = [
22678     'font'
22679 ];
22680 // attributes..
22681
22682 Roo.HtmlEditorCore.ablack = [
22683     'on'
22684 ];
22685     
22686 Roo.HtmlEditorCore.aclean = [ 
22687     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22688 ];
22689
22690 // protocols..
22691 Roo.HtmlEditorCore.pwhite= [
22692         'http',  'https',  'mailto'
22693 ];
22694
22695 // white listed style attributes.
22696 Roo.HtmlEditorCore.cwhite= [
22697       //  'text-align', /// default is to allow most things..
22698       
22699          
22700 //        'font-size'//??
22701 ];
22702
22703 // black listed style attributes.
22704 Roo.HtmlEditorCore.cblack= [
22705       //  'font-size' -- this can be set by the project 
22706 ];
22707
22708
22709 Roo.HtmlEditorCore.swapCodes   =[ 
22710     [    8211, "--" ], 
22711     [    8212, "--" ], 
22712     [    8216,  "'" ],  
22713     [    8217, "'" ],  
22714     [    8220, '"' ],  
22715     [    8221, '"' ],  
22716     [    8226, "*" ],  
22717     [    8230, "..." ]
22718 ]; 
22719
22720     /*
22721  * - LGPL
22722  *
22723  * HtmlEditor
22724  * 
22725  */
22726
22727 /**
22728  * @class Roo.bootstrap.HtmlEditor
22729  * @extends Roo.bootstrap.TextArea
22730  * Bootstrap HtmlEditor class
22731
22732  * @constructor
22733  * Create a new HtmlEditor
22734  * @param {Object} config The config object
22735  */
22736
22737 Roo.bootstrap.HtmlEditor = function(config){
22738     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22739     if (!this.toolbars) {
22740         this.toolbars = [];
22741     }
22742     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22743     this.addEvents({
22744             /**
22745              * @event initialize
22746              * Fires when the editor is fully initialized (including the iframe)
22747              * @param {HtmlEditor} this
22748              */
22749             initialize: true,
22750             /**
22751              * @event activate
22752              * Fires when the editor is first receives the focus. Any insertion must wait
22753              * until after this event.
22754              * @param {HtmlEditor} this
22755              */
22756             activate: true,
22757              /**
22758              * @event beforesync
22759              * Fires before the textarea is updated with content from the editor iframe. Return false
22760              * to cancel the sync.
22761              * @param {HtmlEditor} this
22762              * @param {String} html
22763              */
22764             beforesync: true,
22765              /**
22766              * @event beforepush
22767              * Fires before the iframe editor is updated with content from the textarea. Return false
22768              * to cancel the push.
22769              * @param {HtmlEditor} this
22770              * @param {String} html
22771              */
22772             beforepush: true,
22773              /**
22774              * @event sync
22775              * Fires when the textarea is updated with content from the editor iframe.
22776              * @param {HtmlEditor} this
22777              * @param {String} html
22778              */
22779             sync: true,
22780              /**
22781              * @event push
22782              * Fires when the iframe editor is updated with content from the textarea.
22783              * @param {HtmlEditor} this
22784              * @param {String} html
22785              */
22786             push: true,
22787              /**
22788              * @event editmodechange
22789              * Fires when the editor switches edit modes
22790              * @param {HtmlEditor} this
22791              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22792              */
22793             editmodechange: true,
22794             /**
22795              * @event editorevent
22796              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22797              * @param {HtmlEditor} this
22798              */
22799             editorevent: true,
22800             /**
22801              * @event firstfocus
22802              * Fires when on first focus - needed by toolbars..
22803              * @param {HtmlEditor} this
22804              */
22805             firstfocus: true,
22806             /**
22807              * @event autosave
22808              * Auto save the htmlEditor value as a file into Events
22809              * @param {HtmlEditor} this
22810              */
22811             autosave: true,
22812             /**
22813              * @event savedpreview
22814              * preview the saved version of htmlEditor
22815              * @param {HtmlEditor} this
22816              */
22817             savedpreview: true
22818         });
22819 };
22820
22821
22822 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22823     
22824     
22825       /**
22826      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22827      */
22828     toolbars : false,
22829    
22830      /**
22831      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22832      *                        Roo.resizable.
22833      */
22834     resizable : false,
22835      /**
22836      * @cfg {Number} height (in pixels)
22837      */   
22838     height: 300,
22839    /**
22840      * @cfg {Number} width (in pixels)
22841      */   
22842     width: false,
22843     
22844     /**
22845      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22846      * 
22847      */
22848     stylesheets: false,
22849     
22850     // id of frame..
22851     frameId: false,
22852     
22853     // private properties
22854     validationEvent : false,
22855     deferHeight: true,
22856     initialized : false,
22857     activated : false,
22858     
22859     onFocus : Roo.emptyFn,
22860     iframePad:3,
22861     hideMode:'offsets',
22862     
22863     
22864     tbContainer : false,
22865     
22866     toolbarContainer :function() {
22867         return this.wrap.select('.x-html-editor-tb',true).first();
22868     },
22869
22870     /**
22871      * Protected method that will not generally be called directly. It
22872      * is called when the editor creates its toolbar. Override this method if you need to
22873      * add custom toolbar buttons.
22874      * @param {HtmlEditor} editor
22875      */
22876     createToolbar : function(){
22877         
22878         Roo.log("create toolbars");
22879         
22880         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22881         this.toolbars[0].render(this.toolbarContainer());
22882         
22883         return;
22884         
22885 //        if (!editor.toolbars || !editor.toolbars.length) {
22886 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22887 //        }
22888 //        
22889 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22890 //            editor.toolbars[i] = Roo.factory(
22891 //                    typeof(editor.toolbars[i]) == 'string' ?
22892 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22893 //                Roo.bootstrap.HtmlEditor);
22894 //            editor.toolbars[i].init(editor);
22895 //        }
22896     },
22897
22898      
22899     // private
22900     onRender : function(ct, position)
22901     {
22902        // Roo.log("Call onRender: " + this.xtype);
22903         var _t = this;
22904         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22905       
22906         this.wrap = this.inputEl().wrap({
22907             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22908         });
22909         
22910         this.editorcore.onRender(ct, position);
22911          
22912         if (this.resizable) {
22913             this.resizeEl = new Roo.Resizable(this.wrap, {
22914                 pinned : true,
22915                 wrap: true,
22916                 dynamic : true,
22917                 minHeight : this.height,
22918                 height: this.height,
22919                 handles : this.resizable,
22920                 width: this.width,
22921                 listeners : {
22922                     resize : function(r, w, h) {
22923                         _t.onResize(w,h); // -something
22924                     }
22925                 }
22926             });
22927             
22928         }
22929         this.createToolbar(this);
22930        
22931         
22932         if(!this.width && this.resizable){
22933             this.setSize(this.wrap.getSize());
22934         }
22935         if (this.resizeEl) {
22936             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22937             // should trigger onReize..
22938         }
22939         
22940     },
22941
22942     // private
22943     onResize : function(w, h)
22944     {
22945         Roo.log('resize: ' +w + ',' + h );
22946         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22947         var ew = false;
22948         var eh = false;
22949         
22950         if(this.inputEl() ){
22951             if(typeof w == 'number'){
22952                 var aw = w - this.wrap.getFrameWidth('lr');
22953                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22954                 ew = aw;
22955             }
22956             if(typeof h == 'number'){
22957                  var tbh = -11;  // fixme it needs to tool bar size!
22958                 for (var i =0; i < this.toolbars.length;i++) {
22959                     // fixme - ask toolbars for heights?
22960                     tbh += this.toolbars[i].el.getHeight();
22961                     //if (this.toolbars[i].footer) {
22962                     //    tbh += this.toolbars[i].footer.el.getHeight();
22963                     //}
22964                 }
22965               
22966                 
22967                 
22968                 
22969                 
22970                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22971                 ah -= 5; // knock a few pixes off for look..
22972                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22973                 var eh = ah;
22974             }
22975         }
22976         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22977         this.editorcore.onResize(ew,eh);
22978         
22979     },
22980
22981     /**
22982      * Toggles the editor between standard and source edit mode.
22983      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22984      */
22985     toggleSourceEdit : function(sourceEditMode)
22986     {
22987         this.editorcore.toggleSourceEdit(sourceEditMode);
22988         
22989         if(this.editorcore.sourceEditMode){
22990             Roo.log('editor - showing textarea');
22991             
22992 //            Roo.log('in');
22993 //            Roo.log(this.syncValue());
22994             this.syncValue();
22995             this.inputEl().removeClass(['hide', 'x-hidden']);
22996             this.inputEl().dom.removeAttribute('tabIndex');
22997             this.inputEl().focus();
22998         }else{
22999             Roo.log('editor - hiding textarea');
23000 //            Roo.log('out')
23001 //            Roo.log(this.pushValue()); 
23002             this.pushValue();
23003             
23004             this.inputEl().addClass(['hide', 'x-hidden']);
23005             this.inputEl().dom.setAttribute('tabIndex', -1);
23006             //this.deferFocus();
23007         }
23008          
23009         if(this.resizable){
23010             this.setSize(this.wrap.getSize());
23011         }
23012         
23013         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23014     },
23015  
23016     // private (for BoxComponent)
23017     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23018
23019     // private (for BoxComponent)
23020     getResizeEl : function(){
23021         return this.wrap;
23022     },
23023
23024     // private (for BoxComponent)
23025     getPositionEl : function(){
23026         return this.wrap;
23027     },
23028
23029     // private
23030     initEvents : function(){
23031         this.originalValue = this.getValue();
23032     },
23033
23034 //    /**
23035 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23036 //     * @method
23037 //     */
23038 //    markInvalid : Roo.emptyFn,
23039 //    /**
23040 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23041 //     * @method
23042 //     */
23043 //    clearInvalid : Roo.emptyFn,
23044
23045     setValue : function(v){
23046         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23047         this.editorcore.pushValue();
23048     },
23049
23050      
23051     // private
23052     deferFocus : function(){
23053         this.focus.defer(10, this);
23054     },
23055
23056     // doc'ed in Field
23057     focus : function(){
23058         this.editorcore.focus();
23059         
23060     },
23061       
23062
23063     // private
23064     onDestroy : function(){
23065         
23066         
23067         
23068         if(this.rendered){
23069             
23070             for (var i =0; i < this.toolbars.length;i++) {
23071                 // fixme - ask toolbars for heights?
23072                 this.toolbars[i].onDestroy();
23073             }
23074             
23075             this.wrap.dom.innerHTML = '';
23076             this.wrap.remove();
23077         }
23078     },
23079
23080     // private
23081     onFirstFocus : function(){
23082         //Roo.log("onFirstFocus");
23083         this.editorcore.onFirstFocus();
23084          for (var i =0; i < this.toolbars.length;i++) {
23085             this.toolbars[i].onFirstFocus();
23086         }
23087         
23088     },
23089     
23090     // private
23091     syncValue : function()
23092     {   
23093         this.editorcore.syncValue();
23094     },
23095     
23096     pushValue : function()
23097     {   
23098         this.editorcore.pushValue();
23099     }
23100      
23101     
23102     // hide stuff that is not compatible
23103     /**
23104      * @event blur
23105      * @hide
23106      */
23107     /**
23108      * @event change
23109      * @hide
23110      */
23111     /**
23112      * @event focus
23113      * @hide
23114      */
23115     /**
23116      * @event specialkey
23117      * @hide
23118      */
23119     /**
23120      * @cfg {String} fieldClass @hide
23121      */
23122     /**
23123      * @cfg {String} focusClass @hide
23124      */
23125     /**
23126      * @cfg {String} autoCreate @hide
23127      */
23128     /**
23129      * @cfg {String} inputType @hide
23130      */
23131     /**
23132      * @cfg {String} invalidClass @hide
23133      */
23134     /**
23135      * @cfg {String} invalidText @hide
23136      */
23137     /**
23138      * @cfg {String} msgFx @hide
23139      */
23140     /**
23141      * @cfg {String} validateOnBlur @hide
23142      */
23143 });
23144  
23145     
23146    
23147    
23148    
23149       
23150 Roo.namespace('Roo.bootstrap.htmleditor');
23151 /**
23152  * @class Roo.bootstrap.HtmlEditorToolbar1
23153  * Basic Toolbar
23154  * 
23155  * Usage:
23156  *
23157  new Roo.bootstrap.HtmlEditor({
23158     ....
23159     toolbars : [
23160         new Roo.bootstrap.HtmlEditorToolbar1({
23161             disable : { fonts: 1 , format: 1, ..., ... , ...],
23162             btns : [ .... ]
23163         })
23164     }
23165      
23166  * 
23167  * @cfg {Object} disable List of elements to disable..
23168  * @cfg {Array} btns List of additional buttons.
23169  * 
23170  * 
23171  * NEEDS Extra CSS? 
23172  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23173  */
23174  
23175 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23176 {
23177     
23178     Roo.apply(this, config);
23179     
23180     // default disabled, based on 'good practice'..
23181     this.disable = this.disable || {};
23182     Roo.applyIf(this.disable, {
23183         fontSize : true,
23184         colors : true,
23185         specialElements : true
23186     });
23187     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23188     
23189     this.editor = config.editor;
23190     this.editorcore = config.editor.editorcore;
23191     
23192     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23193     
23194     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23195     // dont call parent... till later.
23196 }
23197 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23198      
23199     bar : true,
23200     
23201     editor : false,
23202     editorcore : false,
23203     
23204     
23205     formats : [
23206         "p" ,  
23207         "h1","h2","h3","h4","h5","h6", 
23208         "pre", "code", 
23209         "abbr", "acronym", "address", "cite", "samp", "var",
23210         'div','span'
23211     ],
23212     
23213     onRender : function(ct, position)
23214     {
23215        // Roo.log("Call onRender: " + this.xtype);
23216         
23217        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23218        Roo.log(this.el);
23219        this.el.dom.style.marginBottom = '0';
23220        var _this = this;
23221        var editorcore = this.editorcore;
23222        var editor= this.editor;
23223        
23224        var children = [];
23225        var btn = function(id,cmd , toggle, handler){
23226        
23227             var  event = toggle ? 'toggle' : 'click';
23228        
23229             var a = {
23230                 size : 'sm',
23231                 xtype: 'Button',
23232                 xns: Roo.bootstrap,
23233                 glyphicon : id,
23234                 cmd : id || cmd,
23235                 enableToggle:toggle !== false,
23236                 //html : 'submit'
23237                 pressed : toggle ? false : null,
23238                 listeners : {}
23239             };
23240             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23241                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23242             };
23243             children.push(a);
23244             return a;
23245        }
23246         
23247         var style = {
23248                 xtype: 'Button',
23249                 size : 'sm',
23250                 xns: Roo.bootstrap,
23251                 glyphicon : 'font',
23252                 //html : 'submit'
23253                 menu : {
23254                     xtype: 'Menu',
23255                     xns: Roo.bootstrap,
23256                     items:  []
23257                 }
23258         };
23259         Roo.each(this.formats, function(f) {
23260             style.menu.items.push({
23261                 xtype :'MenuItem',
23262                 xns: Roo.bootstrap,
23263                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23264                 tagname : f,
23265                 listeners : {
23266                     click : function()
23267                     {
23268                         editorcore.insertTag(this.tagname);
23269                         editor.focus();
23270                     }
23271                 }
23272                 
23273             });
23274         });
23275          children.push(style);   
23276             
23277             
23278         btn('bold',false,true);
23279         btn('italic',false,true);
23280         btn('align-left', 'justifyleft',true);
23281         btn('align-center', 'justifycenter',true);
23282         btn('align-right' , 'justifyright',true);
23283         btn('link', false, false, function(btn) {
23284             //Roo.log("create link?");
23285             var url = prompt(this.createLinkText, this.defaultLinkValue);
23286             if(url && url != 'http:/'+'/'){
23287                 this.editorcore.relayCmd('createlink', url);
23288             }
23289         }),
23290         btn('list','insertunorderedlist',true);
23291         btn('pencil', false,true, function(btn){
23292                 Roo.log(this);
23293                 
23294                 this.toggleSourceEdit(btn.pressed);
23295         });
23296         /*
23297         var cog = {
23298                 xtype: 'Button',
23299                 size : 'sm',
23300                 xns: Roo.bootstrap,
23301                 glyphicon : 'cog',
23302                 //html : 'submit'
23303                 menu : {
23304                     xtype: 'Menu',
23305                     xns: Roo.bootstrap,
23306                     items:  []
23307                 }
23308         };
23309         
23310         cog.menu.items.push({
23311             xtype :'MenuItem',
23312             xns: Roo.bootstrap,
23313             html : Clean styles,
23314             tagname : f,
23315             listeners : {
23316                 click : function()
23317                 {
23318                     editorcore.insertTag(this.tagname);
23319                     editor.focus();
23320                 }
23321             }
23322             
23323         });
23324        */
23325         
23326          
23327        this.xtype = 'NavSimplebar';
23328         
23329         for(var i=0;i< children.length;i++) {
23330             
23331             this.buttons.add(this.addxtypeChild(children[i]));
23332             
23333         }
23334         
23335         editor.on('editorevent', this.updateToolbar, this);
23336     },
23337     onBtnClick : function(id)
23338     {
23339        this.editorcore.relayCmd(id);
23340        this.editorcore.focus();
23341     },
23342     
23343     /**
23344      * Protected method that will not generally be called directly. It triggers
23345      * a toolbar update by reading the markup state of the current selection in the editor.
23346      */
23347     updateToolbar: function(){
23348
23349         if(!this.editorcore.activated){
23350             this.editor.onFirstFocus(); // is this neeed?
23351             return;
23352         }
23353
23354         var btns = this.buttons; 
23355         var doc = this.editorcore.doc;
23356         btns.get('bold').setActive(doc.queryCommandState('bold'));
23357         btns.get('italic').setActive(doc.queryCommandState('italic'));
23358         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23359         
23360         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23361         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23362         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23363         
23364         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23365         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23366          /*
23367         
23368         var ans = this.editorcore.getAllAncestors();
23369         if (this.formatCombo) {
23370             
23371             
23372             var store = this.formatCombo.store;
23373             this.formatCombo.setValue("");
23374             for (var i =0; i < ans.length;i++) {
23375                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23376                     // select it..
23377                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23378                     break;
23379                 }
23380             }
23381         }
23382         
23383         
23384         
23385         // hides menus... - so this cant be on a menu...
23386         Roo.bootstrap.MenuMgr.hideAll();
23387         */
23388         Roo.bootstrap.MenuMgr.hideAll();
23389         //this.editorsyncValue();
23390     },
23391     onFirstFocus: function() {
23392         this.buttons.each(function(item){
23393            item.enable();
23394         });
23395     },
23396     toggleSourceEdit : function(sourceEditMode){
23397         
23398           
23399         if(sourceEditMode){
23400             Roo.log("disabling buttons");
23401            this.buttons.each( function(item){
23402                 if(item.cmd != 'pencil'){
23403                     item.disable();
23404                 }
23405             });
23406           
23407         }else{
23408             Roo.log("enabling buttons");
23409             if(this.editorcore.initialized){
23410                 this.buttons.each( function(item){
23411                     item.enable();
23412                 });
23413             }
23414             
23415         }
23416         Roo.log("calling toggole on editor");
23417         // tell the editor that it's been pressed..
23418         this.editor.toggleSourceEdit(sourceEditMode);
23419        
23420     }
23421 });
23422
23423
23424
23425
23426
23427 /**
23428  * @class Roo.bootstrap.Table.AbstractSelectionModel
23429  * @extends Roo.util.Observable
23430  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23431  * implemented by descendant classes.  This class should not be directly instantiated.
23432  * @constructor
23433  */
23434 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23435     this.locked = false;
23436     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23437 };
23438
23439
23440 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23441     /** @ignore Called by the grid automatically. Do not call directly. */
23442     init : function(grid){
23443         this.grid = grid;
23444         this.initEvents();
23445     },
23446
23447     /**
23448      * Locks the selections.
23449      */
23450     lock : function(){
23451         this.locked = true;
23452     },
23453
23454     /**
23455      * Unlocks the selections.
23456      */
23457     unlock : function(){
23458         this.locked = false;
23459     },
23460
23461     /**
23462      * Returns true if the selections are locked.
23463      * @return {Boolean}
23464      */
23465     isLocked : function(){
23466         return this.locked;
23467     }
23468 });
23469 /**
23470  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23471  * @class Roo.bootstrap.Table.RowSelectionModel
23472  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23473  * It supports multiple selections and keyboard selection/navigation. 
23474  * @constructor
23475  * @param {Object} config
23476  */
23477
23478 Roo.bootstrap.Table.RowSelectionModel = function(config){
23479     Roo.apply(this, config);
23480     this.selections = new Roo.util.MixedCollection(false, function(o){
23481         return o.id;
23482     });
23483
23484     this.last = false;
23485     this.lastActive = false;
23486
23487     this.addEvents({
23488         /**
23489              * @event selectionchange
23490              * Fires when the selection changes
23491              * @param {SelectionModel} this
23492              */
23493             "selectionchange" : true,
23494         /**
23495              * @event afterselectionchange
23496              * Fires after the selection changes (eg. by key press or clicking)
23497              * @param {SelectionModel} this
23498              */
23499             "afterselectionchange" : true,
23500         /**
23501              * @event beforerowselect
23502              * Fires when a row is selected being selected, return false to cancel.
23503              * @param {SelectionModel} this
23504              * @param {Number} rowIndex The selected index
23505              * @param {Boolean} keepExisting False if other selections will be cleared
23506              */
23507             "beforerowselect" : true,
23508         /**
23509              * @event rowselect
23510              * Fires when a row is selected.
23511              * @param {SelectionModel} this
23512              * @param {Number} rowIndex The selected index
23513              * @param {Roo.data.Record} r The record
23514              */
23515             "rowselect" : true,
23516         /**
23517              * @event rowdeselect
23518              * Fires when a row is deselected.
23519              * @param {SelectionModel} this
23520              * @param {Number} rowIndex The selected index
23521              */
23522         "rowdeselect" : true
23523     });
23524     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23525     this.locked = false;
23526  };
23527
23528 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23529     /**
23530      * @cfg {Boolean} singleSelect
23531      * True to allow selection of only one row at a time (defaults to false)
23532      */
23533     singleSelect : false,
23534
23535     // private
23536     initEvents : function()
23537     {
23538
23539         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23540         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23541         //}else{ // allow click to work like normal
23542          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23543         //}
23544         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23545         this.grid.on("rowclick", this.handleMouseDown, this);
23546         
23547         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23548             "up" : function(e){
23549                 if(!e.shiftKey){
23550                     this.selectPrevious(e.shiftKey);
23551                 }else if(this.last !== false && this.lastActive !== false){
23552                     var last = this.last;
23553                     this.selectRange(this.last,  this.lastActive-1);
23554                     this.grid.getView().focusRow(this.lastActive);
23555                     if(last !== false){
23556                         this.last = last;
23557                     }
23558                 }else{
23559                     this.selectFirstRow();
23560                 }
23561                 this.fireEvent("afterselectionchange", this);
23562             },
23563             "down" : function(e){
23564                 if(!e.shiftKey){
23565                     this.selectNext(e.shiftKey);
23566                 }else if(this.last !== false && this.lastActive !== false){
23567                     var last = this.last;
23568                     this.selectRange(this.last,  this.lastActive+1);
23569                     this.grid.getView().focusRow(this.lastActive);
23570                     if(last !== false){
23571                         this.last = last;
23572                     }
23573                 }else{
23574                     this.selectFirstRow();
23575                 }
23576                 this.fireEvent("afterselectionchange", this);
23577             },
23578             scope: this
23579         });
23580         this.grid.store.on('load', function(){
23581             this.selections.clear();
23582         },this);
23583         /*
23584         var view = this.grid.view;
23585         view.on("refresh", this.onRefresh, this);
23586         view.on("rowupdated", this.onRowUpdated, this);
23587         view.on("rowremoved", this.onRemove, this);
23588         */
23589     },
23590
23591     // private
23592     onRefresh : function()
23593     {
23594         var ds = this.grid.store, i, v = this.grid.view;
23595         var s = this.selections;
23596         s.each(function(r){
23597             if((i = ds.indexOfId(r.id)) != -1){
23598                 v.onRowSelect(i);
23599             }else{
23600                 s.remove(r);
23601             }
23602         });
23603     },
23604
23605     // private
23606     onRemove : function(v, index, r){
23607         this.selections.remove(r);
23608     },
23609
23610     // private
23611     onRowUpdated : function(v, index, r){
23612         if(this.isSelected(r)){
23613             v.onRowSelect(index);
23614         }
23615     },
23616
23617     /**
23618      * Select records.
23619      * @param {Array} records The records to select
23620      * @param {Boolean} keepExisting (optional) True to keep existing selections
23621      */
23622     selectRecords : function(records, keepExisting)
23623     {
23624         if(!keepExisting){
23625             this.clearSelections();
23626         }
23627             var ds = this.grid.store;
23628         for(var i = 0, len = records.length; i < len; i++){
23629             this.selectRow(ds.indexOf(records[i]), true);
23630         }
23631     },
23632
23633     /**
23634      * Gets the number of selected rows.
23635      * @return {Number}
23636      */
23637     getCount : function(){
23638         return this.selections.length;
23639     },
23640
23641     /**
23642      * Selects the first row in the grid.
23643      */
23644     selectFirstRow : function(){
23645         this.selectRow(0);
23646     },
23647
23648     /**
23649      * Select the last row.
23650      * @param {Boolean} keepExisting (optional) True to keep existing selections
23651      */
23652     selectLastRow : function(keepExisting){
23653         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23654         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23655     },
23656
23657     /**
23658      * Selects the row immediately following the last selected row.
23659      * @param {Boolean} keepExisting (optional) True to keep existing selections
23660      */
23661     selectNext : function(keepExisting)
23662     {
23663             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23664             this.selectRow(this.last+1, keepExisting);
23665             this.grid.getView().focusRow(this.last);
23666         }
23667     },
23668
23669     /**
23670      * Selects the row that precedes the last selected row.
23671      * @param {Boolean} keepExisting (optional) True to keep existing selections
23672      */
23673     selectPrevious : function(keepExisting){
23674         if(this.last){
23675             this.selectRow(this.last-1, keepExisting);
23676             this.grid.getView().focusRow(this.last);
23677         }
23678     },
23679
23680     /**
23681      * Returns the selected records
23682      * @return {Array} Array of selected records
23683      */
23684     getSelections : function(){
23685         return [].concat(this.selections.items);
23686     },
23687
23688     /**
23689      * Returns the first selected record.
23690      * @return {Record}
23691      */
23692     getSelected : function(){
23693         return this.selections.itemAt(0);
23694     },
23695
23696
23697     /**
23698      * Clears all selections.
23699      */
23700     clearSelections : function(fast)
23701     {
23702         if(this.locked) {
23703             return;
23704         }
23705         if(fast !== true){
23706                 var ds = this.grid.store;
23707             var s = this.selections;
23708             s.each(function(r){
23709                 this.deselectRow(ds.indexOfId(r.id));
23710             }, this);
23711             s.clear();
23712         }else{
23713             this.selections.clear();
23714         }
23715         this.last = false;
23716     },
23717
23718
23719     /**
23720      * Selects all rows.
23721      */
23722     selectAll : function(){
23723         if(this.locked) {
23724             return;
23725         }
23726         this.selections.clear();
23727         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23728             this.selectRow(i, true);
23729         }
23730     },
23731
23732     /**
23733      * Returns True if there is a selection.
23734      * @return {Boolean}
23735      */
23736     hasSelection : function(){
23737         return this.selections.length > 0;
23738     },
23739
23740     /**
23741      * Returns True if the specified row is selected.
23742      * @param {Number/Record} record The record or index of the record to check
23743      * @return {Boolean}
23744      */
23745     isSelected : function(index){
23746             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23747         return (r && this.selections.key(r.id) ? true : false);
23748     },
23749
23750     /**
23751      * Returns True if the specified record id is selected.
23752      * @param {String} id The id of record to check
23753      * @return {Boolean}
23754      */
23755     isIdSelected : function(id){
23756         return (this.selections.key(id) ? true : false);
23757     },
23758
23759
23760     // private
23761     handleMouseDBClick : function(e, t){
23762         
23763     },
23764     // private
23765     handleMouseDown : function(e, t)
23766     {
23767             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23768         if(this.isLocked() || rowIndex < 0 ){
23769             return;
23770         };
23771         if(e.shiftKey && this.last !== false){
23772             var last = this.last;
23773             this.selectRange(last, rowIndex, e.ctrlKey);
23774             this.last = last; // reset the last
23775             t.focus();
23776     
23777         }else{
23778             var isSelected = this.isSelected(rowIndex);
23779             //Roo.log("select row:" + rowIndex);
23780             if(isSelected){
23781                 this.deselectRow(rowIndex);
23782             } else {
23783                         this.selectRow(rowIndex, true);
23784             }
23785     
23786             /*
23787                 if(e.button !== 0 && isSelected){
23788                 alert('rowIndex 2: ' + rowIndex);
23789                     view.focusRow(rowIndex);
23790                 }else if(e.ctrlKey && isSelected){
23791                     this.deselectRow(rowIndex);
23792                 }else if(!isSelected){
23793                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23794                     view.focusRow(rowIndex);
23795                 }
23796             */
23797         }
23798         this.fireEvent("afterselectionchange", this);
23799     },
23800     // private
23801     handleDragableRowClick :  function(grid, rowIndex, e) 
23802     {
23803         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23804             this.selectRow(rowIndex, false);
23805             grid.view.focusRow(rowIndex);
23806              this.fireEvent("afterselectionchange", this);
23807         }
23808     },
23809     
23810     /**
23811      * Selects multiple rows.
23812      * @param {Array} rows Array of the indexes of the row to select
23813      * @param {Boolean} keepExisting (optional) True to keep existing selections
23814      */
23815     selectRows : function(rows, keepExisting){
23816         if(!keepExisting){
23817             this.clearSelections();
23818         }
23819         for(var i = 0, len = rows.length; i < len; i++){
23820             this.selectRow(rows[i], true);
23821         }
23822     },
23823
23824     /**
23825      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23826      * @param {Number} startRow The index of the first row in the range
23827      * @param {Number} endRow The index of the last row in the range
23828      * @param {Boolean} keepExisting (optional) True to retain existing selections
23829      */
23830     selectRange : function(startRow, endRow, keepExisting){
23831         if(this.locked) {
23832             return;
23833         }
23834         if(!keepExisting){
23835             this.clearSelections();
23836         }
23837         if(startRow <= endRow){
23838             for(var i = startRow; i <= endRow; i++){
23839                 this.selectRow(i, true);
23840             }
23841         }else{
23842             for(var i = startRow; i >= endRow; i--){
23843                 this.selectRow(i, true);
23844             }
23845         }
23846     },
23847
23848     /**
23849      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23850      * @param {Number} startRow The index of the first row in the range
23851      * @param {Number} endRow The index of the last row in the range
23852      */
23853     deselectRange : function(startRow, endRow, preventViewNotify){
23854         if(this.locked) {
23855             return;
23856         }
23857         for(var i = startRow; i <= endRow; i++){
23858             this.deselectRow(i, preventViewNotify);
23859         }
23860     },
23861
23862     /**
23863      * Selects a row.
23864      * @param {Number} row The index of the row to select
23865      * @param {Boolean} keepExisting (optional) True to keep existing selections
23866      */
23867     selectRow : function(index, keepExisting, preventViewNotify)
23868     {
23869             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23870             return;
23871         }
23872         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23873             if(!keepExisting || this.singleSelect){
23874                 this.clearSelections();
23875             }
23876             
23877             var r = this.grid.store.getAt(index);
23878             //console.log('selectRow - record id :' + r.id);
23879             
23880             this.selections.add(r);
23881             this.last = this.lastActive = index;
23882             if(!preventViewNotify){
23883                 var proxy = new Roo.Element(
23884                                 this.grid.getRowDom(index)
23885                 );
23886                 proxy.addClass('bg-info info');
23887             }
23888             this.fireEvent("rowselect", this, index, r);
23889             this.fireEvent("selectionchange", this);
23890         }
23891     },
23892
23893     /**
23894      * Deselects a row.
23895      * @param {Number} row The index of the row to deselect
23896      */
23897     deselectRow : function(index, preventViewNotify)
23898     {
23899         if(this.locked) {
23900             return;
23901         }
23902         if(this.last == index){
23903             this.last = false;
23904         }
23905         if(this.lastActive == index){
23906             this.lastActive = false;
23907         }
23908         
23909         var r = this.grid.store.getAt(index);
23910         if (!r) {
23911             return;
23912         }
23913         
23914         this.selections.remove(r);
23915         //.console.log('deselectRow - record id :' + r.id);
23916         if(!preventViewNotify){
23917         
23918             var proxy = new Roo.Element(
23919                 this.grid.getRowDom(index)
23920             );
23921             proxy.removeClass('bg-info info');
23922         }
23923         this.fireEvent("rowdeselect", this, index);
23924         this.fireEvent("selectionchange", this);
23925     },
23926
23927     // private
23928     restoreLast : function(){
23929         if(this._last){
23930             this.last = this._last;
23931         }
23932     },
23933
23934     // private
23935     acceptsNav : function(row, col, cm){
23936         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23937     },
23938
23939     // private
23940     onEditorKey : function(field, e){
23941         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23942         if(k == e.TAB){
23943             e.stopEvent();
23944             ed.completeEdit();
23945             if(e.shiftKey){
23946                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23947             }else{
23948                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23949             }
23950         }else if(k == e.ENTER && !e.ctrlKey){
23951             e.stopEvent();
23952             ed.completeEdit();
23953             if(e.shiftKey){
23954                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23955             }else{
23956                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23957             }
23958         }else if(k == e.ESC){
23959             ed.cancelEdit();
23960         }
23961         if(newCell){
23962             g.startEditing(newCell[0], newCell[1]);
23963         }
23964     }
23965 });
23966 /*
23967  * Based on:
23968  * Ext JS Library 1.1.1
23969  * Copyright(c) 2006-2007, Ext JS, LLC.
23970  *
23971  * Originally Released Under LGPL - original licence link has changed is not relivant.
23972  *
23973  * Fork - LGPL
23974  * <script type="text/javascript">
23975  */
23976  
23977 /**
23978  * @class Roo.bootstrap.PagingToolbar
23979  * @extends Roo.bootstrap.NavSimplebar
23980  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23981  * @constructor
23982  * Create a new PagingToolbar
23983  * @param {Object} config The config object
23984  * @param {Roo.data.Store} store
23985  */
23986 Roo.bootstrap.PagingToolbar = function(config)
23987 {
23988     // old args format still supported... - xtype is prefered..
23989         // created from xtype...
23990     
23991     this.ds = config.dataSource;
23992     
23993     if (config.store && !this.ds) {
23994         this.store= Roo.factory(config.store, Roo.data);
23995         this.ds = this.store;
23996         this.ds.xmodule = this.xmodule || false;
23997     }
23998     
23999     this.toolbarItems = [];
24000     if (config.items) {
24001         this.toolbarItems = config.items;
24002     }
24003     
24004     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24005     
24006     this.cursor = 0;
24007     
24008     if (this.ds) { 
24009         this.bind(this.ds);
24010     }
24011     
24012     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24013     
24014 };
24015
24016 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24017     /**
24018      * @cfg {Roo.data.Store} dataSource
24019      * The underlying data store providing the paged data
24020      */
24021     /**
24022      * @cfg {String/HTMLElement/Element} container
24023      * container The id or element that will contain the toolbar
24024      */
24025     /**
24026      * @cfg {Boolean} displayInfo
24027      * True to display the displayMsg (defaults to false)
24028      */
24029     /**
24030      * @cfg {Number} pageSize
24031      * The number of records to display per page (defaults to 20)
24032      */
24033     pageSize: 20,
24034     /**
24035      * @cfg {String} displayMsg
24036      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24037      */
24038     displayMsg : 'Displaying {0} - {1} of {2}',
24039     /**
24040      * @cfg {String} emptyMsg
24041      * The message to display when no records are found (defaults to "No data to display")
24042      */
24043     emptyMsg : 'No data to display',
24044     /**
24045      * Customizable piece of the default paging text (defaults to "Page")
24046      * @type String
24047      */
24048     beforePageText : "Page",
24049     /**
24050      * Customizable piece of the default paging text (defaults to "of %0")
24051      * @type String
24052      */
24053     afterPageText : "of {0}",
24054     /**
24055      * Customizable piece of the default paging text (defaults to "First Page")
24056      * @type String
24057      */
24058     firstText : "First Page",
24059     /**
24060      * Customizable piece of the default paging text (defaults to "Previous Page")
24061      * @type String
24062      */
24063     prevText : "Previous Page",
24064     /**
24065      * Customizable piece of the default paging text (defaults to "Next Page")
24066      * @type String
24067      */
24068     nextText : "Next Page",
24069     /**
24070      * Customizable piece of the default paging text (defaults to "Last Page")
24071      * @type String
24072      */
24073     lastText : "Last Page",
24074     /**
24075      * Customizable piece of the default paging text (defaults to "Refresh")
24076      * @type String
24077      */
24078     refreshText : "Refresh",
24079
24080     buttons : false,
24081     // private
24082     onRender : function(ct, position) 
24083     {
24084         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24085         this.navgroup.parentId = this.id;
24086         this.navgroup.onRender(this.el, null);
24087         // add the buttons to the navgroup
24088         
24089         if(this.displayInfo){
24090             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24091             this.displayEl = this.el.select('.x-paging-info', true).first();
24092 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24093 //            this.displayEl = navel.el.select('span',true).first();
24094         }
24095         
24096         var _this = this;
24097         
24098         if(this.buttons){
24099             Roo.each(_this.buttons, function(e){ // this might need to use render????
24100                Roo.factory(e).onRender(_this.el, null);
24101             });
24102         }
24103             
24104         Roo.each(_this.toolbarItems, function(e) {
24105             _this.navgroup.addItem(e);
24106         });
24107         
24108         
24109         this.first = this.navgroup.addItem({
24110             tooltip: this.firstText,
24111             cls: "prev",
24112             icon : 'fa fa-backward',
24113             disabled: true,
24114             preventDefault: true,
24115             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24116         });
24117         
24118         this.prev =  this.navgroup.addItem({
24119             tooltip: this.prevText,
24120             cls: "prev",
24121             icon : 'fa fa-step-backward',
24122             disabled: true,
24123             preventDefault: true,
24124             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24125         });
24126     //this.addSeparator();
24127         
24128         
24129         var field = this.navgroup.addItem( {
24130             tagtype : 'span',
24131             cls : 'x-paging-position',
24132             
24133             html : this.beforePageText  +
24134                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24135                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24136          } ); //?? escaped?
24137         
24138         this.field = field.el.select('input', true).first();
24139         this.field.on("keydown", this.onPagingKeydown, this);
24140         this.field.on("focus", function(){this.dom.select();});
24141     
24142     
24143         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24144         //this.field.setHeight(18);
24145         //this.addSeparator();
24146         this.next = this.navgroup.addItem({
24147             tooltip: this.nextText,
24148             cls: "next",
24149             html : ' <i class="fa fa-step-forward">',
24150             disabled: true,
24151             preventDefault: true,
24152             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24153         });
24154         this.last = this.navgroup.addItem({
24155             tooltip: this.lastText,
24156             icon : 'fa fa-forward',
24157             cls: "next",
24158             disabled: true,
24159             preventDefault: true,
24160             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24161         });
24162     //this.addSeparator();
24163         this.loading = this.navgroup.addItem({
24164             tooltip: this.refreshText,
24165             icon: 'fa fa-refresh',
24166             preventDefault: true,
24167             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24168         });
24169         
24170     },
24171
24172     // private
24173     updateInfo : function(){
24174         if(this.displayEl){
24175             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24176             var msg = count == 0 ?
24177                 this.emptyMsg :
24178                 String.format(
24179                     this.displayMsg,
24180                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24181                 );
24182             this.displayEl.update(msg);
24183         }
24184     },
24185
24186     // private
24187     onLoad : function(ds, r, o){
24188        this.cursor = o.params ? o.params.start : 0;
24189        var d = this.getPageData(),
24190             ap = d.activePage,
24191             ps = d.pages;
24192         
24193        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24194        this.field.dom.value = ap;
24195        this.first.setDisabled(ap == 1);
24196        this.prev.setDisabled(ap == 1);
24197        this.next.setDisabled(ap == ps);
24198        this.last.setDisabled(ap == ps);
24199        this.loading.enable();
24200        this.updateInfo();
24201     },
24202
24203     // private
24204     getPageData : function(){
24205         var total = this.ds.getTotalCount();
24206         return {
24207             total : total,
24208             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24209             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24210         };
24211     },
24212
24213     // private
24214     onLoadError : function(){
24215         this.loading.enable();
24216     },
24217
24218     // private
24219     onPagingKeydown : function(e){
24220         var k = e.getKey();
24221         var d = this.getPageData();
24222         if(k == e.RETURN){
24223             var v = this.field.dom.value, pageNum;
24224             if(!v || isNaN(pageNum = parseInt(v, 10))){
24225                 this.field.dom.value = d.activePage;
24226                 return;
24227             }
24228             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24229             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24230             e.stopEvent();
24231         }
24232         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))
24233         {
24234           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24235           this.field.dom.value = pageNum;
24236           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24237           e.stopEvent();
24238         }
24239         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24240         {
24241           var v = this.field.dom.value, pageNum; 
24242           var increment = (e.shiftKey) ? 10 : 1;
24243           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24244                 increment *= -1;
24245           }
24246           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24247             this.field.dom.value = d.activePage;
24248             return;
24249           }
24250           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24251           {
24252             this.field.dom.value = parseInt(v, 10) + increment;
24253             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24254             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24255           }
24256           e.stopEvent();
24257         }
24258     },
24259
24260     // private
24261     beforeLoad : function(){
24262         if(this.loading){
24263             this.loading.disable();
24264         }
24265     },
24266
24267     // private
24268     onClick : function(which){
24269         
24270         var ds = this.ds;
24271         if (!ds) {
24272             return;
24273         }
24274         
24275         switch(which){
24276             case "first":
24277                 ds.load({params:{start: 0, limit: this.pageSize}});
24278             break;
24279             case "prev":
24280                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24281             break;
24282             case "next":
24283                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24284             break;
24285             case "last":
24286                 var total = ds.getTotalCount();
24287                 var extra = total % this.pageSize;
24288                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24289                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24290             break;
24291             case "refresh":
24292                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24293             break;
24294         }
24295     },
24296
24297     /**
24298      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24299      * @param {Roo.data.Store} store The data store to unbind
24300      */
24301     unbind : function(ds){
24302         ds.un("beforeload", this.beforeLoad, this);
24303         ds.un("load", this.onLoad, this);
24304         ds.un("loadexception", this.onLoadError, this);
24305         ds.un("remove", this.updateInfo, this);
24306         ds.un("add", this.updateInfo, this);
24307         this.ds = undefined;
24308     },
24309
24310     /**
24311      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24312      * @param {Roo.data.Store} store The data store to bind
24313      */
24314     bind : function(ds){
24315         ds.on("beforeload", this.beforeLoad, this);
24316         ds.on("load", this.onLoad, this);
24317         ds.on("loadexception", this.onLoadError, this);
24318         ds.on("remove", this.updateInfo, this);
24319         ds.on("add", this.updateInfo, this);
24320         this.ds = ds;
24321     }
24322 });/*
24323  * - LGPL
24324  *
24325  * element
24326  * 
24327  */
24328
24329 /**
24330  * @class Roo.bootstrap.MessageBar
24331  * @extends Roo.bootstrap.Component
24332  * Bootstrap MessageBar class
24333  * @cfg {String} html contents of the MessageBar
24334  * @cfg {String} weight (info | success | warning | danger) default info
24335  * @cfg {String} beforeClass insert the bar before the given class
24336  * @cfg {Boolean} closable (true | false) default false
24337  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24338  * 
24339  * @constructor
24340  * Create a new Element
24341  * @param {Object} config The config object
24342  */
24343
24344 Roo.bootstrap.MessageBar = function(config){
24345     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24346 };
24347
24348 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24349     
24350     html: '',
24351     weight: 'info',
24352     closable: false,
24353     fixed: false,
24354     beforeClass: 'bootstrap-sticky-wrap',
24355     
24356     getAutoCreate : function(){
24357         
24358         var cfg = {
24359             tag: 'div',
24360             cls: 'alert alert-dismissable alert-' + this.weight,
24361             cn: [
24362                 {
24363                     tag: 'span',
24364                     cls: 'message',
24365                     html: this.html || ''
24366                 }
24367             ]
24368         };
24369         
24370         if(this.fixed){
24371             cfg.cls += ' alert-messages-fixed';
24372         }
24373         
24374         if(this.closable){
24375             cfg.cn.push({
24376                 tag: 'button',
24377                 cls: 'close',
24378                 html: 'x'
24379             });
24380         }
24381         
24382         return cfg;
24383     },
24384     
24385     onRender : function(ct, position)
24386     {
24387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24388         
24389         if(!this.el){
24390             var cfg = Roo.apply({},  this.getAutoCreate());
24391             cfg.id = Roo.id();
24392             
24393             if (this.cls) {
24394                 cfg.cls += ' ' + this.cls;
24395             }
24396             if (this.style) {
24397                 cfg.style = this.style;
24398             }
24399             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24400             
24401             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24402         }
24403         
24404         this.el.select('>button.close').on('click', this.hide, this);
24405         
24406     },
24407     
24408     show : function()
24409     {
24410         if (!this.rendered) {
24411             this.render();
24412         }
24413         
24414         this.el.show();
24415         
24416         this.fireEvent('show', this);
24417         
24418     },
24419     
24420     hide : function()
24421     {
24422         if (!this.rendered) {
24423             this.render();
24424         }
24425         
24426         this.el.hide();
24427         
24428         this.fireEvent('hide', this);
24429     },
24430     
24431     update : function()
24432     {
24433 //        var e = this.el.dom.firstChild;
24434 //        
24435 //        if(this.closable){
24436 //            e = e.nextSibling;
24437 //        }
24438 //        
24439 //        e.data = this.html || '';
24440
24441         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24442     }
24443    
24444 });
24445
24446  
24447
24448      /*
24449  * - LGPL
24450  *
24451  * Graph
24452  * 
24453  */
24454
24455
24456 /**
24457  * @class Roo.bootstrap.Graph
24458  * @extends Roo.bootstrap.Component
24459  * Bootstrap Graph class
24460 > Prameters
24461  -sm {number} sm 4
24462  -md {number} md 5
24463  @cfg {String} graphtype  bar | vbar | pie
24464  @cfg {number} g_x coodinator | centre x (pie)
24465  @cfg {number} g_y coodinator | centre y (pie)
24466  @cfg {number} g_r radius (pie)
24467  @cfg {number} g_height height of the chart (respected by all elements in the set)
24468  @cfg {number} g_width width of the chart (respected by all elements in the set)
24469  @cfg {Object} title The title of the chart
24470     
24471  -{Array}  values
24472  -opts (object) options for the chart 
24473      o {
24474      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24475      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24476      o vgutter (number)
24477      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.
24478      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24479      o to
24480      o stretch (boolean)
24481      o }
24482  -opts (object) options for the pie
24483      o{
24484      o cut
24485      o startAngle (number)
24486      o endAngle (number)
24487      } 
24488  *
24489  * @constructor
24490  * Create a new Input
24491  * @param {Object} config The config object
24492  */
24493
24494 Roo.bootstrap.Graph = function(config){
24495     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24496     
24497     this.addEvents({
24498         // img events
24499         /**
24500          * @event click
24501          * The img click event for the img.
24502          * @param {Roo.EventObject} e
24503          */
24504         "click" : true
24505     });
24506 };
24507
24508 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24509     
24510     sm: 4,
24511     md: 5,
24512     graphtype: 'bar',
24513     g_height: 250,
24514     g_width: 400,
24515     g_x: 50,
24516     g_y: 50,
24517     g_r: 30,
24518     opts:{
24519         //g_colors: this.colors,
24520         g_type: 'soft',
24521         g_gutter: '20%'
24522
24523     },
24524     title : false,
24525
24526     getAutoCreate : function(){
24527         
24528         var cfg = {
24529             tag: 'div',
24530             html : null
24531         };
24532         
24533         
24534         return  cfg;
24535     },
24536
24537     onRender : function(ct,position){
24538         
24539         
24540         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24541         
24542         if (typeof(Raphael) == 'undefined') {
24543             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24544             return;
24545         }
24546         
24547         this.raphael = Raphael(this.el.dom);
24548         
24549                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24550                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24551                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24552                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24553                 /*
24554                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24555                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24556                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24557                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24558                 
24559                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24560                 r.barchart(330, 10, 300, 220, data1);
24561                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24562                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24563                 */
24564                 
24565                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24566                 // r.barchart(30, 30, 560, 250,  xdata, {
24567                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24568                 //     axis : "0 0 1 1",
24569                 //     axisxlabels :  xdata
24570                 //     //yvalues : cols,
24571                    
24572                 // });
24573 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24574 //        
24575 //        this.load(null,xdata,{
24576 //                axis : "0 0 1 1",
24577 //                axisxlabels :  xdata
24578 //                });
24579
24580     },
24581
24582     load : function(graphtype,xdata,opts)
24583     {
24584         this.raphael.clear();
24585         if(!graphtype) {
24586             graphtype = this.graphtype;
24587         }
24588         if(!opts){
24589             opts = this.opts;
24590         }
24591         var r = this.raphael,
24592             fin = function () {
24593                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24594             },
24595             fout = function () {
24596                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24597             },
24598             pfin = function() {
24599                 this.sector.stop();
24600                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24601
24602                 if (this.label) {
24603                     this.label[0].stop();
24604                     this.label[0].attr({ r: 7.5 });
24605                     this.label[1].attr({ "font-weight": 800 });
24606                 }
24607             },
24608             pfout = function() {
24609                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24610
24611                 if (this.label) {
24612                     this.label[0].animate({ r: 5 }, 500, "bounce");
24613                     this.label[1].attr({ "font-weight": 400 });
24614                 }
24615             };
24616
24617         switch(graphtype){
24618             case 'bar':
24619                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24620                 break;
24621             case 'hbar':
24622                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24623                 break;
24624             case 'pie':
24625 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24626 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24627 //            
24628                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24629                 
24630                 break;
24631
24632         }
24633         
24634         if(this.title){
24635             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24636         }
24637         
24638     },
24639     
24640     setTitle: function(o)
24641     {
24642         this.title = o;
24643     },
24644     
24645     initEvents: function() {
24646         
24647         if(!this.href){
24648             this.el.on('click', this.onClick, this);
24649         }
24650     },
24651     
24652     onClick : function(e)
24653     {
24654         Roo.log('img onclick');
24655         this.fireEvent('click', this, e);
24656     }
24657    
24658 });
24659
24660  
24661 /*
24662  * - LGPL
24663  *
24664  * numberBox
24665  * 
24666  */
24667 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24668
24669 /**
24670  * @class Roo.bootstrap.dash.NumberBox
24671  * @extends Roo.bootstrap.Component
24672  * Bootstrap NumberBox class
24673  * @cfg {String} headline Box headline
24674  * @cfg {String} content Box content
24675  * @cfg {String} icon Box icon
24676  * @cfg {String} footer Footer text
24677  * @cfg {String} fhref Footer href
24678  * 
24679  * @constructor
24680  * Create a new NumberBox
24681  * @param {Object} config The config object
24682  */
24683
24684
24685 Roo.bootstrap.dash.NumberBox = function(config){
24686     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24687     
24688 };
24689
24690 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24691     
24692     headline : '',
24693     content : '',
24694     icon : '',
24695     footer : '',
24696     fhref : '',
24697     ficon : '',
24698     
24699     getAutoCreate : function(){
24700         
24701         var cfg = {
24702             tag : 'div',
24703             cls : 'small-box ',
24704             cn : [
24705                 {
24706                     tag : 'div',
24707                     cls : 'inner',
24708                     cn :[
24709                         {
24710                             tag : 'h3',
24711                             cls : 'roo-headline',
24712                             html : this.headline
24713                         },
24714                         {
24715                             tag : 'p',
24716                             cls : 'roo-content',
24717                             html : this.content
24718                         }
24719                     ]
24720                 }
24721             ]
24722         };
24723         
24724         if(this.icon){
24725             cfg.cn.push({
24726                 tag : 'div',
24727                 cls : 'icon',
24728                 cn :[
24729                     {
24730                         tag : 'i',
24731                         cls : 'ion ' + this.icon
24732                     }
24733                 ]
24734             });
24735         }
24736         
24737         if(this.footer){
24738             var footer = {
24739                 tag : 'a',
24740                 cls : 'small-box-footer',
24741                 href : this.fhref || '#',
24742                 html : this.footer
24743             };
24744             
24745             cfg.cn.push(footer);
24746             
24747         }
24748         
24749         return  cfg;
24750     },
24751
24752     onRender : function(ct,position){
24753         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24754
24755
24756        
24757                 
24758     },
24759
24760     setHeadline: function (value)
24761     {
24762         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24763     },
24764     
24765     setFooter: function (value, href)
24766     {
24767         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24768         
24769         if(href){
24770             this.el.select('a.small-box-footer',true).first().attr('href', href);
24771         }
24772         
24773     },
24774
24775     setContent: function (value)
24776     {
24777         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24778     },
24779
24780     initEvents: function() 
24781     {   
24782         
24783     }
24784     
24785 });
24786
24787  
24788 /*
24789  * - LGPL
24790  *
24791  * TabBox
24792  * 
24793  */
24794 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24795
24796 /**
24797  * @class Roo.bootstrap.dash.TabBox
24798  * @extends Roo.bootstrap.Component
24799  * Bootstrap TabBox class
24800  * @cfg {String} title Title of the TabBox
24801  * @cfg {String} icon Icon of the TabBox
24802  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24803  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24804  * 
24805  * @constructor
24806  * Create a new TabBox
24807  * @param {Object} config The config object
24808  */
24809
24810
24811 Roo.bootstrap.dash.TabBox = function(config){
24812     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24813     this.addEvents({
24814         // raw events
24815         /**
24816          * @event addpane
24817          * When a pane is added
24818          * @param {Roo.bootstrap.dash.TabPane} pane
24819          */
24820         "addpane" : true,
24821         /**
24822          * @event activatepane
24823          * When a pane is activated
24824          * @param {Roo.bootstrap.dash.TabPane} pane
24825          */
24826         "activatepane" : true
24827         
24828          
24829     });
24830     
24831     this.panes = [];
24832 };
24833
24834 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24835
24836     title : '',
24837     icon : false,
24838     showtabs : true,
24839     tabScrollable : false,
24840     
24841     getChildContainer : function()
24842     {
24843         return this.el.select('.tab-content', true).first();
24844     },
24845     
24846     getAutoCreate : function(){
24847         
24848         var header = {
24849             tag: 'li',
24850             cls: 'pull-left header',
24851             html: this.title,
24852             cn : []
24853         };
24854         
24855         if(this.icon){
24856             header.cn.push({
24857                 tag: 'i',
24858                 cls: 'fa ' + this.icon
24859             });
24860         }
24861         
24862         var h = {
24863             tag: 'ul',
24864             cls: 'nav nav-tabs pull-right',
24865             cn: [
24866                 header
24867             ]
24868         };
24869         
24870         if(this.tabScrollable){
24871             h = {
24872                 tag: 'div',
24873                 cls: 'tab-header',
24874                 cn: [
24875                     {
24876                         tag: 'ul',
24877                         cls: 'nav nav-tabs pull-right',
24878                         cn: [
24879                             header
24880                         ]
24881                     }
24882                 ]
24883             };
24884         }
24885         
24886         var cfg = {
24887             tag: 'div',
24888             cls: 'nav-tabs-custom',
24889             cn: [
24890                 h,
24891                 {
24892                     tag: 'div',
24893                     cls: 'tab-content no-padding',
24894                     cn: []
24895                 }
24896             ]
24897         };
24898
24899         return  cfg;
24900     },
24901     initEvents : function()
24902     {
24903         //Roo.log('add add pane handler');
24904         this.on('addpane', this.onAddPane, this);
24905     },
24906      /**
24907      * Updates the box title
24908      * @param {String} html to set the title to.
24909      */
24910     setTitle : function(value)
24911     {
24912         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24913     },
24914     onAddPane : function(pane)
24915     {
24916         this.panes.push(pane);
24917         //Roo.log('addpane');
24918         //Roo.log(pane);
24919         // tabs are rendere left to right..
24920         if(!this.showtabs){
24921             return;
24922         }
24923         
24924         var ctr = this.el.select('.nav-tabs', true).first();
24925          
24926          
24927         var existing = ctr.select('.nav-tab',true);
24928         var qty = existing.getCount();;
24929         
24930         
24931         var tab = ctr.createChild({
24932             tag : 'li',
24933             cls : 'nav-tab' + (qty ? '' : ' active'),
24934             cn : [
24935                 {
24936                     tag : 'a',
24937                     href:'#',
24938                     html : pane.title
24939                 }
24940             ]
24941         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24942         pane.tab = tab;
24943         
24944         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24945         if (!qty) {
24946             pane.el.addClass('active');
24947         }
24948         
24949                 
24950     },
24951     onTabClick : function(ev,un,ob,pane)
24952     {
24953         //Roo.log('tab - prev default');
24954         ev.preventDefault();
24955         
24956         
24957         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24958         pane.tab.addClass('active');
24959         //Roo.log(pane.title);
24960         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24961         // technically we should have a deactivate event.. but maybe add later.
24962         // and it should not de-activate the selected tab...
24963         this.fireEvent('activatepane', pane);
24964         pane.el.addClass('active');
24965         pane.fireEvent('activate');
24966         
24967         
24968     },
24969     
24970     getActivePane : function()
24971     {
24972         var r = false;
24973         Roo.each(this.panes, function(p) {
24974             if(p.el.hasClass('active')){
24975                 r = p;
24976                 return false;
24977             }
24978             
24979             return;
24980         });
24981         
24982         return r;
24983     }
24984     
24985     
24986 });
24987
24988  
24989 /*
24990  * - LGPL
24991  *
24992  * Tab pane
24993  * 
24994  */
24995 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24996 /**
24997  * @class Roo.bootstrap.TabPane
24998  * @extends Roo.bootstrap.Component
24999  * Bootstrap TabPane class
25000  * @cfg {Boolean} active (false | true) Default false
25001  * @cfg {String} title title of panel
25002
25003  * 
25004  * @constructor
25005  * Create a new TabPane
25006  * @param {Object} config The config object
25007  */
25008
25009 Roo.bootstrap.dash.TabPane = function(config){
25010     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25011     
25012     this.addEvents({
25013         // raw events
25014         /**
25015          * @event activate
25016          * When a pane is activated
25017          * @param {Roo.bootstrap.dash.TabPane} pane
25018          */
25019         "activate" : true
25020          
25021     });
25022 };
25023
25024 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25025     
25026     active : false,
25027     title : '',
25028     
25029     // the tabBox that this is attached to.
25030     tab : false,
25031      
25032     getAutoCreate : function() 
25033     {
25034         var cfg = {
25035             tag: 'div',
25036             cls: 'tab-pane'
25037         };
25038         
25039         if(this.active){
25040             cfg.cls += ' active';
25041         }
25042         
25043         return cfg;
25044     },
25045     initEvents  : function()
25046     {
25047         //Roo.log('trigger add pane handler');
25048         this.parent().fireEvent('addpane', this)
25049     },
25050     
25051      /**
25052      * Updates the tab title 
25053      * @param {String} html to set the title to.
25054      */
25055     setTitle: function(str)
25056     {
25057         if (!this.tab) {
25058             return;
25059         }
25060         this.title = str;
25061         this.tab.select('a', true).first().dom.innerHTML = str;
25062         
25063     }
25064     
25065     
25066     
25067 });
25068
25069  
25070
25071
25072  /*
25073  * - LGPL
25074  *
25075  * menu
25076  * 
25077  */
25078 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25079
25080 /**
25081  * @class Roo.bootstrap.menu.Menu
25082  * @extends Roo.bootstrap.Component
25083  * Bootstrap Menu class - container for Menu
25084  * @cfg {String} html Text of the menu
25085  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25086  * @cfg {String} icon Font awesome icon
25087  * @cfg {String} pos Menu align to (top | bottom) default bottom
25088  * 
25089  * 
25090  * @constructor
25091  * Create a new Menu
25092  * @param {Object} config The config object
25093  */
25094
25095
25096 Roo.bootstrap.menu.Menu = function(config){
25097     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25098     
25099     this.addEvents({
25100         /**
25101          * @event beforeshow
25102          * Fires before this menu is displayed
25103          * @param {Roo.bootstrap.menu.Menu} this
25104          */
25105         beforeshow : true,
25106         /**
25107          * @event beforehide
25108          * Fires before this menu is hidden
25109          * @param {Roo.bootstrap.menu.Menu} this
25110          */
25111         beforehide : true,
25112         /**
25113          * @event show
25114          * Fires after this menu is displayed
25115          * @param {Roo.bootstrap.menu.Menu} this
25116          */
25117         show : true,
25118         /**
25119          * @event hide
25120          * Fires after this menu is hidden
25121          * @param {Roo.bootstrap.menu.Menu} this
25122          */
25123         hide : true,
25124         /**
25125          * @event click
25126          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25127          * @param {Roo.bootstrap.menu.Menu} this
25128          * @param {Roo.EventObject} e
25129          */
25130         click : true
25131     });
25132     
25133 };
25134
25135 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25136     
25137     submenu : false,
25138     html : '',
25139     weight : 'default',
25140     icon : false,
25141     pos : 'bottom',
25142     
25143     
25144     getChildContainer : function() {
25145         if(this.isSubMenu){
25146             return this.el;
25147         }
25148         
25149         return this.el.select('ul.dropdown-menu', true).first();  
25150     },
25151     
25152     getAutoCreate : function()
25153     {
25154         var text = [
25155             {
25156                 tag : 'span',
25157                 cls : 'roo-menu-text',
25158                 html : this.html
25159             }
25160         ];
25161         
25162         if(this.icon){
25163             text.unshift({
25164                 tag : 'i',
25165                 cls : 'fa ' + this.icon
25166             })
25167         }
25168         
25169         
25170         var cfg = {
25171             tag : 'div',
25172             cls : 'btn-group',
25173             cn : [
25174                 {
25175                     tag : 'button',
25176                     cls : 'dropdown-button btn btn-' + this.weight,
25177                     cn : text
25178                 },
25179                 {
25180                     tag : 'button',
25181                     cls : 'dropdown-toggle btn btn-' + this.weight,
25182                     cn : [
25183                         {
25184                             tag : 'span',
25185                             cls : 'caret'
25186                         }
25187                     ]
25188                 },
25189                 {
25190                     tag : 'ul',
25191                     cls : 'dropdown-menu'
25192                 }
25193             ]
25194             
25195         };
25196         
25197         if(this.pos == 'top'){
25198             cfg.cls += ' dropup';
25199         }
25200         
25201         if(this.isSubMenu){
25202             cfg = {
25203                 tag : 'ul',
25204                 cls : 'dropdown-menu'
25205             }
25206         }
25207         
25208         return cfg;
25209     },
25210     
25211     onRender : function(ct, position)
25212     {
25213         this.isSubMenu = ct.hasClass('dropdown-submenu');
25214         
25215         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25216     },
25217     
25218     initEvents : function() 
25219     {
25220         if(this.isSubMenu){
25221             return;
25222         }
25223         
25224         this.hidden = true;
25225         
25226         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25227         this.triggerEl.on('click', this.onTriggerPress, this);
25228         
25229         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25230         this.buttonEl.on('click', this.onClick, this);
25231         
25232     },
25233     
25234     list : function()
25235     {
25236         if(this.isSubMenu){
25237             return this.el;
25238         }
25239         
25240         return this.el.select('ul.dropdown-menu', true).first();
25241     },
25242     
25243     onClick : function(e)
25244     {
25245         this.fireEvent("click", this, e);
25246     },
25247     
25248     onTriggerPress  : function(e)
25249     {   
25250         if (this.isVisible()) {
25251             this.hide();
25252         } else {
25253             this.show();
25254         }
25255     },
25256     
25257     isVisible : function(){
25258         return !this.hidden;
25259     },
25260     
25261     show : function()
25262     {
25263         this.fireEvent("beforeshow", this);
25264         
25265         this.hidden = false;
25266         this.el.addClass('open');
25267         
25268         Roo.get(document).on("mouseup", this.onMouseUp, this);
25269         
25270         this.fireEvent("show", this);
25271         
25272         
25273     },
25274     
25275     hide : function()
25276     {
25277         this.fireEvent("beforehide", this);
25278         
25279         this.hidden = true;
25280         this.el.removeClass('open');
25281         
25282         Roo.get(document).un("mouseup", this.onMouseUp);
25283         
25284         this.fireEvent("hide", this);
25285     },
25286     
25287     onMouseUp : function()
25288     {
25289         this.hide();
25290     }
25291     
25292 });
25293
25294  
25295  /*
25296  * - LGPL
25297  *
25298  * menu item
25299  * 
25300  */
25301 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25302
25303 /**
25304  * @class Roo.bootstrap.menu.Item
25305  * @extends Roo.bootstrap.Component
25306  * Bootstrap MenuItem class
25307  * @cfg {Boolean} submenu (true | false) default false
25308  * @cfg {String} html text of the item
25309  * @cfg {String} href the link
25310  * @cfg {Boolean} disable (true | false) default false
25311  * @cfg {Boolean} preventDefault (true | false) default true
25312  * @cfg {String} icon Font awesome icon
25313  * @cfg {String} pos Submenu align to (left | right) default right 
25314  * 
25315  * 
25316  * @constructor
25317  * Create a new Item
25318  * @param {Object} config The config object
25319  */
25320
25321
25322 Roo.bootstrap.menu.Item = function(config){
25323     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25324     this.addEvents({
25325         /**
25326          * @event mouseover
25327          * Fires when the mouse is hovering over this menu
25328          * @param {Roo.bootstrap.menu.Item} this
25329          * @param {Roo.EventObject} e
25330          */
25331         mouseover : true,
25332         /**
25333          * @event mouseout
25334          * Fires when the mouse exits this menu
25335          * @param {Roo.bootstrap.menu.Item} this
25336          * @param {Roo.EventObject} e
25337          */
25338         mouseout : true,
25339         // raw events
25340         /**
25341          * @event click
25342          * The raw click event for the entire grid.
25343          * @param {Roo.EventObject} e
25344          */
25345         click : true
25346     });
25347 };
25348
25349 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25350     
25351     submenu : false,
25352     href : '',
25353     html : '',
25354     preventDefault: true,
25355     disable : false,
25356     icon : false,
25357     pos : 'right',
25358     
25359     getAutoCreate : function()
25360     {
25361         var text = [
25362             {
25363                 tag : 'span',
25364                 cls : 'roo-menu-item-text',
25365                 html : this.html
25366             }
25367         ];
25368         
25369         if(this.icon){
25370             text.unshift({
25371                 tag : 'i',
25372                 cls : 'fa ' + this.icon
25373             })
25374         }
25375         
25376         var cfg = {
25377             tag : 'li',
25378             cn : [
25379                 {
25380                     tag : 'a',
25381                     href : this.href || '#',
25382                     cn : text
25383                 }
25384             ]
25385         };
25386         
25387         if(this.disable){
25388             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25389         }
25390         
25391         if(this.submenu){
25392             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25393             
25394             if(this.pos == 'left'){
25395                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25396             }
25397         }
25398         
25399         return cfg;
25400     },
25401     
25402     initEvents : function() 
25403     {
25404         this.el.on('mouseover', this.onMouseOver, this);
25405         this.el.on('mouseout', this.onMouseOut, this);
25406         
25407         this.el.select('a', true).first().on('click', this.onClick, this);
25408         
25409     },
25410     
25411     onClick : function(e)
25412     {
25413         if(this.preventDefault){
25414             e.preventDefault();
25415         }
25416         
25417         this.fireEvent("click", this, e);
25418     },
25419     
25420     onMouseOver : function(e)
25421     {
25422         if(this.submenu && this.pos == 'left'){
25423             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25424         }
25425         
25426         this.fireEvent("mouseover", this, e);
25427     },
25428     
25429     onMouseOut : function(e)
25430     {
25431         this.fireEvent("mouseout", this, e);
25432     }
25433 });
25434
25435  
25436
25437  /*
25438  * - LGPL
25439  *
25440  * menu separator
25441  * 
25442  */
25443 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25444
25445 /**
25446  * @class Roo.bootstrap.menu.Separator
25447  * @extends Roo.bootstrap.Component
25448  * Bootstrap Separator class
25449  * 
25450  * @constructor
25451  * Create a new Separator
25452  * @param {Object} config The config object
25453  */
25454
25455
25456 Roo.bootstrap.menu.Separator = function(config){
25457     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25458 };
25459
25460 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25461     
25462     getAutoCreate : function(){
25463         var cfg = {
25464             tag : 'li',
25465             cls: 'divider'
25466         };
25467         
25468         return cfg;
25469     }
25470    
25471 });
25472
25473  
25474
25475  /*
25476  * - LGPL
25477  *
25478  * Tooltip
25479  * 
25480  */
25481
25482 /**
25483  * @class Roo.bootstrap.Tooltip
25484  * Bootstrap Tooltip class
25485  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25486  * to determine which dom element triggers the tooltip.
25487  * 
25488  * It needs to add support for additional attributes like tooltip-position
25489  * 
25490  * @constructor
25491  * Create a new Toolti
25492  * @param {Object} config The config object
25493  */
25494
25495 Roo.bootstrap.Tooltip = function(config){
25496     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25497     
25498     this.alignment = Roo.bootstrap.Tooltip.alignment;
25499     
25500     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25501         this.alignment = config.alignment;
25502     }
25503     
25504 };
25505
25506 Roo.apply(Roo.bootstrap.Tooltip, {
25507     /**
25508      * @function init initialize tooltip monitoring.
25509      * @static
25510      */
25511     currentEl : false,
25512     currentTip : false,
25513     currentRegion : false,
25514     
25515     //  init : delay?
25516     
25517     init : function()
25518     {
25519         Roo.get(document).on('mouseover', this.enter ,this);
25520         Roo.get(document).on('mouseout', this.leave, this);
25521          
25522         
25523         this.currentTip = new Roo.bootstrap.Tooltip();
25524     },
25525     
25526     enter : function(ev)
25527     {
25528         var dom = ev.getTarget();
25529         
25530         //Roo.log(['enter',dom]);
25531         var el = Roo.fly(dom);
25532         if (this.currentEl) {
25533             //Roo.log(dom);
25534             //Roo.log(this.currentEl);
25535             //Roo.log(this.currentEl.contains(dom));
25536             if (this.currentEl == el) {
25537                 return;
25538             }
25539             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25540                 return;
25541             }
25542
25543         }
25544         
25545         if (this.currentTip.el) {
25546             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25547         }    
25548         //Roo.log(ev);
25549         
25550         if(!el || el.dom == document){
25551             return;
25552         }
25553         
25554         var bindEl = el;
25555         
25556         // you can not look for children, as if el is the body.. then everythign is the child..
25557         if (!el.attr('tooltip')) { //
25558             if (!el.select("[tooltip]").elements.length) {
25559                 return;
25560             }
25561             // is the mouse over this child...?
25562             bindEl = el.select("[tooltip]").first();
25563             var xy = ev.getXY();
25564             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25565                 //Roo.log("not in region.");
25566                 return;
25567             }
25568             //Roo.log("child element over..");
25569             
25570         }
25571         this.currentEl = bindEl;
25572         this.currentTip.bind(bindEl);
25573         this.currentRegion = Roo.lib.Region.getRegion(dom);
25574         this.currentTip.enter();
25575         
25576     },
25577     leave : function(ev)
25578     {
25579         var dom = ev.getTarget();
25580         //Roo.log(['leave',dom]);
25581         if (!this.currentEl) {
25582             return;
25583         }
25584         
25585         
25586         if (dom != this.currentEl.dom) {
25587             return;
25588         }
25589         var xy = ev.getXY();
25590         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25591             return;
25592         }
25593         // only activate leave if mouse cursor is outside... bounding box..
25594         
25595         
25596         
25597         
25598         if (this.currentTip) {
25599             this.currentTip.leave();
25600         }
25601         //Roo.log('clear currentEl');
25602         this.currentEl = false;
25603         
25604         
25605     },
25606     alignment : {
25607         'left' : ['r-l', [-2,0], 'right'],
25608         'right' : ['l-r', [2,0], 'left'],
25609         'bottom' : ['t-b', [0,2], 'top'],
25610         'top' : [ 'b-t', [0,-2], 'bottom']
25611     }
25612     
25613 });
25614
25615
25616 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25617     
25618     
25619     bindEl : false,
25620     
25621     delay : null, // can be { show : 300 , hide: 500}
25622     
25623     timeout : null,
25624     
25625     hoverState : null, //???
25626     
25627     placement : 'bottom', 
25628     
25629     alignment : false,
25630     
25631     getAutoCreate : function(){
25632     
25633         var cfg = {
25634            cls : 'tooltip',
25635            role : 'tooltip',
25636            cn : [
25637                 {
25638                     cls : 'tooltip-arrow'
25639                 },
25640                 {
25641                     cls : 'tooltip-inner'
25642                 }
25643            ]
25644         };
25645         
25646         return cfg;
25647     },
25648     bind : function(el)
25649     {
25650         this.bindEl = el;
25651     },
25652       
25653     
25654     enter : function () {
25655        
25656         if (this.timeout != null) {
25657             clearTimeout(this.timeout);
25658         }
25659         
25660         this.hoverState = 'in';
25661          //Roo.log("enter - show");
25662         if (!this.delay || !this.delay.show) {
25663             this.show();
25664             return;
25665         }
25666         var _t = this;
25667         this.timeout = setTimeout(function () {
25668             if (_t.hoverState == 'in') {
25669                 _t.show();
25670             }
25671         }, this.delay.show);
25672     },
25673     leave : function()
25674     {
25675         clearTimeout(this.timeout);
25676     
25677         this.hoverState = 'out';
25678          if (!this.delay || !this.delay.hide) {
25679             this.hide();
25680             return;
25681         }
25682        
25683         var _t = this;
25684         this.timeout = setTimeout(function () {
25685             //Roo.log("leave - timeout");
25686             
25687             if (_t.hoverState == 'out') {
25688                 _t.hide();
25689                 Roo.bootstrap.Tooltip.currentEl = false;
25690             }
25691         }, delay);
25692     },
25693     
25694     show : function (msg)
25695     {
25696         if (!this.el) {
25697             this.render(document.body);
25698         }
25699         // set content.
25700         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25701         
25702         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25703         
25704         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25705         
25706         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25707         
25708         var placement = typeof this.placement == 'function' ?
25709             this.placement.call(this, this.el, on_el) :
25710             this.placement;
25711             
25712         var autoToken = /\s?auto?\s?/i;
25713         var autoPlace = autoToken.test(placement);
25714         if (autoPlace) {
25715             placement = placement.replace(autoToken, '') || 'top';
25716         }
25717         
25718         //this.el.detach()
25719         //this.el.setXY([0,0]);
25720         this.el.show();
25721         //this.el.dom.style.display='block';
25722         
25723         //this.el.appendTo(on_el);
25724         
25725         var p = this.getPosition();
25726         var box = this.el.getBox();
25727         
25728         if (autoPlace) {
25729             // fixme..
25730         }
25731         
25732         var align = this.alignment[placement];
25733         
25734         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25735         
25736         if(placement == 'top' || placement == 'bottom'){
25737             if(xy[0] < 0){
25738                 placement = 'right';
25739             }
25740             
25741             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25742                 placement = 'left';
25743             }
25744             
25745             var scroll = Roo.select('body', true).first().getScroll();
25746             
25747             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25748                 placement = 'top';
25749             }
25750             
25751         }
25752         
25753         this.el.alignTo(this.bindEl, align[0],align[1]);
25754         //var arrow = this.el.select('.arrow',true).first();
25755         //arrow.set(align[2], 
25756         
25757         this.el.addClass(placement);
25758         
25759         this.el.addClass('in fade');
25760         
25761         this.hoverState = null;
25762         
25763         if (this.el.hasClass('fade')) {
25764             // fade it?
25765         }
25766         
25767     },
25768     hide : function()
25769     {
25770          
25771         if (!this.el) {
25772             return;
25773         }
25774         //this.el.setXY([0,0]);
25775         this.el.removeClass('in');
25776         //this.el.hide();
25777         
25778     }
25779     
25780 });
25781  
25782
25783  /*
25784  * - LGPL
25785  *
25786  * Location Picker
25787  * 
25788  */
25789
25790 /**
25791  * @class Roo.bootstrap.LocationPicker
25792  * @extends Roo.bootstrap.Component
25793  * Bootstrap LocationPicker class
25794  * @cfg {Number} latitude Position when init default 0
25795  * @cfg {Number} longitude Position when init default 0
25796  * @cfg {Number} zoom default 15
25797  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25798  * @cfg {Boolean} mapTypeControl default false
25799  * @cfg {Boolean} disableDoubleClickZoom default false
25800  * @cfg {Boolean} scrollwheel default true
25801  * @cfg {Boolean} streetViewControl default false
25802  * @cfg {Number} radius default 0
25803  * @cfg {String} locationName
25804  * @cfg {Boolean} draggable default true
25805  * @cfg {Boolean} enableAutocomplete default false
25806  * @cfg {Boolean} enableReverseGeocode default true
25807  * @cfg {String} markerTitle
25808  * 
25809  * @constructor
25810  * Create a new LocationPicker
25811  * @param {Object} config The config object
25812  */
25813
25814
25815 Roo.bootstrap.LocationPicker = function(config){
25816     
25817     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25818     
25819     this.addEvents({
25820         /**
25821          * @event initial
25822          * Fires when the picker initialized.
25823          * @param {Roo.bootstrap.LocationPicker} this
25824          * @param {Google Location} location
25825          */
25826         initial : true,
25827         /**
25828          * @event positionchanged
25829          * Fires when the picker position changed.
25830          * @param {Roo.bootstrap.LocationPicker} this
25831          * @param {Google Location} location
25832          */
25833         positionchanged : true,
25834         /**
25835          * @event resize
25836          * Fires when the map resize.
25837          * @param {Roo.bootstrap.LocationPicker} this
25838          */
25839         resize : true,
25840         /**
25841          * @event show
25842          * Fires when the map show.
25843          * @param {Roo.bootstrap.LocationPicker} this
25844          */
25845         show : true,
25846         /**
25847          * @event hide
25848          * Fires when the map hide.
25849          * @param {Roo.bootstrap.LocationPicker} this
25850          */
25851         hide : true,
25852         /**
25853          * @event mapClick
25854          * Fires when click the map.
25855          * @param {Roo.bootstrap.LocationPicker} this
25856          * @param {Map event} e
25857          */
25858         mapClick : true,
25859         /**
25860          * @event mapRightClick
25861          * Fires when right click the map.
25862          * @param {Roo.bootstrap.LocationPicker} this
25863          * @param {Map event} e
25864          */
25865         mapRightClick : true,
25866         /**
25867          * @event markerClick
25868          * Fires when click the marker.
25869          * @param {Roo.bootstrap.LocationPicker} this
25870          * @param {Map event} e
25871          */
25872         markerClick : true,
25873         /**
25874          * @event markerRightClick
25875          * Fires when right click the marker.
25876          * @param {Roo.bootstrap.LocationPicker} this
25877          * @param {Map event} e
25878          */
25879         markerRightClick : true,
25880         /**
25881          * @event OverlayViewDraw
25882          * Fires when OverlayView Draw
25883          * @param {Roo.bootstrap.LocationPicker} this
25884          */
25885         OverlayViewDraw : true,
25886         /**
25887          * @event OverlayViewOnAdd
25888          * Fires when OverlayView Draw
25889          * @param {Roo.bootstrap.LocationPicker} this
25890          */
25891         OverlayViewOnAdd : true,
25892         /**
25893          * @event OverlayViewOnRemove
25894          * Fires when OverlayView Draw
25895          * @param {Roo.bootstrap.LocationPicker} this
25896          */
25897         OverlayViewOnRemove : true,
25898         /**
25899          * @event OverlayViewShow
25900          * Fires when OverlayView Draw
25901          * @param {Roo.bootstrap.LocationPicker} this
25902          * @param {Pixel} cpx
25903          */
25904         OverlayViewShow : true,
25905         /**
25906          * @event OverlayViewHide
25907          * Fires when OverlayView Draw
25908          * @param {Roo.bootstrap.LocationPicker} this
25909          */
25910         OverlayViewHide : true,
25911         /**
25912          * @event loadexception
25913          * Fires when load google lib failed.
25914          * @param {Roo.bootstrap.LocationPicker} this
25915          */
25916         loadexception : true
25917     });
25918         
25919 };
25920
25921 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25922     
25923     gMapContext: false,
25924     
25925     latitude: 0,
25926     longitude: 0,
25927     zoom: 15,
25928     mapTypeId: false,
25929     mapTypeControl: false,
25930     disableDoubleClickZoom: false,
25931     scrollwheel: true,
25932     streetViewControl: false,
25933     radius: 0,
25934     locationName: '',
25935     draggable: true,
25936     enableAutocomplete: false,
25937     enableReverseGeocode: true,
25938     markerTitle: '',
25939     
25940     getAutoCreate: function()
25941     {
25942
25943         var cfg = {
25944             tag: 'div',
25945             cls: 'roo-location-picker'
25946         };
25947         
25948         return cfg
25949     },
25950     
25951     initEvents: function(ct, position)
25952     {       
25953         if(!this.el.getWidth() || this.isApplied()){
25954             return;
25955         }
25956         
25957         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25958         
25959         this.initial();
25960     },
25961     
25962     initial: function()
25963     {
25964         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25965             this.fireEvent('loadexception', this);
25966             return;
25967         }
25968         
25969         if(!this.mapTypeId){
25970             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25971         }
25972         
25973         this.gMapContext = this.GMapContext();
25974         
25975         this.initOverlayView();
25976         
25977         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25978         
25979         var _this = this;
25980                 
25981         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25982             _this.setPosition(_this.gMapContext.marker.position);
25983         });
25984         
25985         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25986             _this.fireEvent('mapClick', this, event);
25987             
25988         });
25989
25990         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25991             _this.fireEvent('mapRightClick', this, event);
25992             
25993         });
25994         
25995         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25996             _this.fireEvent('markerClick', this, event);
25997             
25998         });
25999
26000         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26001             _this.fireEvent('markerRightClick', this, event);
26002             
26003         });
26004         
26005         this.setPosition(this.gMapContext.location);
26006         
26007         this.fireEvent('initial', this, this.gMapContext.location);
26008     },
26009     
26010     initOverlayView: function()
26011     {
26012         var _this = this;
26013         
26014         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26015             
26016             draw: function()
26017             {
26018                 _this.fireEvent('OverlayViewDraw', _this);
26019             },
26020             
26021             onAdd: function()
26022             {
26023                 _this.fireEvent('OverlayViewOnAdd', _this);
26024             },
26025             
26026             onRemove: function()
26027             {
26028                 _this.fireEvent('OverlayViewOnRemove', _this);
26029             },
26030             
26031             show: function(cpx)
26032             {
26033                 _this.fireEvent('OverlayViewShow', _this, cpx);
26034             },
26035             
26036             hide: function()
26037             {
26038                 _this.fireEvent('OverlayViewHide', _this);
26039             }
26040             
26041         });
26042     },
26043     
26044     fromLatLngToContainerPixel: function(event)
26045     {
26046         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26047     },
26048     
26049     isApplied: function() 
26050     {
26051         return this.getGmapContext() == false ? false : true;
26052     },
26053     
26054     getGmapContext: function() 
26055     {
26056         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26057     },
26058     
26059     GMapContext: function() 
26060     {
26061         var position = new google.maps.LatLng(this.latitude, this.longitude);
26062         
26063         var _map = new google.maps.Map(this.el.dom, {
26064             center: position,
26065             zoom: this.zoom,
26066             mapTypeId: this.mapTypeId,
26067             mapTypeControl: this.mapTypeControl,
26068             disableDoubleClickZoom: this.disableDoubleClickZoom,
26069             scrollwheel: this.scrollwheel,
26070             streetViewControl: this.streetViewControl,
26071             locationName: this.locationName,
26072             draggable: this.draggable,
26073             enableAutocomplete: this.enableAutocomplete,
26074             enableReverseGeocode: this.enableReverseGeocode
26075         });
26076         
26077         var _marker = new google.maps.Marker({
26078             position: position,
26079             map: _map,
26080             title: this.markerTitle,
26081             draggable: this.draggable
26082         });
26083         
26084         return {
26085             map: _map,
26086             marker: _marker,
26087             circle: null,
26088             location: position,
26089             radius: this.radius,
26090             locationName: this.locationName,
26091             addressComponents: {
26092                 formatted_address: null,
26093                 addressLine1: null,
26094                 addressLine2: null,
26095                 streetName: null,
26096                 streetNumber: null,
26097                 city: null,
26098                 district: null,
26099                 state: null,
26100                 stateOrProvince: null
26101             },
26102             settings: this,
26103             domContainer: this.el.dom,
26104             geodecoder: new google.maps.Geocoder()
26105         };
26106     },
26107     
26108     drawCircle: function(center, radius, options) 
26109     {
26110         if (this.gMapContext.circle != null) {
26111             this.gMapContext.circle.setMap(null);
26112         }
26113         if (radius > 0) {
26114             radius *= 1;
26115             options = Roo.apply({}, options, {
26116                 strokeColor: "#0000FF",
26117                 strokeOpacity: .35,
26118                 strokeWeight: 2,
26119                 fillColor: "#0000FF",
26120                 fillOpacity: .2
26121             });
26122             
26123             options.map = this.gMapContext.map;
26124             options.radius = radius;
26125             options.center = center;
26126             this.gMapContext.circle = new google.maps.Circle(options);
26127             return this.gMapContext.circle;
26128         }
26129         
26130         return null;
26131     },
26132     
26133     setPosition: function(location) 
26134     {
26135         this.gMapContext.location = location;
26136         this.gMapContext.marker.setPosition(location);
26137         this.gMapContext.map.panTo(location);
26138         this.drawCircle(location, this.gMapContext.radius, {});
26139         
26140         var _this = this;
26141         
26142         if (this.gMapContext.settings.enableReverseGeocode) {
26143             this.gMapContext.geodecoder.geocode({
26144                 latLng: this.gMapContext.location
26145             }, function(results, status) {
26146                 
26147                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26148                     _this.gMapContext.locationName = results[0].formatted_address;
26149                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26150                     
26151                     _this.fireEvent('positionchanged', this, location);
26152                 }
26153             });
26154             
26155             return;
26156         }
26157         
26158         this.fireEvent('positionchanged', this, location);
26159     },
26160     
26161     resize: function()
26162     {
26163         google.maps.event.trigger(this.gMapContext.map, "resize");
26164         
26165         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26166         
26167         this.fireEvent('resize', this);
26168     },
26169     
26170     setPositionByLatLng: function(latitude, longitude)
26171     {
26172         this.setPosition(new google.maps.LatLng(latitude, longitude));
26173     },
26174     
26175     getCurrentPosition: function() 
26176     {
26177         return {
26178             latitude: this.gMapContext.location.lat(),
26179             longitude: this.gMapContext.location.lng()
26180         };
26181     },
26182     
26183     getAddressName: function() 
26184     {
26185         return this.gMapContext.locationName;
26186     },
26187     
26188     getAddressComponents: function() 
26189     {
26190         return this.gMapContext.addressComponents;
26191     },
26192     
26193     address_component_from_google_geocode: function(address_components) 
26194     {
26195         var result = {};
26196         
26197         for (var i = 0; i < address_components.length; i++) {
26198             var component = address_components[i];
26199             if (component.types.indexOf("postal_code") >= 0) {
26200                 result.postalCode = component.short_name;
26201             } else if (component.types.indexOf("street_number") >= 0) {
26202                 result.streetNumber = component.short_name;
26203             } else if (component.types.indexOf("route") >= 0) {
26204                 result.streetName = component.short_name;
26205             } else if (component.types.indexOf("neighborhood") >= 0) {
26206                 result.city = component.short_name;
26207             } else if (component.types.indexOf("locality") >= 0) {
26208                 result.city = component.short_name;
26209             } else if (component.types.indexOf("sublocality") >= 0) {
26210                 result.district = component.short_name;
26211             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26212                 result.stateOrProvince = component.short_name;
26213             } else if (component.types.indexOf("country") >= 0) {
26214                 result.country = component.short_name;
26215             }
26216         }
26217         
26218         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26219         result.addressLine2 = "";
26220         return result;
26221     },
26222     
26223     setZoomLevel: function(zoom)
26224     {
26225         this.gMapContext.map.setZoom(zoom);
26226     },
26227     
26228     show: function()
26229     {
26230         if(!this.el){
26231             return;
26232         }
26233         
26234         this.el.show();
26235         
26236         this.resize();
26237         
26238         this.fireEvent('show', this);
26239     },
26240     
26241     hide: function()
26242     {
26243         if(!this.el){
26244             return;
26245         }
26246         
26247         this.el.hide();
26248         
26249         this.fireEvent('hide', this);
26250     }
26251     
26252 });
26253
26254 Roo.apply(Roo.bootstrap.LocationPicker, {
26255     
26256     OverlayView : function(map, options)
26257     {
26258         options = options || {};
26259         
26260         this.setMap(map);
26261     }
26262     
26263     
26264 });/*
26265  * - LGPL
26266  *
26267  * Alert
26268  * 
26269  */
26270
26271 /**
26272  * @class Roo.bootstrap.Alert
26273  * @extends Roo.bootstrap.Component
26274  * Bootstrap Alert class
26275  * @cfg {String} title The title of alert
26276  * @cfg {String} html The content of alert
26277  * @cfg {String} weight (  success | info | warning | danger )
26278  * @cfg {String} faicon font-awesomeicon
26279  * 
26280  * @constructor
26281  * Create a new alert
26282  * @param {Object} config The config object
26283  */
26284
26285
26286 Roo.bootstrap.Alert = function(config){
26287     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26288     
26289 };
26290
26291 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26292     
26293     title: '',
26294     html: '',
26295     weight: false,
26296     faicon: false,
26297     
26298     getAutoCreate : function()
26299     {
26300         
26301         var cfg = {
26302             tag : 'div',
26303             cls : 'alert',
26304             cn : [
26305                 {
26306                     tag : 'i',
26307                     cls : 'roo-alert-icon'
26308                     
26309                 },
26310                 {
26311                     tag : 'b',
26312                     cls : 'roo-alert-title',
26313                     html : this.title
26314                 },
26315                 {
26316                     tag : 'span',
26317                     cls : 'roo-alert-text',
26318                     html : this.html
26319                 }
26320             ]
26321         };
26322         
26323         if(this.faicon){
26324             cfg.cn[0].cls += ' fa ' + this.faicon;
26325         }
26326         
26327         if(this.weight){
26328             cfg.cls += ' alert-' + this.weight;
26329         }
26330         
26331         return cfg;
26332     },
26333     
26334     initEvents: function() 
26335     {
26336         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26337     },
26338     
26339     setTitle : function(str)
26340     {
26341         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26342     },
26343     
26344     setText : function(str)
26345     {
26346         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26347     },
26348     
26349     setWeight : function(weight)
26350     {
26351         if(this.weight){
26352             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26353         }
26354         
26355         this.weight = weight;
26356         
26357         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26358     },
26359     
26360     setIcon : function(icon)
26361     {
26362         if(this.faicon){
26363             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26364         }
26365         
26366         this.faicon = icon;
26367         
26368         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26369     },
26370     
26371     hide: function() 
26372     {
26373         this.el.hide();   
26374     },
26375     
26376     show: function() 
26377     {  
26378         this.el.show();   
26379     }
26380     
26381 });
26382
26383  
26384 /*
26385 * Licence: LGPL
26386 */
26387
26388 /**
26389  * @class Roo.bootstrap.UploadCropbox
26390  * @extends Roo.bootstrap.Component
26391  * Bootstrap UploadCropbox class
26392  * @cfg {String} emptyText show when image has been loaded
26393  * @cfg {String} rotateNotify show when image too small to rotate
26394  * @cfg {Number} errorTimeout default 3000
26395  * @cfg {Number} minWidth default 300
26396  * @cfg {Number} minHeight default 300
26397  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26398  * @cfg {Boolean} isDocument (true|false) default false
26399  * @cfg {String} url action url
26400  * @cfg {String} paramName default 'imageUpload'
26401  * @cfg {String} method default POST
26402  * @cfg {Boolean} loadMask (true|false) default true
26403  * @cfg {Boolean} loadingText default 'Loading...'
26404  * 
26405  * @constructor
26406  * Create a new UploadCropbox
26407  * @param {Object} config The config object
26408  */
26409
26410 Roo.bootstrap.UploadCropbox = function(config){
26411     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26412     
26413     this.addEvents({
26414         /**
26415          * @event beforeselectfile
26416          * Fire before select file
26417          * @param {Roo.bootstrap.UploadCropbox} this
26418          */
26419         "beforeselectfile" : true,
26420         /**
26421          * @event initial
26422          * Fire after initEvent
26423          * @param {Roo.bootstrap.UploadCropbox} this
26424          */
26425         "initial" : true,
26426         /**
26427          * @event crop
26428          * Fire after initEvent
26429          * @param {Roo.bootstrap.UploadCropbox} this
26430          * @param {String} data
26431          */
26432         "crop" : true,
26433         /**
26434          * @event prepare
26435          * Fire when preparing the file data
26436          * @param {Roo.bootstrap.UploadCropbox} this
26437          * @param {Object} file
26438          */
26439         "prepare" : true,
26440         /**
26441          * @event exception
26442          * Fire when get exception
26443          * @param {Roo.bootstrap.UploadCropbox} this
26444          * @param {XMLHttpRequest} xhr
26445          */
26446         "exception" : true,
26447         /**
26448          * @event beforeloadcanvas
26449          * Fire before load the canvas
26450          * @param {Roo.bootstrap.UploadCropbox} this
26451          * @param {String} src
26452          */
26453         "beforeloadcanvas" : true,
26454         /**
26455          * @event trash
26456          * Fire when trash image
26457          * @param {Roo.bootstrap.UploadCropbox} this
26458          */
26459         "trash" : true,
26460         /**
26461          * @event download
26462          * Fire when download the image
26463          * @param {Roo.bootstrap.UploadCropbox} this
26464          */
26465         "download" : true,
26466         /**
26467          * @event footerbuttonclick
26468          * Fire when footerbuttonclick
26469          * @param {Roo.bootstrap.UploadCropbox} this
26470          * @param {String} type
26471          */
26472         "footerbuttonclick" : true,
26473         /**
26474          * @event resize
26475          * Fire when resize
26476          * @param {Roo.bootstrap.UploadCropbox} this
26477          */
26478         "resize" : true,
26479         /**
26480          * @event rotate
26481          * Fire when rotate the image
26482          * @param {Roo.bootstrap.UploadCropbox} this
26483          * @param {String} pos
26484          */
26485         "rotate" : true,
26486         /**
26487          * @event inspect
26488          * Fire when inspect the file
26489          * @param {Roo.bootstrap.UploadCropbox} this
26490          * @param {Object} file
26491          */
26492         "inspect" : true,
26493         /**
26494          * @event upload
26495          * Fire when xhr upload the file
26496          * @param {Roo.bootstrap.UploadCropbox} this
26497          * @param {Object} data
26498          */
26499         "upload" : true,
26500         /**
26501          * @event arrange
26502          * Fire when arrange the file data
26503          * @param {Roo.bootstrap.UploadCropbox} this
26504          * @param {Object} formData
26505          */
26506         "arrange" : true
26507     });
26508     
26509     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26510 };
26511
26512 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26513     
26514     emptyText : 'Click to upload image',
26515     rotateNotify : 'Image is too small to rotate',
26516     errorTimeout : 3000,
26517     scale : 0,
26518     baseScale : 1,
26519     rotate : 0,
26520     dragable : false,
26521     pinching : false,
26522     mouseX : 0,
26523     mouseY : 0,
26524     cropData : false,
26525     minWidth : 300,
26526     minHeight : 300,
26527     file : false,
26528     exif : {},
26529     baseRotate : 1,
26530     cropType : 'image/jpeg',
26531     buttons : false,
26532     canvasLoaded : false,
26533     isDocument : false,
26534     method : 'POST',
26535     paramName : 'imageUpload',
26536     loadMask : true,
26537     loadingText : 'Loading...',
26538     maskEl : false,
26539     
26540     getAutoCreate : function()
26541     {
26542         var cfg = {
26543             tag : 'div',
26544             cls : 'roo-upload-cropbox',
26545             cn : [
26546                 {
26547                     tag : 'input',
26548                     cls : 'roo-upload-cropbox-selector',
26549                     type : 'file'
26550                 },
26551                 {
26552                     tag : 'div',
26553                     cls : 'roo-upload-cropbox-body',
26554                     style : 'cursor:pointer',
26555                     cn : [
26556                         {
26557                             tag : 'div',
26558                             cls : 'roo-upload-cropbox-preview'
26559                         },
26560                         {
26561                             tag : 'div',
26562                             cls : 'roo-upload-cropbox-thumb'
26563                         },
26564                         {
26565                             tag : 'div',
26566                             cls : 'roo-upload-cropbox-empty-notify',
26567                             html : this.emptyText
26568                         },
26569                         {
26570                             tag : 'div',
26571                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26572                             html : this.rotateNotify
26573                         }
26574                     ]
26575                 },
26576                 {
26577                     tag : 'div',
26578                     cls : 'roo-upload-cropbox-footer',
26579                     cn : {
26580                         tag : 'div',
26581                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26582                         cn : []
26583                     }
26584                 }
26585             ]
26586         };
26587         
26588         return cfg;
26589     },
26590     
26591     onRender : function(ct, position)
26592     {
26593         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26594         
26595         if (this.buttons.length) {
26596             
26597             Roo.each(this.buttons, function(bb) {
26598                 
26599                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26600                 
26601                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26602                 
26603             }, this);
26604         }
26605         
26606         if(this.loadMask){
26607             this.maskEl = this.el;
26608         }
26609     },
26610     
26611     initEvents : function()
26612     {
26613         this.urlAPI = (window.createObjectURL && window) || 
26614                                 (window.URL && URL.revokeObjectURL && URL) || 
26615                                 (window.webkitURL && webkitURL);
26616                         
26617         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26618         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26619         
26620         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26621         this.selectorEl.hide();
26622         
26623         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26624         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26625         
26626         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26627         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26628         this.thumbEl.hide();
26629         
26630         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26631         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26632         
26633         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26634         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26635         this.errorEl.hide();
26636         
26637         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26638         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26639         this.footerEl.hide();
26640         
26641         this.setThumbBoxSize();
26642         
26643         this.bind();
26644         
26645         this.resize();
26646         
26647         this.fireEvent('initial', this);
26648     },
26649
26650     bind : function()
26651     {
26652         var _this = this;
26653         
26654         window.addEventListener("resize", function() { _this.resize(); } );
26655         
26656         this.bodyEl.on('click', this.beforeSelectFile, this);
26657         
26658         if(Roo.isTouch){
26659             this.bodyEl.on('touchstart', this.onTouchStart, this);
26660             this.bodyEl.on('touchmove', this.onTouchMove, this);
26661             this.bodyEl.on('touchend', this.onTouchEnd, this);
26662         }
26663         
26664         if(!Roo.isTouch){
26665             this.bodyEl.on('mousedown', this.onMouseDown, this);
26666             this.bodyEl.on('mousemove', this.onMouseMove, this);
26667             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26668             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26669             Roo.get(document).on('mouseup', this.onMouseUp, this);
26670         }
26671         
26672         this.selectorEl.on('change', this.onFileSelected, this);
26673     },
26674     
26675     reset : function()
26676     {    
26677         this.scale = 0;
26678         this.baseScale = 1;
26679         this.rotate = 0;
26680         this.baseRotate = 1;
26681         this.dragable = false;
26682         this.pinching = false;
26683         this.mouseX = 0;
26684         this.mouseY = 0;
26685         this.cropData = false;
26686         this.notifyEl.dom.innerHTML = this.emptyText;
26687         
26688         this.selectorEl.dom.value = '';
26689         
26690     },
26691     
26692     resize : function()
26693     {
26694         if(this.fireEvent('resize', this) != false){
26695             this.setThumbBoxPosition();
26696             this.setCanvasPosition();
26697         }
26698     },
26699     
26700     onFooterButtonClick : function(e, el, o, type)
26701     {
26702         switch (type) {
26703             case 'rotate-left' :
26704                 this.onRotateLeft(e);
26705                 break;
26706             case 'rotate-right' :
26707                 this.onRotateRight(e);
26708                 break;
26709             case 'picture' :
26710                 this.beforeSelectFile(e);
26711                 break;
26712             case 'trash' :
26713                 this.trash(e);
26714                 break;
26715             case 'crop' :
26716                 this.crop(e);
26717                 break;
26718             case 'download' :
26719                 this.download(e);
26720                 break;
26721             default :
26722                 break;
26723         }
26724         
26725         this.fireEvent('footerbuttonclick', this, type);
26726     },
26727     
26728     beforeSelectFile : function(e)
26729     {
26730         e.preventDefault();
26731         
26732         if(this.fireEvent('beforeselectfile', this) != false){
26733             this.selectorEl.dom.click();
26734         }
26735     },
26736     
26737     onFileSelected : function(e)
26738     {
26739         e.preventDefault();
26740         
26741         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26742             return;
26743         }
26744         
26745         var file = this.selectorEl.dom.files[0];
26746         
26747         if(this.fireEvent('inspect', this, file) != false){
26748             this.prepare(file);
26749         }
26750         
26751     },
26752     
26753     trash : function(e)
26754     {
26755         this.fireEvent('trash', this);
26756     },
26757     
26758     download : function(e)
26759     {
26760         this.fireEvent('download', this);
26761     },
26762     
26763     loadCanvas : function(src)
26764     {   
26765         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26766             
26767             this.reset();
26768             
26769             this.imageEl = document.createElement('img');
26770             
26771             var _this = this;
26772             
26773             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26774             
26775             this.imageEl.src = src;
26776         }
26777     },
26778     
26779     onLoadCanvas : function()
26780     {   
26781         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26782         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26783         
26784         this.bodyEl.un('click', this.beforeSelectFile, this);
26785         
26786         this.notifyEl.hide();
26787         this.thumbEl.show();
26788         this.footerEl.show();
26789         
26790         this.baseRotateLevel();
26791         
26792         if(this.isDocument){
26793             this.setThumbBoxSize();
26794         }
26795         
26796         this.setThumbBoxPosition();
26797         
26798         this.baseScaleLevel();
26799         
26800         this.draw();
26801         
26802         this.resize();
26803         
26804         this.canvasLoaded = true;
26805         
26806         if(this.loadMask){
26807             this.maskEl.unmask();
26808         }
26809         
26810     },
26811     
26812     setCanvasPosition : function()
26813     {   
26814         if(!this.canvasEl){
26815             return;
26816         }
26817         
26818         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26819         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26820         
26821         this.previewEl.setLeft(pw);
26822         this.previewEl.setTop(ph);
26823         
26824     },
26825     
26826     onMouseDown : function(e)
26827     {   
26828         e.stopEvent();
26829         
26830         this.dragable = true;
26831         this.pinching = false;
26832         
26833         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26834             this.dragable = false;
26835             return;
26836         }
26837         
26838         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26839         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26840         
26841     },
26842     
26843     onMouseMove : function(e)
26844     {   
26845         e.stopEvent();
26846         
26847         if(!this.canvasLoaded){
26848             return;
26849         }
26850         
26851         if (!this.dragable){
26852             return;
26853         }
26854         
26855         var minX = Math.ceil(this.thumbEl.getLeft(true));
26856         var minY = Math.ceil(this.thumbEl.getTop(true));
26857         
26858         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26859         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26860         
26861         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26862         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26863         
26864         x = x - this.mouseX;
26865         y = y - this.mouseY;
26866         
26867         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26868         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26869         
26870         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26871         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26872         
26873         this.previewEl.setLeft(bgX);
26874         this.previewEl.setTop(bgY);
26875         
26876         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26877         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26878     },
26879     
26880     onMouseUp : function(e)
26881     {   
26882         e.stopEvent();
26883         
26884         this.dragable = false;
26885     },
26886     
26887     onMouseWheel : function(e)
26888     {   
26889         e.stopEvent();
26890         
26891         this.startScale = this.scale;
26892         
26893         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26894         
26895         if(!this.zoomable()){
26896             this.scale = this.startScale;
26897             return;
26898         }
26899         
26900         this.draw();
26901         
26902         return;
26903     },
26904     
26905     zoomable : function()
26906     {
26907         var minScale = this.thumbEl.getWidth() / this.minWidth;
26908         
26909         if(this.minWidth < this.minHeight){
26910             minScale = this.thumbEl.getHeight() / this.minHeight;
26911         }
26912         
26913         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26914         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26915         
26916         if(
26917                 this.isDocument &&
26918                 (this.rotate == 0 || this.rotate == 180) && 
26919                 (
26920                     width > this.imageEl.OriginWidth || 
26921                     height > this.imageEl.OriginHeight ||
26922                     (width < this.minWidth && height < this.minHeight)
26923                 )
26924         ){
26925             return false;
26926         }
26927         
26928         if(
26929                 this.isDocument &&
26930                 (this.rotate == 90 || this.rotate == 270) && 
26931                 (
26932                     width > this.imageEl.OriginWidth || 
26933                     height > this.imageEl.OriginHeight ||
26934                     (width < this.minHeight && height < this.minWidth)
26935                 )
26936         ){
26937             return false;
26938         }
26939         
26940         if(
26941                 !this.isDocument &&
26942                 (this.rotate == 0 || this.rotate == 180) && 
26943                 (
26944                     width < this.minWidth || 
26945                     width > this.imageEl.OriginWidth || 
26946                     height < this.minHeight || 
26947                     height > this.imageEl.OriginHeight
26948                 )
26949         ){
26950             return false;
26951         }
26952         
26953         if(
26954                 !this.isDocument &&
26955                 (this.rotate == 90 || this.rotate == 270) && 
26956                 (
26957                     width < this.minHeight || 
26958                     width > this.imageEl.OriginWidth || 
26959                     height < this.minWidth || 
26960                     height > this.imageEl.OriginHeight
26961                 )
26962         ){
26963             return false;
26964         }
26965         
26966         return true;
26967         
26968     },
26969     
26970     onRotateLeft : function(e)
26971     {   
26972         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26973             
26974             var minScale = this.thumbEl.getWidth() / this.minWidth;
26975             
26976             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26977             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26978             
26979             this.startScale = this.scale;
26980             
26981             while (this.getScaleLevel() < minScale){
26982             
26983                 this.scale = this.scale + 1;
26984                 
26985                 if(!this.zoomable()){
26986                     break;
26987                 }
26988                 
26989                 if(
26990                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26991                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26992                 ){
26993                     continue;
26994                 }
26995                 
26996                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26997
26998                 this.draw();
26999                 
27000                 return;
27001             }
27002             
27003             this.scale = this.startScale;
27004             
27005             this.onRotateFail();
27006             
27007             return false;
27008         }
27009         
27010         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27011
27012         if(this.isDocument){
27013             this.setThumbBoxSize();
27014             this.setThumbBoxPosition();
27015             this.setCanvasPosition();
27016         }
27017         
27018         this.draw();
27019         
27020         this.fireEvent('rotate', this, 'left');
27021         
27022     },
27023     
27024     onRotateRight : function(e)
27025     {
27026         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27027             
27028             var minScale = this.thumbEl.getWidth() / this.minWidth;
27029         
27030             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27031             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27032             
27033             this.startScale = this.scale;
27034             
27035             while (this.getScaleLevel() < minScale){
27036             
27037                 this.scale = this.scale + 1;
27038                 
27039                 if(!this.zoomable()){
27040                     break;
27041                 }
27042                 
27043                 if(
27044                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27045                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27046                 ){
27047                     continue;
27048                 }
27049                 
27050                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27051
27052                 this.draw();
27053                 
27054                 return;
27055             }
27056             
27057             this.scale = this.startScale;
27058             
27059             this.onRotateFail();
27060             
27061             return false;
27062         }
27063         
27064         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27065
27066         if(this.isDocument){
27067             this.setThumbBoxSize();
27068             this.setThumbBoxPosition();
27069             this.setCanvasPosition();
27070         }
27071         
27072         this.draw();
27073         
27074         this.fireEvent('rotate', this, 'right');
27075     },
27076     
27077     onRotateFail : function()
27078     {
27079         this.errorEl.show(true);
27080         
27081         var _this = this;
27082         
27083         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27084     },
27085     
27086     draw : function()
27087     {
27088         this.previewEl.dom.innerHTML = '';
27089         
27090         var canvasEl = document.createElement("canvas");
27091         
27092         var contextEl = canvasEl.getContext("2d");
27093         
27094         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27095         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27096         var center = this.imageEl.OriginWidth / 2;
27097         
27098         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27099             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27100             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27101             center = this.imageEl.OriginHeight / 2;
27102         }
27103         
27104         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27105         
27106         contextEl.translate(center, center);
27107         contextEl.rotate(this.rotate * Math.PI / 180);
27108
27109         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27110         
27111         this.canvasEl = document.createElement("canvas");
27112         
27113         this.contextEl = this.canvasEl.getContext("2d");
27114         
27115         switch (this.rotate) {
27116             case 0 :
27117                 
27118                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27119                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27120                 
27121                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27122                 
27123                 break;
27124             case 90 : 
27125                 
27126                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27127                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27128                 
27129                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27130                     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);
27131                     break;
27132                 }
27133                 
27134                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27135                 
27136                 break;
27137             case 180 :
27138                 
27139                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27140                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27141                 
27142                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27143                     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);
27144                     break;
27145                 }
27146                 
27147                 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);
27148                 
27149                 break;
27150             case 270 :
27151                 
27152                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27153                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27154         
27155                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27156                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27157                     break;
27158                 }
27159                 
27160                 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);
27161                 
27162                 break;
27163             default : 
27164                 break;
27165         }
27166         
27167         this.previewEl.appendChild(this.canvasEl);
27168         
27169         this.setCanvasPosition();
27170     },
27171     
27172     crop : function()
27173     {
27174         if(!this.canvasLoaded){
27175             return;
27176         }
27177         
27178         var imageCanvas = document.createElement("canvas");
27179         
27180         var imageContext = imageCanvas.getContext("2d");
27181         
27182         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27183         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27184         
27185         var center = imageCanvas.width / 2;
27186         
27187         imageContext.translate(center, center);
27188         
27189         imageContext.rotate(this.rotate * Math.PI / 180);
27190         
27191         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27192         
27193         var canvas = document.createElement("canvas");
27194         
27195         var context = canvas.getContext("2d");
27196                 
27197         canvas.width = this.minWidth;
27198         canvas.height = this.minHeight;
27199
27200         switch (this.rotate) {
27201             case 0 :
27202                 
27203                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27204                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27205                 
27206                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27207                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27208                 
27209                 var targetWidth = this.minWidth - 2 * x;
27210                 var targetHeight = this.minHeight - 2 * y;
27211                 
27212                 var scale = 1;
27213                 
27214                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27215                     scale = targetWidth / width;
27216                 }
27217                 
27218                 if(x > 0 && y == 0){
27219                     scale = targetHeight / height;
27220                 }
27221                 
27222                 if(x > 0 && y > 0){
27223                     scale = targetWidth / width;
27224                     
27225                     if(width < height){
27226                         scale = targetHeight / height;
27227                     }
27228                 }
27229                 
27230                 context.scale(scale, scale);
27231                 
27232                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27233                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27234
27235                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27236                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27237
27238                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27239                 
27240                 break;
27241             case 90 : 
27242                 
27243                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27244                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27245                 
27246                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27247                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27248                 
27249                 var targetWidth = this.minWidth - 2 * x;
27250                 var targetHeight = this.minHeight - 2 * y;
27251                 
27252                 var scale = 1;
27253                 
27254                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27255                     scale = targetWidth / width;
27256                 }
27257                 
27258                 if(x > 0 && y == 0){
27259                     scale = targetHeight / height;
27260                 }
27261                 
27262                 if(x > 0 && y > 0){
27263                     scale = targetWidth / width;
27264                     
27265                     if(width < height){
27266                         scale = targetHeight / height;
27267                     }
27268                 }
27269                 
27270                 context.scale(scale, scale);
27271                 
27272                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27273                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27274
27275                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27276                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27277                 
27278                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27279                 
27280                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27281                 
27282                 break;
27283             case 180 :
27284                 
27285                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27286                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27287                 
27288                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27289                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27290                 
27291                 var targetWidth = this.minWidth - 2 * x;
27292                 var targetHeight = this.minHeight - 2 * y;
27293                 
27294                 var scale = 1;
27295                 
27296                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27297                     scale = targetWidth / width;
27298                 }
27299                 
27300                 if(x > 0 && y == 0){
27301                     scale = targetHeight / height;
27302                 }
27303                 
27304                 if(x > 0 && y > 0){
27305                     scale = targetWidth / width;
27306                     
27307                     if(width < height){
27308                         scale = targetHeight / height;
27309                     }
27310                 }
27311                 
27312                 context.scale(scale, scale);
27313                 
27314                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27315                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27316
27317                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27318                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27319
27320                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27321                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27322                 
27323                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27324                 
27325                 break;
27326             case 270 :
27327                 
27328                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27329                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27330                 
27331                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27332                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27333                 
27334                 var targetWidth = this.minWidth - 2 * x;
27335                 var targetHeight = this.minHeight - 2 * y;
27336                 
27337                 var scale = 1;
27338                 
27339                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27340                     scale = targetWidth / width;
27341                 }
27342                 
27343                 if(x > 0 && y == 0){
27344                     scale = targetHeight / height;
27345                 }
27346                 
27347                 if(x > 0 && y > 0){
27348                     scale = targetWidth / width;
27349                     
27350                     if(width < height){
27351                         scale = targetHeight / height;
27352                     }
27353                 }
27354                 
27355                 context.scale(scale, scale);
27356                 
27357                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27358                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27359
27360                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27361                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27362                 
27363                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27364                 
27365                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27366                 
27367                 break;
27368             default : 
27369                 break;
27370         }
27371         
27372         this.cropData = canvas.toDataURL(this.cropType);
27373         
27374         if(this.fireEvent('crop', this, this.cropData) !== false){
27375             this.process(this.file, this.cropData);
27376         }
27377         
27378         return;
27379         
27380     },
27381     
27382     setThumbBoxSize : function()
27383     {
27384         var width, height;
27385         
27386         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27387             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27388             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27389             
27390             this.minWidth = width;
27391             this.minHeight = height;
27392             
27393             if(this.rotate == 90 || this.rotate == 270){
27394                 this.minWidth = height;
27395                 this.minHeight = width;
27396             }
27397         }
27398         
27399         height = 300;
27400         width = Math.ceil(this.minWidth * height / this.minHeight);
27401         
27402         if(this.minWidth > this.minHeight){
27403             width = 300;
27404             height = Math.ceil(this.minHeight * width / this.minWidth);
27405         }
27406         
27407         this.thumbEl.setStyle({
27408             width : width + 'px',
27409             height : height + 'px'
27410         });
27411
27412         return;
27413             
27414     },
27415     
27416     setThumbBoxPosition : function()
27417     {
27418         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27419         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27420         
27421         this.thumbEl.setLeft(x);
27422         this.thumbEl.setTop(y);
27423         
27424     },
27425     
27426     baseRotateLevel : function()
27427     {
27428         this.baseRotate = 1;
27429         
27430         if(
27431                 typeof(this.exif) != 'undefined' &&
27432                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27433                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27434         ){
27435             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27436         }
27437         
27438         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27439         
27440     },
27441     
27442     baseScaleLevel : function()
27443     {
27444         var width, height;
27445         
27446         if(this.isDocument){
27447             
27448             if(this.baseRotate == 6 || this.baseRotate == 8){
27449             
27450                 height = this.thumbEl.getHeight();
27451                 this.baseScale = height / this.imageEl.OriginWidth;
27452
27453                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27454                     width = this.thumbEl.getWidth();
27455                     this.baseScale = width / this.imageEl.OriginHeight;
27456                 }
27457
27458                 return;
27459             }
27460
27461             height = this.thumbEl.getHeight();
27462             this.baseScale = height / this.imageEl.OriginHeight;
27463
27464             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27465                 width = this.thumbEl.getWidth();
27466                 this.baseScale = width / this.imageEl.OriginWidth;
27467             }
27468
27469             return;
27470         }
27471         
27472         if(this.baseRotate == 6 || this.baseRotate == 8){
27473             
27474             width = this.thumbEl.getHeight();
27475             this.baseScale = width / this.imageEl.OriginHeight;
27476             
27477             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27478                 height = this.thumbEl.getWidth();
27479                 this.baseScale = height / this.imageEl.OriginHeight;
27480             }
27481             
27482             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27483                 height = this.thumbEl.getWidth();
27484                 this.baseScale = height / this.imageEl.OriginHeight;
27485                 
27486                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27487                     width = this.thumbEl.getHeight();
27488                     this.baseScale = width / this.imageEl.OriginWidth;
27489                 }
27490             }
27491             
27492             return;
27493         }
27494         
27495         width = this.thumbEl.getWidth();
27496         this.baseScale = width / this.imageEl.OriginWidth;
27497         
27498         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27499             height = this.thumbEl.getHeight();
27500             this.baseScale = height / this.imageEl.OriginHeight;
27501         }
27502         
27503         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27504             
27505             height = this.thumbEl.getHeight();
27506             this.baseScale = height / this.imageEl.OriginHeight;
27507             
27508             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27509                 width = this.thumbEl.getWidth();
27510                 this.baseScale = width / this.imageEl.OriginWidth;
27511             }
27512             
27513         }
27514         
27515         return;
27516     },
27517     
27518     getScaleLevel : function()
27519     {
27520         return this.baseScale * Math.pow(1.1, this.scale);
27521     },
27522     
27523     onTouchStart : function(e)
27524     {
27525         if(!this.canvasLoaded){
27526             this.beforeSelectFile(e);
27527             return;
27528         }
27529         
27530         var touches = e.browserEvent.touches;
27531         
27532         if(!touches){
27533             return;
27534         }
27535         
27536         if(touches.length == 1){
27537             this.onMouseDown(e);
27538             return;
27539         }
27540         
27541         if(touches.length != 2){
27542             return;
27543         }
27544         
27545         var coords = [];
27546         
27547         for(var i = 0, finger; finger = touches[i]; i++){
27548             coords.push(finger.pageX, finger.pageY);
27549         }
27550         
27551         var x = Math.pow(coords[0] - coords[2], 2);
27552         var y = Math.pow(coords[1] - coords[3], 2);
27553         
27554         this.startDistance = Math.sqrt(x + y);
27555         
27556         this.startScale = this.scale;
27557         
27558         this.pinching = true;
27559         this.dragable = false;
27560         
27561     },
27562     
27563     onTouchMove : function(e)
27564     {
27565         if(!this.pinching && !this.dragable){
27566             return;
27567         }
27568         
27569         var touches = e.browserEvent.touches;
27570         
27571         if(!touches){
27572             return;
27573         }
27574         
27575         if(this.dragable){
27576             this.onMouseMove(e);
27577             return;
27578         }
27579         
27580         var coords = [];
27581         
27582         for(var i = 0, finger; finger = touches[i]; i++){
27583             coords.push(finger.pageX, finger.pageY);
27584         }
27585         
27586         var x = Math.pow(coords[0] - coords[2], 2);
27587         var y = Math.pow(coords[1] - coords[3], 2);
27588         
27589         this.endDistance = Math.sqrt(x + y);
27590         
27591         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27592         
27593         if(!this.zoomable()){
27594             this.scale = this.startScale;
27595             return;
27596         }
27597         
27598         this.draw();
27599         
27600     },
27601     
27602     onTouchEnd : function(e)
27603     {
27604         this.pinching = false;
27605         this.dragable = false;
27606         
27607     },
27608     
27609     process : function(file, crop)
27610     {
27611         if(this.loadMask){
27612             this.maskEl.mask(this.loadingText);
27613         }
27614         
27615         this.xhr = new XMLHttpRequest();
27616         
27617         file.xhr = this.xhr;
27618
27619         this.xhr.open(this.method, this.url, true);
27620         
27621         var headers = {
27622             "Accept": "application/json",
27623             "Cache-Control": "no-cache",
27624             "X-Requested-With": "XMLHttpRequest"
27625         };
27626         
27627         for (var headerName in headers) {
27628             var headerValue = headers[headerName];
27629             if (headerValue) {
27630                 this.xhr.setRequestHeader(headerName, headerValue);
27631             }
27632         }
27633         
27634         var _this = this;
27635         
27636         this.xhr.onload = function()
27637         {
27638             _this.xhrOnLoad(_this.xhr);
27639         }
27640         
27641         this.xhr.onerror = function()
27642         {
27643             _this.xhrOnError(_this.xhr);
27644         }
27645         
27646         var formData = new FormData();
27647
27648         formData.append('returnHTML', 'NO');
27649         
27650         if(crop){
27651             formData.append('crop', crop);
27652         }
27653         
27654         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27655             formData.append(this.paramName, file, file.name);
27656         }
27657         
27658         if(typeof(file.filename) != 'undefined'){
27659             formData.append('filename', file.filename);
27660         }
27661         
27662         if(typeof(file.mimetype) != 'undefined'){
27663             formData.append('mimetype', file.mimetype);
27664         }
27665         
27666         if(this.fireEvent('arrange', this, formData) != false){
27667             this.xhr.send(formData);
27668         };
27669     },
27670     
27671     xhrOnLoad : function(xhr)
27672     {
27673         if(this.loadMask){
27674             this.maskEl.unmask();
27675         }
27676         
27677         if (xhr.readyState !== 4) {
27678             this.fireEvent('exception', this, xhr);
27679             return;
27680         }
27681
27682         var response = Roo.decode(xhr.responseText);
27683         
27684         if(!response.success){
27685             this.fireEvent('exception', this, xhr);
27686             return;
27687         }
27688         
27689         var response = Roo.decode(xhr.responseText);
27690         
27691         this.fireEvent('upload', this, response);
27692         
27693     },
27694     
27695     xhrOnError : function()
27696     {
27697         if(this.loadMask){
27698             this.maskEl.unmask();
27699         }
27700         
27701         Roo.log('xhr on error');
27702         
27703         var response = Roo.decode(xhr.responseText);
27704           
27705         Roo.log(response);
27706         
27707     },
27708     
27709     prepare : function(file)
27710     {   
27711         if(this.loadMask){
27712             this.maskEl.mask(this.loadingText);
27713         }
27714         
27715         this.file = false;
27716         this.exif = {};
27717         
27718         if(typeof(file) === 'string'){
27719             this.loadCanvas(file);
27720             return;
27721         }
27722         
27723         if(!file || !this.urlAPI){
27724             return;
27725         }
27726         
27727         this.file = file;
27728         this.cropType = file.type;
27729         
27730         var _this = this;
27731         
27732         if(this.fireEvent('prepare', this, this.file) != false){
27733             
27734             var reader = new FileReader();
27735             
27736             reader.onload = function (e) {
27737                 if (e.target.error) {
27738                     Roo.log(e.target.error);
27739                     return;
27740                 }
27741                 
27742                 var buffer = e.target.result,
27743                     dataView = new DataView(buffer),
27744                     offset = 2,
27745                     maxOffset = dataView.byteLength - 4,
27746                     markerBytes,
27747                     markerLength;
27748                 
27749                 if (dataView.getUint16(0) === 0xffd8) {
27750                     while (offset < maxOffset) {
27751                         markerBytes = dataView.getUint16(offset);
27752                         
27753                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27754                             markerLength = dataView.getUint16(offset + 2) + 2;
27755                             if (offset + markerLength > dataView.byteLength) {
27756                                 Roo.log('Invalid meta data: Invalid segment size.');
27757                                 break;
27758                             }
27759                             
27760                             if(markerBytes == 0xffe1){
27761                                 _this.parseExifData(
27762                                     dataView,
27763                                     offset,
27764                                     markerLength
27765                                 );
27766                             }
27767                             
27768                             offset += markerLength;
27769                             
27770                             continue;
27771                         }
27772                         
27773                         break;
27774                     }
27775                     
27776                 }
27777                 
27778                 var url = _this.urlAPI.createObjectURL(_this.file);
27779                 
27780                 _this.loadCanvas(url);
27781                 
27782                 return;
27783             }
27784             
27785             reader.readAsArrayBuffer(this.file);
27786             
27787         }
27788         
27789     },
27790     
27791     parseExifData : function(dataView, offset, length)
27792     {
27793         var tiffOffset = offset + 10,
27794             littleEndian,
27795             dirOffset;
27796     
27797         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27798             // No Exif data, might be XMP data instead
27799             return;
27800         }
27801         
27802         // Check for the ASCII code for "Exif" (0x45786966):
27803         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27804             // No Exif data, might be XMP data instead
27805             return;
27806         }
27807         if (tiffOffset + 8 > dataView.byteLength) {
27808             Roo.log('Invalid Exif data: Invalid segment size.');
27809             return;
27810         }
27811         // Check for the two null bytes:
27812         if (dataView.getUint16(offset + 8) !== 0x0000) {
27813             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27814             return;
27815         }
27816         // Check the byte alignment:
27817         switch (dataView.getUint16(tiffOffset)) {
27818         case 0x4949:
27819             littleEndian = true;
27820             break;
27821         case 0x4D4D:
27822             littleEndian = false;
27823             break;
27824         default:
27825             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27826             return;
27827         }
27828         // Check for the TIFF tag marker (0x002A):
27829         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27830             Roo.log('Invalid Exif data: Missing TIFF marker.');
27831             return;
27832         }
27833         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27834         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27835         
27836         this.parseExifTags(
27837             dataView,
27838             tiffOffset,
27839             tiffOffset + dirOffset,
27840             littleEndian
27841         );
27842     },
27843     
27844     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27845     {
27846         var tagsNumber,
27847             dirEndOffset,
27848             i;
27849         if (dirOffset + 6 > dataView.byteLength) {
27850             Roo.log('Invalid Exif data: Invalid directory offset.');
27851             return;
27852         }
27853         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27854         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27855         if (dirEndOffset + 4 > dataView.byteLength) {
27856             Roo.log('Invalid Exif data: Invalid directory size.');
27857             return;
27858         }
27859         for (i = 0; i < tagsNumber; i += 1) {
27860             this.parseExifTag(
27861                 dataView,
27862                 tiffOffset,
27863                 dirOffset + 2 + 12 * i, // tag offset
27864                 littleEndian
27865             );
27866         }
27867         // Return the offset to the next directory:
27868         return dataView.getUint32(dirEndOffset, littleEndian);
27869     },
27870     
27871     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27872     {
27873         var tag = dataView.getUint16(offset, littleEndian);
27874         
27875         this.exif[tag] = this.getExifValue(
27876             dataView,
27877             tiffOffset,
27878             offset,
27879             dataView.getUint16(offset + 2, littleEndian), // tag type
27880             dataView.getUint32(offset + 4, littleEndian), // tag length
27881             littleEndian
27882         );
27883     },
27884     
27885     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27886     {
27887         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27888             tagSize,
27889             dataOffset,
27890             values,
27891             i,
27892             str,
27893             c;
27894     
27895         if (!tagType) {
27896             Roo.log('Invalid Exif data: Invalid tag type.');
27897             return;
27898         }
27899         
27900         tagSize = tagType.size * length;
27901         // Determine if the value is contained in the dataOffset bytes,
27902         // or if the value at the dataOffset is a pointer to the actual data:
27903         dataOffset = tagSize > 4 ?
27904                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27905         if (dataOffset + tagSize > dataView.byteLength) {
27906             Roo.log('Invalid Exif data: Invalid data offset.');
27907             return;
27908         }
27909         if (length === 1) {
27910             return tagType.getValue(dataView, dataOffset, littleEndian);
27911         }
27912         values = [];
27913         for (i = 0; i < length; i += 1) {
27914             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27915         }
27916         
27917         if (tagType.ascii) {
27918             str = '';
27919             // Concatenate the chars:
27920             for (i = 0; i < values.length; i += 1) {
27921                 c = values[i];
27922                 // Ignore the terminating NULL byte(s):
27923                 if (c === '\u0000') {
27924                     break;
27925                 }
27926                 str += c;
27927             }
27928             return str;
27929         }
27930         return values;
27931     }
27932     
27933 });
27934
27935 Roo.apply(Roo.bootstrap.UploadCropbox, {
27936     tags : {
27937         'Orientation': 0x0112
27938     },
27939     
27940     Orientation: {
27941             1: 0, //'top-left',
27942 //            2: 'top-right',
27943             3: 180, //'bottom-right',
27944 //            4: 'bottom-left',
27945 //            5: 'left-top',
27946             6: 90, //'right-top',
27947 //            7: 'right-bottom',
27948             8: 270 //'left-bottom'
27949     },
27950     
27951     exifTagTypes : {
27952         // byte, 8-bit unsigned int:
27953         1: {
27954             getValue: function (dataView, dataOffset) {
27955                 return dataView.getUint8(dataOffset);
27956             },
27957             size: 1
27958         },
27959         // ascii, 8-bit byte:
27960         2: {
27961             getValue: function (dataView, dataOffset) {
27962                 return String.fromCharCode(dataView.getUint8(dataOffset));
27963             },
27964             size: 1,
27965             ascii: true
27966         },
27967         // short, 16 bit int:
27968         3: {
27969             getValue: function (dataView, dataOffset, littleEndian) {
27970                 return dataView.getUint16(dataOffset, littleEndian);
27971             },
27972             size: 2
27973         },
27974         // long, 32 bit int:
27975         4: {
27976             getValue: function (dataView, dataOffset, littleEndian) {
27977                 return dataView.getUint32(dataOffset, littleEndian);
27978             },
27979             size: 4
27980         },
27981         // rational = two long values, first is numerator, second is denominator:
27982         5: {
27983             getValue: function (dataView, dataOffset, littleEndian) {
27984                 return dataView.getUint32(dataOffset, littleEndian) /
27985                     dataView.getUint32(dataOffset + 4, littleEndian);
27986             },
27987             size: 8
27988         },
27989         // slong, 32 bit signed int:
27990         9: {
27991             getValue: function (dataView, dataOffset, littleEndian) {
27992                 return dataView.getInt32(dataOffset, littleEndian);
27993             },
27994             size: 4
27995         },
27996         // srational, two slongs, first is numerator, second is denominator:
27997         10: {
27998             getValue: function (dataView, dataOffset, littleEndian) {
27999                 return dataView.getInt32(dataOffset, littleEndian) /
28000                     dataView.getInt32(dataOffset + 4, littleEndian);
28001             },
28002             size: 8
28003         }
28004     },
28005     
28006     footer : {
28007         STANDARD : [
28008             {
28009                 tag : 'div',
28010                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28011                 action : 'rotate-left',
28012                 cn : [
28013                     {
28014                         tag : 'button',
28015                         cls : 'btn btn-default',
28016                         html : '<i class="fa fa-undo"></i>'
28017                     }
28018                 ]
28019             },
28020             {
28021                 tag : 'div',
28022                 cls : 'btn-group roo-upload-cropbox-picture',
28023                 action : 'picture',
28024                 cn : [
28025                     {
28026                         tag : 'button',
28027                         cls : 'btn btn-default',
28028                         html : '<i class="fa fa-picture-o"></i>'
28029                     }
28030                 ]
28031             },
28032             {
28033                 tag : 'div',
28034                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28035                 action : 'rotate-right',
28036                 cn : [
28037                     {
28038                         tag : 'button',
28039                         cls : 'btn btn-default',
28040                         html : '<i class="fa fa-repeat"></i>'
28041                     }
28042                 ]
28043             }
28044         ],
28045         DOCUMENT : [
28046             {
28047                 tag : 'div',
28048                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28049                 action : 'rotate-left',
28050                 cn : [
28051                     {
28052                         tag : 'button',
28053                         cls : 'btn btn-default',
28054                         html : '<i class="fa fa-undo"></i>'
28055                     }
28056                 ]
28057             },
28058             {
28059                 tag : 'div',
28060                 cls : 'btn-group roo-upload-cropbox-download',
28061                 action : 'download',
28062                 cn : [
28063                     {
28064                         tag : 'button',
28065                         cls : 'btn btn-default',
28066                         html : '<i class="fa fa-download"></i>'
28067                     }
28068                 ]
28069             },
28070             {
28071                 tag : 'div',
28072                 cls : 'btn-group roo-upload-cropbox-crop',
28073                 action : 'crop',
28074                 cn : [
28075                     {
28076                         tag : 'button',
28077                         cls : 'btn btn-default',
28078                         html : '<i class="fa fa-crop"></i>'
28079                     }
28080                 ]
28081             },
28082             {
28083                 tag : 'div',
28084                 cls : 'btn-group roo-upload-cropbox-trash',
28085                 action : 'trash',
28086                 cn : [
28087                     {
28088                         tag : 'button',
28089                         cls : 'btn btn-default',
28090                         html : '<i class="fa fa-trash"></i>'
28091                     }
28092                 ]
28093             },
28094             {
28095                 tag : 'div',
28096                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28097                 action : 'rotate-right',
28098                 cn : [
28099                     {
28100                         tag : 'button',
28101                         cls : 'btn btn-default',
28102                         html : '<i class="fa fa-repeat"></i>'
28103                     }
28104                 ]
28105             }
28106         ],
28107         ROTATOR : [
28108             {
28109                 tag : 'div',
28110                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28111                 action : 'rotate-left',
28112                 cn : [
28113                     {
28114                         tag : 'button',
28115                         cls : 'btn btn-default',
28116                         html : '<i class="fa fa-undo"></i>'
28117                     }
28118                 ]
28119             },
28120             {
28121                 tag : 'div',
28122                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28123                 action : 'rotate-right',
28124                 cn : [
28125                     {
28126                         tag : 'button',
28127                         cls : 'btn btn-default',
28128                         html : '<i class="fa fa-repeat"></i>'
28129                     }
28130                 ]
28131             }
28132         ]
28133     }
28134 });
28135
28136 /*
28137 * Licence: LGPL
28138 */
28139
28140 /**
28141  * @class Roo.bootstrap.DocumentManager
28142  * @extends Roo.bootstrap.Component
28143  * Bootstrap DocumentManager class
28144  * @cfg {String} paramName default 'imageUpload'
28145  * @cfg {String} toolTipName default 'filename'
28146  * @cfg {String} method default POST
28147  * @cfg {String} url action url
28148  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28149  * @cfg {Boolean} multiple multiple upload default true
28150  * @cfg {Number} thumbSize default 300
28151  * @cfg {String} fieldLabel
28152  * @cfg {Number} labelWidth default 4
28153  * @cfg {String} labelAlign (left|top) default left
28154  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28155 * @cfg {Number} labellg set the width of label (1-12)
28156  * @cfg {Number} labelmd set the width of label (1-12)
28157  * @cfg {Number} labelsm set the width of label (1-12)
28158  * @cfg {Number} labelxs set the width of label (1-12)
28159  * 
28160  * @constructor
28161  * Create a new DocumentManager
28162  * @param {Object} config The config object
28163  */
28164
28165 Roo.bootstrap.DocumentManager = function(config){
28166     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28167     
28168     this.files = [];
28169     this.delegates = [];
28170     
28171     this.addEvents({
28172         /**
28173          * @event initial
28174          * Fire when initial the DocumentManager
28175          * @param {Roo.bootstrap.DocumentManager} this
28176          */
28177         "initial" : true,
28178         /**
28179          * @event inspect
28180          * inspect selected file
28181          * @param {Roo.bootstrap.DocumentManager} this
28182          * @param {File} file
28183          */
28184         "inspect" : true,
28185         /**
28186          * @event exception
28187          * Fire when xhr load exception
28188          * @param {Roo.bootstrap.DocumentManager} this
28189          * @param {XMLHttpRequest} xhr
28190          */
28191         "exception" : true,
28192         /**
28193          * @event afterupload
28194          * Fire when xhr load exception
28195          * @param {Roo.bootstrap.DocumentManager} this
28196          * @param {XMLHttpRequest} xhr
28197          */
28198         "afterupload" : true,
28199         /**
28200          * @event prepare
28201          * prepare the form data
28202          * @param {Roo.bootstrap.DocumentManager} this
28203          * @param {Object} formData
28204          */
28205         "prepare" : true,
28206         /**
28207          * @event remove
28208          * Fire when remove the file
28209          * @param {Roo.bootstrap.DocumentManager} this
28210          * @param {Object} file
28211          */
28212         "remove" : true,
28213         /**
28214          * @event refresh
28215          * Fire after refresh the file
28216          * @param {Roo.bootstrap.DocumentManager} this
28217          */
28218         "refresh" : true,
28219         /**
28220          * @event click
28221          * Fire after click the image
28222          * @param {Roo.bootstrap.DocumentManager} this
28223          * @param {Object} file
28224          */
28225         "click" : true,
28226         /**
28227          * @event edit
28228          * Fire when upload a image and editable set to true
28229          * @param {Roo.bootstrap.DocumentManager} this
28230          * @param {Object} file
28231          */
28232         "edit" : true,
28233         /**
28234          * @event beforeselectfile
28235          * Fire before select file
28236          * @param {Roo.bootstrap.DocumentManager} this
28237          */
28238         "beforeselectfile" : true,
28239         /**
28240          * @event process
28241          * Fire before process file
28242          * @param {Roo.bootstrap.DocumentManager} this
28243          * @param {Object} file
28244          */
28245         "process" : true
28246         
28247     });
28248 };
28249
28250 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28251     
28252     boxes : 0,
28253     inputName : '',
28254     thumbSize : 300,
28255     multiple : true,
28256     files : false,
28257     method : 'POST',
28258     url : '',
28259     paramName : 'imageUpload',
28260     toolTipName : 'filename',
28261     fieldLabel : '',
28262     labelWidth : 4,
28263     labelAlign : 'left',
28264     editable : true,
28265     delegates : false,
28266     xhr : false, 
28267     
28268     labellg : 0,
28269     labelmd : 0,
28270     labelsm : 0,
28271     labelxs : 0,
28272     
28273     getAutoCreate : function()
28274     {   
28275         var managerWidget = {
28276             tag : 'div',
28277             cls : 'roo-document-manager',
28278             cn : [
28279                 {
28280                     tag : 'input',
28281                     cls : 'roo-document-manager-selector',
28282                     type : 'file'
28283                 },
28284                 {
28285                     tag : 'div',
28286                     cls : 'roo-document-manager-uploader',
28287                     cn : [
28288                         {
28289                             tag : 'div',
28290                             cls : 'roo-document-manager-upload-btn',
28291                             html : '<i class="fa fa-plus"></i>'
28292                         }
28293                     ]
28294                     
28295                 }
28296             ]
28297         };
28298         
28299         var content = [
28300             {
28301                 tag : 'div',
28302                 cls : 'column col-md-12',
28303                 cn : managerWidget
28304             }
28305         ];
28306         
28307         if(this.fieldLabel.length){
28308             
28309             content = [
28310                 {
28311                     tag : 'div',
28312                     cls : 'column col-md-12',
28313                     html : this.fieldLabel
28314                 },
28315                 {
28316                     tag : 'div',
28317                     cls : 'column col-md-12',
28318                     cn : managerWidget
28319                 }
28320             ];
28321
28322             if(this.labelAlign == 'left'){
28323                 content = [
28324                     {
28325                         tag : 'div',
28326                         cls : 'column',
28327                         html : this.fieldLabel
28328                     },
28329                     {
28330                         tag : 'div',
28331                         cls : 'column',
28332                         cn : managerWidget
28333                     }
28334                 ];
28335                 
28336                 if(this.labelWidth > 12){
28337                     content[0].style = "width: " + this.labelWidth + 'px';
28338                 }
28339
28340                 if(this.labelWidth < 13 && this.labelmd == 0){
28341                     this.labelmd = this.labelWidth;
28342                 }
28343
28344                 if(this.labellg > 0){
28345                     content[0].cls += ' col-lg-' + this.labellg;
28346                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28347                 }
28348
28349                 if(this.labelmd > 0){
28350                     content[0].cls += ' col-md-' + this.labelmd;
28351                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28352                 }
28353
28354                 if(this.labelsm > 0){
28355                     content[0].cls += ' col-sm-' + this.labelsm;
28356                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28357                 }
28358
28359                 if(this.labelxs > 0){
28360                     content[0].cls += ' col-xs-' + this.labelxs;
28361                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28362                 }
28363                 
28364             }
28365         }
28366         
28367         var cfg = {
28368             tag : 'div',
28369             cls : 'row clearfix',
28370             cn : content
28371         };
28372         
28373         return cfg;
28374         
28375     },
28376     
28377     initEvents : function()
28378     {
28379         this.managerEl = this.el.select('.roo-document-manager', true).first();
28380         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28381         
28382         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28383         this.selectorEl.hide();
28384         
28385         if(this.multiple){
28386             this.selectorEl.attr('multiple', 'multiple');
28387         }
28388         
28389         this.selectorEl.on('change', this.onFileSelected, this);
28390         
28391         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28392         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28393         
28394         this.uploader.on('click', this.onUploaderClick, this);
28395         
28396         this.renderProgressDialog();
28397         
28398         var _this = this;
28399         
28400         window.addEventListener("resize", function() { _this.refresh(); } );
28401         
28402         this.fireEvent('initial', this);
28403     },
28404     
28405     renderProgressDialog : function()
28406     {
28407         var _this = this;
28408         
28409         this.progressDialog = new Roo.bootstrap.Modal({
28410             cls : 'roo-document-manager-progress-dialog',
28411             allow_close : false,
28412             title : '',
28413             buttons : [
28414                 {
28415                     name  :'cancel',
28416                     weight : 'danger',
28417                     html : 'Cancel'
28418                 }
28419             ], 
28420             listeners : { 
28421                 btnclick : function() {
28422                     _this.uploadCancel();
28423                     this.hide();
28424                 }
28425             }
28426         });
28427          
28428         this.progressDialog.render(Roo.get(document.body));
28429          
28430         this.progress = new Roo.bootstrap.Progress({
28431             cls : 'roo-document-manager-progress',
28432             active : true,
28433             striped : true
28434         });
28435         
28436         this.progress.render(this.progressDialog.getChildContainer());
28437         
28438         this.progressBar = new Roo.bootstrap.ProgressBar({
28439             cls : 'roo-document-manager-progress-bar',
28440             aria_valuenow : 0,
28441             aria_valuemin : 0,
28442             aria_valuemax : 12,
28443             panel : 'success'
28444         });
28445         
28446         this.progressBar.render(this.progress.getChildContainer());
28447     },
28448     
28449     onUploaderClick : function(e)
28450     {
28451         e.preventDefault();
28452      
28453         if(this.fireEvent('beforeselectfile', this) != false){
28454             this.selectorEl.dom.click();
28455         }
28456         
28457     },
28458     
28459     onFileSelected : function(e)
28460     {
28461         e.preventDefault();
28462         
28463         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28464             return;
28465         }
28466         
28467         Roo.each(this.selectorEl.dom.files, function(file){
28468             if(this.fireEvent('inspect', this, file) != false){
28469                 this.files.push(file);
28470             }
28471         }, this);
28472         
28473         this.queue();
28474         
28475     },
28476     
28477     queue : function()
28478     {
28479         this.selectorEl.dom.value = '';
28480         
28481         if(!this.files.length){
28482             return;
28483         }
28484         
28485         if(this.boxes > 0 && this.files.length > this.boxes){
28486             this.files = this.files.slice(0, this.boxes);
28487         }
28488         
28489         this.uploader.show();
28490         
28491         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28492             this.uploader.hide();
28493         }
28494         
28495         var _this = this;
28496         
28497         var files = [];
28498         
28499         var docs = [];
28500         
28501         Roo.each(this.files, function(file){
28502             
28503             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28504                 var f = this.renderPreview(file);
28505                 files.push(f);
28506                 return;
28507             }
28508             
28509             if(file.type.indexOf('image') != -1){
28510                 this.delegates.push(
28511                     (function(){
28512                         _this.process(file);
28513                     }).createDelegate(this)
28514                 );
28515         
28516                 return;
28517             }
28518             
28519             docs.push(
28520                 (function(){
28521                     _this.process(file);
28522                 }).createDelegate(this)
28523             );
28524             
28525         }, this);
28526         
28527         this.files = files;
28528         
28529         this.delegates = this.delegates.concat(docs);
28530         
28531         if(!this.delegates.length){
28532             this.refresh();
28533             return;
28534         }
28535         
28536         this.progressBar.aria_valuemax = this.delegates.length;
28537         
28538         this.arrange();
28539         
28540         return;
28541     },
28542     
28543     arrange : function()
28544     {
28545         if(!this.delegates.length){
28546             this.progressDialog.hide();
28547             this.refresh();
28548             return;
28549         }
28550         
28551         var delegate = this.delegates.shift();
28552         
28553         this.progressDialog.show();
28554         
28555         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28556         
28557         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28558         
28559         delegate();
28560     },
28561     
28562     refresh : function()
28563     {
28564         this.uploader.show();
28565         
28566         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28567             this.uploader.hide();
28568         }
28569         
28570         Roo.isTouch ? this.closable(false) : this.closable(true);
28571         
28572         this.fireEvent('refresh', this);
28573     },
28574     
28575     onRemove : function(e, el, o)
28576     {
28577         e.preventDefault();
28578         
28579         this.fireEvent('remove', this, o);
28580         
28581     },
28582     
28583     remove : function(o)
28584     {
28585         var files = [];
28586         
28587         Roo.each(this.files, function(file){
28588             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28589                 files.push(file);
28590                 return;
28591             }
28592
28593             o.target.remove();
28594
28595         }, this);
28596         
28597         this.files = files;
28598         
28599         this.refresh();
28600     },
28601     
28602     clear : function()
28603     {
28604         Roo.each(this.files, function(file){
28605             if(!file.target){
28606                 return;
28607             }
28608             
28609             file.target.remove();
28610
28611         }, this);
28612         
28613         this.files = [];
28614         
28615         this.refresh();
28616     },
28617     
28618     onClick : function(e, el, o)
28619     {
28620         e.preventDefault();
28621         
28622         this.fireEvent('click', this, o);
28623         
28624     },
28625     
28626     closable : function(closable)
28627     {
28628         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28629             
28630             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28631             
28632             if(closable){
28633                 el.show();
28634                 return;
28635             }
28636             
28637             el.hide();
28638             
28639         }, this);
28640     },
28641     
28642     xhrOnLoad : function(xhr)
28643     {
28644         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28645             el.remove();
28646         }, this);
28647         
28648         if (xhr.readyState !== 4) {
28649             this.arrange();
28650             this.fireEvent('exception', this, xhr);
28651             return;
28652         }
28653
28654         var response = Roo.decode(xhr.responseText);
28655         
28656         if(!response.success){
28657             this.arrange();
28658             this.fireEvent('exception', this, xhr);
28659             return;
28660         }
28661         
28662         var file = this.renderPreview(response.data);
28663         
28664         this.files.push(file);
28665         
28666         this.arrange();
28667         
28668         this.fireEvent('afterupload', this, xhr);
28669         
28670     },
28671     
28672     xhrOnError : function(xhr)
28673     {
28674         Roo.log('xhr on error');
28675         
28676         var response = Roo.decode(xhr.responseText);
28677           
28678         Roo.log(response);
28679         
28680         this.arrange();
28681     },
28682     
28683     process : function(file)
28684     {
28685         if(this.fireEvent('process', this, file) !== false){
28686             if(this.editable && file.type.indexOf('image') != -1){
28687                 this.fireEvent('edit', this, file);
28688                 return;
28689             }
28690
28691             this.uploadStart(file, false);
28692
28693             return;
28694         }
28695         
28696     },
28697     
28698     uploadStart : function(file, crop)
28699     {
28700         this.xhr = new XMLHttpRequest();
28701         
28702         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28703             this.arrange();
28704             return;
28705         }
28706         
28707         file.xhr = this.xhr;
28708             
28709         this.managerEl.createChild({
28710             tag : 'div',
28711             cls : 'roo-document-manager-loading',
28712             cn : [
28713                 {
28714                     tag : 'div',
28715                     tooltip : file.name,
28716                     cls : 'roo-document-manager-thumb',
28717                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28718                 }
28719             ]
28720
28721         });
28722
28723         this.xhr.open(this.method, this.url, true);
28724         
28725         var headers = {
28726             "Accept": "application/json",
28727             "Cache-Control": "no-cache",
28728             "X-Requested-With": "XMLHttpRequest"
28729         };
28730         
28731         for (var headerName in headers) {
28732             var headerValue = headers[headerName];
28733             if (headerValue) {
28734                 this.xhr.setRequestHeader(headerName, headerValue);
28735             }
28736         }
28737         
28738         var _this = this;
28739         
28740         this.xhr.onload = function()
28741         {
28742             _this.xhrOnLoad(_this.xhr);
28743         }
28744         
28745         this.xhr.onerror = function()
28746         {
28747             _this.xhrOnError(_this.xhr);
28748         }
28749         
28750         var formData = new FormData();
28751
28752         formData.append('returnHTML', 'NO');
28753         
28754         if(crop){
28755             formData.append('crop', crop);
28756         }
28757         
28758         formData.append(this.paramName, file, file.name);
28759         
28760         var options = {
28761             file : file, 
28762             manually : false
28763         };
28764         
28765         if(this.fireEvent('prepare', this, formData, options) != false){
28766             
28767             if(options.manually){
28768                 return;
28769             }
28770             
28771             this.xhr.send(formData);
28772             return;
28773         };
28774         
28775         this.uploadCancel();
28776     },
28777     
28778     uploadCancel : function()
28779     {
28780         if (this.xhr) {
28781             this.xhr.abort();
28782         }
28783         
28784         this.delegates = [];
28785         
28786         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28787             el.remove();
28788         }, this);
28789         
28790         this.arrange();
28791     },
28792     
28793     renderPreview : function(file)
28794     {
28795         if(typeof(file.target) != 'undefined' && file.target){
28796             return file;
28797         }
28798         
28799         var previewEl = this.managerEl.createChild({
28800             tag : 'div',
28801             cls : 'roo-document-manager-preview',
28802             cn : [
28803                 {
28804                     tag : 'div',
28805                     tooltip : file[this.toolTipName],
28806                     cls : 'roo-document-manager-thumb',
28807                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28808                 },
28809                 {
28810                     tag : 'button',
28811                     cls : 'close',
28812                     html : '<i class="fa fa-times-circle"></i>'
28813                 }
28814             ]
28815         });
28816
28817         var close = previewEl.select('button.close', true).first();
28818
28819         close.on('click', this.onRemove, this, file);
28820
28821         file.target = previewEl;
28822
28823         var image = previewEl.select('img', true).first();
28824         
28825         var _this = this;
28826         
28827         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28828         
28829         image.on('click', this.onClick, this, file);
28830         
28831         return file;
28832         
28833     },
28834     
28835     onPreviewLoad : function(file, image)
28836     {
28837         if(typeof(file.target) == 'undefined' || !file.target){
28838             return;
28839         }
28840         
28841         var width = image.dom.naturalWidth || image.dom.width;
28842         var height = image.dom.naturalHeight || image.dom.height;
28843         
28844         if(width > height){
28845             file.target.addClass('wide');
28846             return;
28847         }
28848         
28849         file.target.addClass('tall');
28850         return;
28851         
28852     },
28853     
28854     uploadFromSource : function(file, crop)
28855     {
28856         this.xhr = new XMLHttpRequest();
28857         
28858         this.managerEl.createChild({
28859             tag : 'div',
28860             cls : 'roo-document-manager-loading',
28861             cn : [
28862                 {
28863                     tag : 'div',
28864                     tooltip : file.name,
28865                     cls : 'roo-document-manager-thumb',
28866                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28867                 }
28868             ]
28869
28870         });
28871
28872         this.xhr.open(this.method, this.url, true);
28873         
28874         var headers = {
28875             "Accept": "application/json",
28876             "Cache-Control": "no-cache",
28877             "X-Requested-With": "XMLHttpRequest"
28878         };
28879         
28880         for (var headerName in headers) {
28881             var headerValue = headers[headerName];
28882             if (headerValue) {
28883                 this.xhr.setRequestHeader(headerName, headerValue);
28884             }
28885         }
28886         
28887         var _this = this;
28888         
28889         this.xhr.onload = function()
28890         {
28891             _this.xhrOnLoad(_this.xhr);
28892         }
28893         
28894         this.xhr.onerror = function()
28895         {
28896             _this.xhrOnError(_this.xhr);
28897         }
28898         
28899         var formData = new FormData();
28900
28901         formData.append('returnHTML', 'NO');
28902         
28903         formData.append('crop', crop);
28904         
28905         if(typeof(file.filename) != 'undefined'){
28906             formData.append('filename', file.filename);
28907         }
28908         
28909         if(typeof(file.mimetype) != 'undefined'){
28910             formData.append('mimetype', file.mimetype);
28911         }
28912         
28913         Roo.log(formData);
28914         
28915         if(this.fireEvent('prepare', this, formData) != false){
28916             this.xhr.send(formData);
28917         };
28918     }
28919 });
28920
28921 /*
28922 * Licence: LGPL
28923 */
28924
28925 /**
28926  * @class Roo.bootstrap.DocumentViewer
28927  * @extends Roo.bootstrap.Component
28928  * Bootstrap DocumentViewer class
28929  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28930  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28931  * 
28932  * @constructor
28933  * Create a new DocumentViewer
28934  * @param {Object} config The config object
28935  */
28936
28937 Roo.bootstrap.DocumentViewer = function(config){
28938     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28939     
28940     this.addEvents({
28941         /**
28942          * @event initial
28943          * Fire after initEvent
28944          * @param {Roo.bootstrap.DocumentViewer} this
28945          */
28946         "initial" : true,
28947         /**
28948          * @event click
28949          * Fire after click
28950          * @param {Roo.bootstrap.DocumentViewer} this
28951          */
28952         "click" : true,
28953         /**
28954          * @event download
28955          * Fire after download button
28956          * @param {Roo.bootstrap.DocumentViewer} this
28957          */
28958         "download" : true,
28959         /**
28960          * @event trash
28961          * Fire after trash button
28962          * @param {Roo.bootstrap.DocumentViewer} this
28963          */
28964         "trash" : true
28965         
28966     });
28967 };
28968
28969 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28970     
28971     showDownload : true,
28972     
28973     showTrash : true,
28974     
28975     getAutoCreate : function()
28976     {
28977         var cfg = {
28978             tag : 'div',
28979             cls : 'roo-document-viewer',
28980             cn : [
28981                 {
28982                     tag : 'div',
28983                     cls : 'roo-document-viewer-body',
28984                     cn : [
28985                         {
28986                             tag : 'div',
28987                             cls : 'roo-document-viewer-thumb',
28988                             cn : [
28989                                 {
28990                                     tag : 'img',
28991                                     cls : 'roo-document-viewer-image'
28992                                 }
28993                             ]
28994                         }
28995                     ]
28996                 },
28997                 {
28998                     tag : 'div',
28999                     cls : 'roo-document-viewer-footer',
29000                     cn : {
29001                         tag : 'div',
29002                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29003                         cn : [
29004                             {
29005                                 tag : 'div',
29006                                 cls : 'btn-group roo-document-viewer-download',
29007                                 cn : [
29008                                     {
29009                                         tag : 'button',
29010                                         cls : 'btn btn-default',
29011                                         html : '<i class="fa fa-download"></i>'
29012                                     }
29013                                 ]
29014                             },
29015                             {
29016                                 tag : 'div',
29017                                 cls : 'btn-group roo-document-viewer-trash',
29018                                 cn : [
29019                                     {
29020                                         tag : 'button',
29021                                         cls : 'btn btn-default',
29022                                         html : '<i class="fa fa-trash"></i>'
29023                                     }
29024                                 ]
29025                             }
29026                         ]
29027                     }
29028                 }
29029             ]
29030         };
29031         
29032         return cfg;
29033     },
29034     
29035     initEvents : function()
29036     {
29037         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29038         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29039         
29040         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29041         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29042         
29043         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29044         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29045         
29046         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29047         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29048         
29049         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29050         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29051         
29052         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29053         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29054         
29055         this.bodyEl.on('click', this.onClick, this);
29056         this.downloadBtn.on('click', this.onDownload, this);
29057         this.trashBtn.on('click', this.onTrash, this);
29058         
29059         this.downloadBtn.hide();
29060         this.trashBtn.hide();
29061         
29062         if(this.showDownload){
29063             this.downloadBtn.show();
29064         }
29065         
29066         if(this.showTrash){
29067             this.trashBtn.show();
29068         }
29069         
29070         if(!this.showDownload && !this.showTrash) {
29071             this.footerEl.hide();
29072         }
29073         
29074     },
29075     
29076     initial : function()
29077     {
29078         this.fireEvent('initial', this);
29079         
29080     },
29081     
29082     onClick : function(e)
29083     {
29084         e.preventDefault();
29085         
29086         this.fireEvent('click', this);
29087     },
29088     
29089     onDownload : function(e)
29090     {
29091         e.preventDefault();
29092         
29093         this.fireEvent('download', this);
29094     },
29095     
29096     onTrash : function(e)
29097     {
29098         e.preventDefault();
29099         
29100         this.fireEvent('trash', this);
29101     }
29102     
29103 });
29104 /*
29105  * - LGPL
29106  *
29107  * nav progress bar
29108  * 
29109  */
29110
29111 /**
29112  * @class Roo.bootstrap.NavProgressBar
29113  * @extends Roo.bootstrap.Component
29114  * Bootstrap NavProgressBar class
29115  * 
29116  * @constructor
29117  * Create a new nav progress bar
29118  * @param {Object} config The config object
29119  */
29120
29121 Roo.bootstrap.NavProgressBar = function(config){
29122     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29123
29124     this.bullets = this.bullets || [];
29125    
29126 //    Roo.bootstrap.NavProgressBar.register(this);
29127      this.addEvents({
29128         /**
29129              * @event changed
29130              * Fires when the active item changes
29131              * @param {Roo.bootstrap.NavProgressBar} this
29132              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29133              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29134          */
29135         'changed': true
29136      });
29137     
29138 };
29139
29140 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29141     
29142     bullets : [],
29143     barItems : [],
29144     
29145     getAutoCreate : function()
29146     {
29147         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29148         
29149         cfg = {
29150             tag : 'div',
29151             cls : 'roo-navigation-bar-group',
29152             cn : [
29153                 {
29154                     tag : 'div',
29155                     cls : 'roo-navigation-top-bar'
29156                 },
29157                 {
29158                     tag : 'div',
29159                     cls : 'roo-navigation-bullets-bar',
29160                     cn : [
29161                         {
29162                             tag : 'ul',
29163                             cls : 'roo-navigation-bar'
29164                         }
29165                     ]
29166                 },
29167                 
29168                 {
29169                     tag : 'div',
29170                     cls : 'roo-navigation-bottom-bar'
29171                 }
29172             ]
29173             
29174         };
29175         
29176         return cfg;
29177         
29178     },
29179     
29180     initEvents: function() 
29181     {
29182         
29183     },
29184     
29185     onRender : function(ct, position) 
29186     {
29187         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29188         
29189         if(this.bullets.length){
29190             Roo.each(this.bullets, function(b){
29191                this.addItem(b);
29192             }, this);
29193         }
29194         
29195         this.format();
29196         
29197     },
29198     
29199     addItem : function(cfg)
29200     {
29201         var item = new Roo.bootstrap.NavProgressItem(cfg);
29202         
29203         item.parentId = this.id;
29204         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29205         
29206         if(cfg.html){
29207             var top = new Roo.bootstrap.Element({
29208                 tag : 'div',
29209                 cls : 'roo-navigation-bar-text'
29210             });
29211             
29212             var bottom = new Roo.bootstrap.Element({
29213                 tag : 'div',
29214                 cls : 'roo-navigation-bar-text'
29215             });
29216             
29217             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29218             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29219             
29220             var topText = new Roo.bootstrap.Element({
29221                 tag : 'span',
29222                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29223             });
29224             
29225             var bottomText = new Roo.bootstrap.Element({
29226                 tag : 'span',
29227                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29228             });
29229             
29230             topText.onRender(top.el, null);
29231             bottomText.onRender(bottom.el, null);
29232             
29233             item.topEl = top;
29234             item.bottomEl = bottom;
29235         }
29236         
29237         this.barItems.push(item);
29238         
29239         return item;
29240     },
29241     
29242     getActive : function()
29243     {
29244         var active = false;
29245         
29246         Roo.each(this.barItems, function(v){
29247             
29248             if (!v.isActive()) {
29249                 return;
29250             }
29251             
29252             active = v;
29253             return false;
29254             
29255         });
29256         
29257         return active;
29258     },
29259     
29260     setActiveItem : function(item)
29261     {
29262         var prev = false;
29263         
29264         Roo.each(this.barItems, function(v){
29265             if (v.rid == item.rid) {
29266                 return ;
29267             }
29268             
29269             if (v.isActive()) {
29270                 v.setActive(false);
29271                 prev = v;
29272             }
29273         });
29274
29275         item.setActive(true);
29276         
29277         this.fireEvent('changed', this, item, prev);
29278     },
29279     
29280     getBarItem: function(rid)
29281     {
29282         var ret = false;
29283         
29284         Roo.each(this.barItems, function(e) {
29285             if (e.rid != rid) {
29286                 return;
29287             }
29288             
29289             ret =  e;
29290             return false;
29291         });
29292         
29293         return ret;
29294     },
29295     
29296     indexOfItem : function(item)
29297     {
29298         var index = false;
29299         
29300         Roo.each(this.barItems, function(v, i){
29301             
29302             if (v.rid != item.rid) {
29303                 return;
29304             }
29305             
29306             index = i;
29307             return false
29308         });
29309         
29310         return index;
29311     },
29312     
29313     setActiveNext : function()
29314     {
29315         var i = this.indexOfItem(this.getActive());
29316         
29317         if (i > this.barItems.length) {
29318             return;
29319         }
29320         
29321         this.setActiveItem(this.barItems[i+1]);
29322     },
29323     
29324     setActivePrev : function()
29325     {
29326         var i = this.indexOfItem(this.getActive());
29327         
29328         if (i  < 1) {
29329             return;
29330         }
29331         
29332         this.setActiveItem(this.barItems[i-1]);
29333     },
29334     
29335     format : function()
29336     {
29337         if(!this.barItems.length){
29338             return;
29339         }
29340      
29341         var width = 100 / this.barItems.length;
29342         
29343         Roo.each(this.barItems, function(i){
29344             i.el.setStyle('width', width + '%');
29345             i.topEl.el.setStyle('width', width + '%');
29346             i.bottomEl.el.setStyle('width', width + '%');
29347         }, this);
29348         
29349     }
29350     
29351 });
29352 /*
29353  * - LGPL
29354  *
29355  * Nav Progress Item
29356  * 
29357  */
29358
29359 /**
29360  * @class Roo.bootstrap.NavProgressItem
29361  * @extends Roo.bootstrap.Component
29362  * Bootstrap NavProgressItem class
29363  * @cfg {String} rid the reference id
29364  * @cfg {Boolean} active (true|false) Is item active default false
29365  * @cfg {Boolean} disabled (true|false) Is item active default false
29366  * @cfg {String} html
29367  * @cfg {String} position (top|bottom) text position default bottom
29368  * @cfg {String} icon show icon instead of number
29369  * 
29370  * @constructor
29371  * Create a new NavProgressItem
29372  * @param {Object} config The config object
29373  */
29374 Roo.bootstrap.NavProgressItem = function(config){
29375     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29376     this.addEvents({
29377         // raw events
29378         /**
29379          * @event click
29380          * The raw click event for the entire grid.
29381          * @param {Roo.bootstrap.NavProgressItem} this
29382          * @param {Roo.EventObject} e
29383          */
29384         "click" : true
29385     });
29386    
29387 };
29388
29389 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29390     
29391     rid : '',
29392     active : false,
29393     disabled : false,
29394     html : '',
29395     position : 'bottom',
29396     icon : false,
29397     
29398     getAutoCreate : function()
29399     {
29400         var iconCls = 'roo-navigation-bar-item-icon';
29401         
29402         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29403         
29404         var cfg = {
29405             tag: 'li',
29406             cls: 'roo-navigation-bar-item',
29407             cn : [
29408                 {
29409                     tag : 'i',
29410                     cls : iconCls
29411                 }
29412             ]
29413         };
29414         
29415         if(this.active){
29416             cfg.cls += ' active';
29417         }
29418         if(this.disabled){
29419             cfg.cls += ' disabled';
29420         }
29421         
29422         return cfg;
29423     },
29424     
29425     disable : function()
29426     {
29427         this.setDisabled(true);
29428     },
29429     
29430     enable : function()
29431     {
29432         this.setDisabled(false);
29433     },
29434     
29435     initEvents: function() 
29436     {
29437         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29438         
29439         this.iconEl.on('click', this.onClick, this);
29440     },
29441     
29442     onClick : function(e)
29443     {
29444         e.preventDefault();
29445         
29446         if(this.disabled){
29447             return;
29448         }
29449         
29450         if(this.fireEvent('click', this, e) === false){
29451             return;
29452         };
29453         
29454         this.parent().setActiveItem(this);
29455     },
29456     
29457     isActive: function () 
29458     {
29459         return this.active;
29460     },
29461     
29462     setActive : function(state)
29463     {
29464         if(this.active == state){
29465             return;
29466         }
29467         
29468         this.active = state;
29469         
29470         if (state) {
29471             this.el.addClass('active');
29472             return;
29473         }
29474         
29475         this.el.removeClass('active');
29476         
29477         return;
29478     },
29479     
29480     setDisabled : function(state)
29481     {
29482         if(this.disabled == state){
29483             return;
29484         }
29485         
29486         this.disabled = state;
29487         
29488         if (state) {
29489             this.el.addClass('disabled');
29490             return;
29491         }
29492         
29493         this.el.removeClass('disabled');
29494     },
29495     
29496     tooltipEl : function()
29497     {
29498         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29499     }
29500 });
29501  
29502
29503  /*
29504  * - LGPL
29505  *
29506  * FieldLabel
29507  * 
29508  */
29509
29510 /**
29511  * @class Roo.bootstrap.FieldLabel
29512  * @extends Roo.bootstrap.Component
29513  * Bootstrap FieldLabel class
29514  * @cfg {String} html contents of the element
29515  * @cfg {String} tag tag of the element default label
29516  * @cfg {String} cls class of the element
29517  * @cfg {String} target label target 
29518  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29519  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29520  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29521  * @cfg {String} iconTooltip default "This field is required"
29522  * 
29523  * @constructor
29524  * Create a new FieldLabel
29525  * @param {Object} config The config object
29526  */
29527
29528 Roo.bootstrap.FieldLabel = function(config){
29529     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29530     
29531     this.addEvents({
29532             /**
29533              * @event invalid
29534              * Fires after the field has been marked as invalid.
29535              * @param {Roo.form.FieldLabel} this
29536              * @param {String} msg The validation message
29537              */
29538             invalid : true,
29539             /**
29540              * @event valid
29541              * Fires after the field has been validated with no errors.
29542              * @param {Roo.form.FieldLabel} this
29543              */
29544             valid : true
29545         });
29546 };
29547
29548 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29549     
29550     tag: 'label',
29551     cls: '',
29552     html: '',
29553     target: '',
29554     allowBlank : true,
29555     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29556     validClass : 'text-success fa fa-lg fa-check',
29557     iconTooltip : 'This field is required',
29558     
29559     getAutoCreate : function(){
29560         
29561         var cfg = {
29562             tag : this.tag,
29563             cls : 'roo-bootstrap-field-label ' + this.cls,
29564             for : this.target,
29565             cn : [
29566                 {
29567                     tag : 'i',
29568                     cls : '',
29569                     tooltip : this.iconTooltip
29570                 },
29571                 {
29572                     tag : 'span',
29573                     html : this.html
29574                 }
29575             ] 
29576         };
29577         
29578         return cfg;
29579     },
29580     
29581     initEvents: function() 
29582     {
29583         Roo.bootstrap.Element.superclass.initEvents.call(this);
29584         
29585         this.iconEl = this.el.select('i', true).first();
29586         
29587         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29588         
29589         Roo.bootstrap.FieldLabel.register(this);
29590     },
29591     
29592     /**
29593      * Mark this field as valid
29594      */
29595     markValid : function()
29596     {
29597         this.iconEl.show();
29598         
29599         this.iconEl.removeClass(this.invalidClass);
29600         
29601         this.iconEl.addClass(this.validClass);
29602         
29603         this.fireEvent('valid', this);
29604     },
29605     
29606     /**
29607      * Mark this field as invalid
29608      * @param {String} msg The validation message
29609      */
29610     markInvalid : function(msg)
29611     {
29612         this.iconEl.show();
29613         
29614         this.iconEl.removeClass(this.validClass);
29615         
29616         this.iconEl.addClass(this.invalidClass);
29617         
29618         this.fireEvent('invalid', this, msg);
29619     }
29620     
29621    
29622 });
29623
29624 Roo.apply(Roo.bootstrap.FieldLabel, {
29625     
29626     groups: {},
29627     
29628      /**
29629     * register a FieldLabel Group
29630     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29631     */
29632     register : function(label)
29633     {
29634         if(this.groups.hasOwnProperty(label.target)){
29635             return;
29636         }
29637      
29638         this.groups[label.target] = label;
29639         
29640     },
29641     /**
29642     * fetch a FieldLabel Group based on the target
29643     * @param {string} target
29644     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29645     */
29646     get: function(target) {
29647         if (typeof(this.groups[target]) == 'undefined') {
29648             return false;
29649         }
29650         
29651         return this.groups[target] ;
29652     }
29653 });
29654
29655  
29656
29657  /*
29658  * - LGPL
29659  *
29660  * page DateSplitField.
29661  * 
29662  */
29663
29664
29665 /**
29666  * @class Roo.bootstrap.DateSplitField
29667  * @extends Roo.bootstrap.Component
29668  * Bootstrap DateSplitField class
29669  * @cfg {string} fieldLabel - the label associated
29670  * @cfg {Number} labelWidth set the width of label (0-12)
29671  * @cfg {String} labelAlign (top|left)
29672  * @cfg {Boolean} dayAllowBlank (true|false) default false
29673  * @cfg {Boolean} monthAllowBlank (true|false) default false
29674  * @cfg {Boolean} yearAllowBlank (true|false) default false
29675  * @cfg {string} dayPlaceholder 
29676  * @cfg {string} monthPlaceholder
29677  * @cfg {string} yearPlaceholder
29678  * @cfg {string} dayFormat default 'd'
29679  * @cfg {string} monthFormat default 'm'
29680  * @cfg {string} yearFormat default 'Y'
29681  * @cfg {Number} labellg set the width of label (1-12)
29682  * @cfg {Number} labelmd set the width of label (1-12)
29683  * @cfg {Number} labelsm set the width of label (1-12)
29684  * @cfg {Number} labelxs set the width of label (1-12)
29685
29686  *     
29687  * @constructor
29688  * Create a new DateSplitField
29689  * @param {Object} config The config object
29690  */
29691
29692 Roo.bootstrap.DateSplitField = function(config){
29693     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29694     
29695     this.addEvents({
29696         // raw events
29697          /**
29698          * @event years
29699          * getting the data of years
29700          * @param {Roo.bootstrap.DateSplitField} this
29701          * @param {Object} years
29702          */
29703         "years" : true,
29704         /**
29705          * @event days
29706          * getting the data of days
29707          * @param {Roo.bootstrap.DateSplitField} this
29708          * @param {Object} days
29709          */
29710         "days" : true,
29711         /**
29712          * @event invalid
29713          * Fires after the field has been marked as invalid.
29714          * @param {Roo.form.Field} this
29715          * @param {String} msg The validation message
29716          */
29717         invalid : true,
29718        /**
29719          * @event valid
29720          * Fires after the field has been validated with no errors.
29721          * @param {Roo.form.Field} this
29722          */
29723         valid : true
29724     });
29725 };
29726
29727 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29728     
29729     fieldLabel : '',
29730     labelAlign : 'top',
29731     labelWidth : 3,
29732     dayAllowBlank : false,
29733     monthAllowBlank : false,
29734     yearAllowBlank : false,
29735     dayPlaceholder : '',
29736     monthPlaceholder : '',
29737     yearPlaceholder : '',
29738     dayFormat : 'd',
29739     monthFormat : 'm',
29740     yearFormat : 'Y',
29741     isFormField : true,
29742     labellg : 0,
29743     labelmd : 0,
29744     labelsm : 0,
29745     labelxs : 0,
29746     
29747     getAutoCreate : function()
29748     {
29749         var cfg = {
29750             tag : 'div',
29751             cls : 'row roo-date-split-field-group',
29752             cn : [
29753                 {
29754                     tag : 'input',
29755                     type : 'hidden',
29756                     cls : 'form-hidden-field roo-date-split-field-group-value',
29757                     name : this.name
29758                 }
29759             ]
29760         };
29761         
29762         var labelCls = 'col-md-12';
29763         var contentCls = 'col-md-4';
29764         
29765         if(this.fieldLabel){
29766             
29767             var label = {
29768                 tag : 'div',
29769                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29770                 cn : [
29771                     {
29772                         tag : 'label',
29773                         html : this.fieldLabel
29774                     }
29775                 ]
29776             };
29777             
29778             if(this.labelAlign == 'left'){
29779             
29780                 if(this.labelWidth > 12){
29781                     label.style = "width: " + this.labelWidth + 'px';
29782                 }
29783
29784                 if(this.labelWidth < 13 && this.labelmd == 0){
29785                     this.labelmd = this.labelWidth;
29786                 }
29787
29788                 if(this.labellg > 0){
29789                     labelCls = ' col-lg-' + this.labellg;
29790                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29791                 }
29792
29793                 if(this.labelmd > 0){
29794                     labelCls = ' col-md-' + this.labelmd;
29795                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29796                 }
29797
29798                 if(this.labelsm > 0){
29799                     labelCls = ' col-sm-' + this.labelsm;
29800                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29801                 }
29802
29803                 if(this.labelxs > 0){
29804                     labelCls = ' col-xs-' + this.labelxs;
29805                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29806                 }
29807             }
29808             
29809             label.cls += ' ' + labelCls;
29810             
29811             cfg.cn.push(label);
29812         }
29813         
29814         Roo.each(['day', 'month', 'year'], function(t){
29815             cfg.cn.push({
29816                 tag : 'div',
29817                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29818             });
29819         }, this);
29820         
29821         return cfg;
29822     },
29823     
29824     inputEl: function ()
29825     {
29826         return this.el.select('.roo-date-split-field-group-value', true).first();
29827     },
29828     
29829     onRender : function(ct, position) 
29830     {
29831         var _this = this;
29832         
29833         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29834         
29835         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29836         
29837         this.dayField = new Roo.bootstrap.ComboBox({
29838             allowBlank : this.dayAllowBlank,
29839             alwaysQuery : true,
29840             displayField : 'value',
29841             editable : false,
29842             fieldLabel : '',
29843             forceSelection : true,
29844             mode : 'local',
29845             placeholder : this.dayPlaceholder,
29846             selectOnFocus : true,
29847             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29848             triggerAction : 'all',
29849             typeAhead : true,
29850             valueField : 'value',
29851             store : new Roo.data.SimpleStore({
29852                 data : (function() {    
29853                     var days = [];
29854                     _this.fireEvent('days', _this, days);
29855                     return days;
29856                 })(),
29857                 fields : [ 'value' ]
29858             }),
29859             listeners : {
29860                 select : function (_self, record, index)
29861                 {
29862                     _this.setValue(_this.getValue());
29863                 }
29864             }
29865         });
29866
29867         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29868         
29869         this.monthField = new Roo.bootstrap.MonthField({
29870             after : '<i class=\"fa fa-calendar\"></i>',
29871             allowBlank : this.monthAllowBlank,
29872             placeholder : this.monthPlaceholder,
29873             readOnly : true,
29874             listeners : {
29875                 render : function (_self)
29876                 {
29877                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29878                         e.preventDefault();
29879                         _self.focus();
29880                     });
29881                 },
29882                 select : function (_self, oldvalue, newvalue)
29883                 {
29884                     _this.setValue(_this.getValue());
29885                 }
29886             }
29887         });
29888         
29889         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29890         
29891         this.yearField = new Roo.bootstrap.ComboBox({
29892             allowBlank : this.yearAllowBlank,
29893             alwaysQuery : true,
29894             displayField : 'value',
29895             editable : false,
29896             fieldLabel : '',
29897             forceSelection : true,
29898             mode : 'local',
29899             placeholder : this.yearPlaceholder,
29900             selectOnFocus : true,
29901             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29902             triggerAction : 'all',
29903             typeAhead : true,
29904             valueField : 'value',
29905             store : new Roo.data.SimpleStore({
29906                 data : (function() {
29907                     var years = [];
29908                     _this.fireEvent('years', _this, years);
29909                     return years;
29910                 })(),
29911                 fields : [ 'value' ]
29912             }),
29913             listeners : {
29914                 select : function (_self, record, index)
29915                 {
29916                     _this.setValue(_this.getValue());
29917                 }
29918             }
29919         });
29920
29921         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29922     },
29923     
29924     setValue : function(v, format)
29925     {
29926         this.inputEl.dom.value = v;
29927         
29928         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29929         
29930         var d = Date.parseDate(v, f);
29931         
29932         if(!d){
29933             this.validate();
29934             return;
29935         }
29936         
29937         this.setDay(d.format(this.dayFormat));
29938         this.setMonth(d.format(this.monthFormat));
29939         this.setYear(d.format(this.yearFormat));
29940         
29941         this.validate();
29942         
29943         return;
29944     },
29945     
29946     setDay : function(v)
29947     {
29948         this.dayField.setValue(v);
29949         this.inputEl.dom.value = this.getValue();
29950         this.validate();
29951         return;
29952     },
29953     
29954     setMonth : function(v)
29955     {
29956         this.monthField.setValue(v, true);
29957         this.inputEl.dom.value = this.getValue();
29958         this.validate();
29959         return;
29960     },
29961     
29962     setYear : function(v)
29963     {
29964         this.yearField.setValue(v);
29965         this.inputEl.dom.value = this.getValue();
29966         this.validate();
29967         return;
29968     },
29969     
29970     getDay : function()
29971     {
29972         return this.dayField.getValue();
29973     },
29974     
29975     getMonth : function()
29976     {
29977         return this.monthField.getValue();
29978     },
29979     
29980     getYear : function()
29981     {
29982         return this.yearField.getValue();
29983     },
29984     
29985     getValue : function()
29986     {
29987         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29988         
29989         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29990         
29991         return date;
29992     },
29993     
29994     reset : function()
29995     {
29996         this.setDay('');
29997         this.setMonth('');
29998         this.setYear('');
29999         this.inputEl.dom.value = '';
30000         this.validate();
30001         return;
30002     },
30003     
30004     validate : function()
30005     {
30006         var d = this.dayField.validate();
30007         var m = this.monthField.validate();
30008         var y = this.yearField.validate();
30009         
30010         var valid = true;
30011         
30012         if(
30013                 (!this.dayAllowBlank && !d) ||
30014                 (!this.monthAllowBlank && !m) ||
30015                 (!this.yearAllowBlank && !y)
30016         ){
30017             valid = false;
30018         }
30019         
30020         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30021             return valid;
30022         }
30023         
30024         if(valid){
30025             this.markValid();
30026             return valid;
30027         }
30028         
30029         this.markInvalid();
30030         
30031         return valid;
30032     },
30033     
30034     markValid : function()
30035     {
30036         
30037         var label = this.el.select('label', true).first();
30038         var icon = this.el.select('i.fa-star', true).first();
30039
30040         if(label && icon){
30041             icon.remove();
30042         }
30043         
30044         this.fireEvent('valid', this);
30045     },
30046     
30047      /**
30048      * Mark this field as invalid
30049      * @param {String} msg The validation message
30050      */
30051     markInvalid : function(msg)
30052     {
30053         
30054         var label = this.el.select('label', true).first();
30055         var icon = this.el.select('i.fa-star', true).first();
30056
30057         if(label && !icon){
30058             this.el.select('.roo-date-split-field-label', true).createChild({
30059                 tag : 'i',
30060                 cls : 'text-danger fa fa-lg fa-star',
30061                 tooltip : 'This field is required',
30062                 style : 'margin-right:5px;'
30063             }, label, true);
30064         }
30065         
30066         this.fireEvent('invalid', this, msg);
30067     },
30068     
30069     clearInvalid : function()
30070     {
30071         var label = this.el.select('label', true).first();
30072         var icon = this.el.select('i.fa-star', true).first();
30073
30074         if(label && icon){
30075             icon.remove();
30076         }
30077         
30078         this.fireEvent('valid', this);
30079     },
30080     
30081     getName: function()
30082     {
30083         return this.name;
30084     }
30085     
30086 });
30087
30088  /**
30089  *
30090  * This is based on 
30091  * http://masonry.desandro.com
30092  *
30093  * The idea is to render all the bricks based on vertical width...
30094  *
30095  * The original code extends 'outlayer' - we might need to use that....
30096  * 
30097  */
30098
30099
30100 /**
30101  * @class Roo.bootstrap.LayoutMasonry
30102  * @extends Roo.bootstrap.Component
30103  * Bootstrap Layout Masonry class
30104  * 
30105  * @constructor
30106  * Create a new Element
30107  * @param {Object} config The config object
30108  */
30109
30110 Roo.bootstrap.LayoutMasonry = function(config){
30111     
30112     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30113     
30114     this.bricks = [];
30115     
30116     Roo.bootstrap.LayoutMasonry.register(this);
30117     
30118     this.addEvents({
30119         // raw events
30120         /**
30121          * @event layout
30122          * Fire after layout the items
30123          * @param {Roo.bootstrap.LayoutMasonry} this
30124          * @param {Roo.EventObject} e
30125          */
30126         "layout" : true
30127     });
30128     
30129 };
30130
30131 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30132     
30133     /**
30134      * @cfg {Boolean} isLayoutInstant = no animation?
30135      */   
30136     isLayoutInstant : false, // needed?
30137    
30138     /**
30139      * @cfg {Number} boxWidth  width of the columns
30140      */   
30141     boxWidth : 450,
30142     
30143       /**
30144      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30145      */   
30146     boxHeight : 0,
30147     
30148     /**
30149      * @cfg {Number} padWidth padding below box..
30150      */   
30151     padWidth : 10, 
30152     
30153     /**
30154      * @cfg {Number} gutter gutter width..
30155      */   
30156     gutter : 10,
30157     
30158      /**
30159      * @cfg {Number} maxCols maximum number of columns
30160      */   
30161     
30162     maxCols: 0,
30163     
30164     /**
30165      * @cfg {Boolean} isAutoInitial defalut true
30166      */   
30167     isAutoInitial : true, 
30168     
30169     containerWidth: 0,
30170     
30171     /**
30172      * @cfg {Boolean} isHorizontal defalut false
30173      */   
30174     isHorizontal : false, 
30175
30176     currentSize : null,
30177     
30178     tag: 'div',
30179     
30180     cls: '',
30181     
30182     bricks: null, //CompositeElement
30183     
30184     cols : 1,
30185     
30186     _isLayoutInited : false,
30187     
30188 //    isAlternative : false, // only use for vertical layout...
30189     
30190     /**
30191      * @cfg {Number} alternativePadWidth padding below box..
30192      */   
30193     alternativePadWidth : 50,
30194     
30195     selectedBrick : [],
30196     
30197     getAutoCreate : function(){
30198         
30199         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30200         
30201         var cfg = {
30202             tag: this.tag,
30203             cls: 'blog-masonary-wrapper ' + this.cls,
30204             cn : {
30205                 cls : 'mas-boxes masonary'
30206             }
30207         };
30208         
30209         return cfg;
30210     },
30211     
30212     getChildContainer: function( )
30213     {
30214         if (this.boxesEl) {
30215             return this.boxesEl;
30216         }
30217         
30218         this.boxesEl = this.el.select('.mas-boxes').first();
30219         
30220         return this.boxesEl;
30221     },
30222     
30223     
30224     initEvents : function()
30225     {
30226         var _this = this;
30227         
30228         if(this.isAutoInitial){
30229             Roo.log('hook children rendered');
30230             this.on('childrenrendered', function() {
30231                 Roo.log('children rendered');
30232                 _this.initial();
30233             } ,this);
30234         }
30235     },
30236     
30237     initial : function()
30238     {
30239         this.currentSize = this.el.getBox(true);
30240         
30241         Roo.EventManager.onWindowResize(this.resize, this); 
30242
30243         if(!this.isAutoInitial){
30244             this.layout();
30245             return;
30246         }
30247         
30248         this.layout();
30249         
30250         return;
30251         //this.layout.defer(500,this);
30252         
30253     },
30254     
30255     resize : function()
30256     {
30257         var cs = this.el.getBox(true);
30258         
30259         if (
30260                 this.currentSize.width == cs.width && 
30261                 this.currentSize.x == cs.x && 
30262                 this.currentSize.height == cs.height && 
30263                 this.currentSize.y == cs.y 
30264         ) {
30265             Roo.log("no change in with or X or Y");
30266             return;
30267         }
30268         
30269         this.currentSize = cs;
30270         
30271         this.layout();
30272         
30273     },
30274     
30275     layout : function()
30276     {   
30277         this._resetLayout();
30278         
30279         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30280         
30281         this.layoutItems( isInstant );
30282       
30283         this._isLayoutInited = true;
30284         
30285         this.fireEvent('layout', this);
30286         
30287     },
30288     
30289     _resetLayout : function()
30290     {
30291         if(this.isHorizontal){
30292             this.horizontalMeasureColumns();
30293             return;
30294         }
30295         
30296         this.verticalMeasureColumns();
30297         
30298     },
30299     
30300     verticalMeasureColumns : function()
30301     {
30302         this.getContainerWidth();
30303         
30304 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30305 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30306 //            return;
30307 //        }
30308         
30309         var boxWidth = this.boxWidth + this.padWidth;
30310         
30311         if(this.containerWidth < this.boxWidth){
30312             boxWidth = this.containerWidth
30313         }
30314         
30315         var containerWidth = this.containerWidth;
30316         
30317         var cols = Math.floor(containerWidth / boxWidth);
30318         
30319         this.cols = Math.max( cols, 1 );
30320         
30321         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30322         
30323         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30324         
30325         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30326         
30327         this.colWidth = boxWidth + avail - this.padWidth;
30328         
30329         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
30330         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30331     },
30332     
30333     horizontalMeasureColumns : function()
30334     {
30335         this.getContainerWidth();
30336         
30337         var boxWidth = this.boxWidth;
30338         
30339         if(this.containerWidth < boxWidth){
30340             boxWidth = this.containerWidth;
30341         }
30342         
30343         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30344         
30345         this.el.setHeight(boxWidth);
30346         
30347     },
30348     
30349     getContainerWidth : function()
30350     {
30351         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30352     },
30353     
30354     layoutItems : function( isInstant )
30355     {
30356         Roo.log(this.bricks);
30357         
30358         var items = Roo.apply([], this.bricks);
30359         
30360         if(this.isHorizontal){
30361             this._horizontalLayoutItems( items , isInstant );
30362             return;
30363         }
30364         
30365 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30366 //            this._verticalAlternativeLayoutItems( items , isInstant );
30367 //            return;
30368 //        }
30369         
30370         this._verticalLayoutItems( items , isInstant );
30371         
30372     },
30373     
30374     _verticalLayoutItems : function ( items , isInstant)
30375     {
30376         if ( !items || !items.length ) {
30377             return;
30378         }
30379         
30380         var standard = [
30381             ['xs', 'xs', 'xs', 'tall'],
30382             ['xs', 'xs', 'tall'],
30383             ['xs', 'xs', 'sm'],
30384             ['xs', 'xs', 'xs'],
30385             ['xs', 'tall'],
30386             ['xs', 'sm'],
30387             ['xs', 'xs'],
30388             ['xs'],
30389             
30390             ['sm', 'xs', 'xs'],
30391             ['sm', 'xs'],
30392             ['sm'],
30393             
30394             ['tall', 'xs', 'xs', 'xs'],
30395             ['tall', 'xs', 'xs'],
30396             ['tall', 'xs'],
30397             ['tall']
30398             
30399         ];
30400         
30401         var queue = [];
30402         
30403         var boxes = [];
30404         
30405         var box = [];
30406         
30407         Roo.each(items, function(item, k){
30408             
30409             switch (item.size) {
30410                 // these layouts take up a full box,
30411                 case 'md' :
30412                 case 'md-left' :
30413                 case 'md-right' :
30414                 case 'wide' :
30415                     
30416                     if(box.length){
30417                         boxes.push(box);
30418                         box = [];
30419                     }
30420                     
30421                     boxes.push([item]);
30422                     
30423                     break;
30424                     
30425                 case 'xs' :
30426                 case 'sm' :
30427                 case 'tall' :
30428                     
30429                     box.push(item);
30430                     
30431                     break;
30432                 default :
30433                     break;
30434                     
30435             }
30436             
30437         }, this);
30438         
30439         if(box.length){
30440             boxes.push(box);
30441             box = [];
30442         }
30443         
30444         var filterPattern = function(box, length)
30445         {
30446             if(!box.length){
30447                 return;
30448             }
30449             
30450             var match = false;
30451             
30452             var pattern = box.slice(0, length);
30453             
30454             var format = [];
30455             
30456             Roo.each(pattern, function(i){
30457                 format.push(i.size);
30458             }, this);
30459             
30460             Roo.each(standard, function(s){
30461                 
30462                 if(String(s) != String(format)){
30463                     return;
30464                 }
30465                 
30466                 match = true;
30467                 return false;
30468                 
30469             }, this);
30470             
30471             if(!match && length == 1){
30472                 return;
30473             }
30474             
30475             if(!match){
30476                 filterPattern(box, length - 1);
30477                 return;
30478             }
30479                 
30480             queue.push(pattern);
30481
30482             box = box.slice(length, box.length);
30483
30484             filterPattern(box, 4);
30485
30486             return;
30487             
30488         }
30489         
30490         Roo.each(boxes, function(box, k){
30491             
30492             if(!box.length){
30493                 return;
30494             }
30495             
30496             if(box.length == 1){
30497                 queue.push(box);
30498                 return;
30499             }
30500             
30501             filterPattern(box, 4);
30502             
30503         }, this);
30504         
30505         this._processVerticalLayoutQueue( queue, isInstant );
30506         
30507     },
30508     
30509 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30510 //    {
30511 //        if ( !items || !items.length ) {
30512 //            return;
30513 //        }
30514 //
30515 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30516 //        
30517 //    },
30518     
30519     _horizontalLayoutItems : function ( items , isInstant)
30520     {
30521         if ( !items || !items.length || items.length < 3) {
30522             return;
30523         }
30524         
30525         items.reverse();
30526         
30527         var eItems = items.slice(0, 3);
30528         
30529         items = items.slice(3, items.length);
30530         
30531         var standard = [
30532             ['xs', 'xs', 'xs', 'wide'],
30533             ['xs', 'xs', 'wide'],
30534             ['xs', 'xs', 'sm'],
30535             ['xs', 'xs', 'xs'],
30536             ['xs', 'wide'],
30537             ['xs', 'sm'],
30538             ['xs', 'xs'],
30539             ['xs'],
30540             
30541             ['sm', 'xs', 'xs'],
30542             ['sm', 'xs'],
30543             ['sm'],
30544             
30545             ['wide', 'xs', 'xs', 'xs'],
30546             ['wide', 'xs', 'xs'],
30547             ['wide', 'xs'],
30548             ['wide'],
30549             
30550             ['wide-thin']
30551         ];
30552         
30553         var queue = [];
30554         
30555         var boxes = [];
30556         
30557         var box = [];
30558         
30559         Roo.each(items, function(item, k){
30560             
30561             switch (item.size) {
30562                 case 'md' :
30563                 case 'md-left' :
30564                 case 'md-right' :
30565                 case 'tall' :
30566                     
30567                     if(box.length){
30568                         boxes.push(box);
30569                         box = [];
30570                     }
30571                     
30572                     boxes.push([item]);
30573                     
30574                     break;
30575                     
30576                 case 'xs' :
30577                 case 'sm' :
30578                 case 'wide' :
30579                 case 'wide-thin' :
30580                     
30581                     box.push(item);
30582                     
30583                     break;
30584                 default :
30585                     break;
30586                     
30587             }
30588             
30589         }, this);
30590         
30591         if(box.length){
30592             boxes.push(box);
30593             box = [];
30594         }
30595         
30596         var filterPattern = function(box, length)
30597         {
30598             if(!box.length){
30599                 return;
30600             }
30601             
30602             var match = false;
30603             
30604             var pattern = box.slice(0, length);
30605             
30606             var format = [];
30607             
30608             Roo.each(pattern, function(i){
30609                 format.push(i.size);
30610             }, this);
30611             
30612             Roo.each(standard, function(s){
30613                 
30614                 if(String(s) != String(format)){
30615                     return;
30616                 }
30617                 
30618                 match = true;
30619                 return false;
30620                 
30621             }, this);
30622             
30623             if(!match && length == 1){
30624                 return;
30625             }
30626             
30627             if(!match){
30628                 filterPattern(box, length - 1);
30629                 return;
30630             }
30631                 
30632             queue.push(pattern);
30633
30634             box = box.slice(length, box.length);
30635
30636             filterPattern(box, 4);
30637
30638             return;
30639             
30640         }
30641         
30642         Roo.each(boxes, function(box, k){
30643             
30644             if(!box.length){
30645                 return;
30646             }
30647             
30648             if(box.length == 1){
30649                 queue.push(box);
30650                 return;
30651             }
30652             
30653             filterPattern(box, 4);
30654             
30655         }, this);
30656         
30657         
30658         var prune = [];
30659         
30660         var pos = this.el.getBox(true);
30661         
30662         var minX = pos.x;
30663         
30664         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30665         
30666         var hit_end = false;
30667         
30668         Roo.each(queue, function(box){
30669             
30670             if(hit_end){
30671                 
30672                 Roo.each(box, function(b){
30673                 
30674                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30675                     b.el.hide();
30676
30677                 }, this);
30678
30679                 return;
30680             }
30681             
30682             var mx = 0;
30683             
30684             Roo.each(box, function(b){
30685                 
30686                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30687                 b.el.show();
30688
30689                 mx = Math.max(mx, b.x);
30690                 
30691             }, this);
30692             
30693             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30694             
30695             if(maxX < minX){
30696                 
30697                 Roo.each(box, function(b){
30698                 
30699                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30700                     b.el.hide();
30701                     
30702                 }, this);
30703                 
30704                 hit_end = true;
30705                 
30706                 return;
30707             }
30708             
30709             prune.push(box);
30710             
30711         }, this);
30712         
30713         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30714     },
30715     
30716     /** Sets position of item in DOM
30717     * @param {Element} item
30718     * @param {Number} x - horizontal position
30719     * @param {Number} y - vertical position
30720     * @param {Boolean} isInstant - disables transitions
30721     */
30722     _processVerticalLayoutQueue : function( queue, isInstant )
30723     {
30724         var pos = this.el.getBox(true);
30725         var x = pos.x;
30726         var y = pos.y;
30727         var maxY = [];
30728         
30729         for (var i = 0; i < this.cols; i++){
30730             maxY[i] = pos.y;
30731         }
30732         
30733         Roo.each(queue, function(box, k){
30734             
30735             var col = k % this.cols;
30736             
30737             Roo.each(box, function(b,kk){
30738                 
30739                 b.el.position('absolute');
30740                 
30741                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30742                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30743                 
30744                 if(b.size == 'md-left' || b.size == 'md-right'){
30745                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30746                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30747                 }
30748                 
30749                 b.el.setWidth(width);
30750                 b.el.setHeight(height);
30751                 // iframe?
30752                 b.el.select('iframe',true).setSize(width,height);
30753                 
30754             }, this);
30755             
30756             for (var i = 0; i < this.cols; i++){
30757                 
30758                 if(maxY[i] < maxY[col]){
30759                     col = i;
30760                     continue;
30761                 }
30762                 
30763                 col = Math.min(col, i);
30764                 
30765             }
30766             
30767             x = pos.x + col * (this.colWidth + this.padWidth);
30768             
30769             y = maxY[col];
30770             
30771             var positions = [];
30772             
30773             switch (box.length){
30774                 case 1 :
30775                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30776                     break;
30777                 case 2 :
30778                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30779                     break;
30780                 case 3 :
30781                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30782                     break;
30783                 case 4 :
30784                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30785                     break;
30786                 default :
30787                     break;
30788             }
30789             
30790             Roo.each(box, function(b,kk){
30791                 
30792                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30793                 
30794                 var sz = b.el.getSize();
30795                 
30796                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30797                 
30798             }, this);
30799             
30800         }, this);
30801         
30802         var mY = 0;
30803         
30804         for (var i = 0; i < this.cols; i++){
30805             mY = Math.max(mY, maxY[i]);
30806         }
30807         
30808         this.el.setHeight(mY - pos.y);
30809         
30810     },
30811     
30812 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30813 //    {
30814 //        var pos = this.el.getBox(true);
30815 //        var x = pos.x;
30816 //        var y = pos.y;
30817 //        var maxX = pos.right;
30818 //        
30819 //        var maxHeight = 0;
30820 //        
30821 //        Roo.each(items, function(item, k){
30822 //            
30823 //            var c = k % 2;
30824 //            
30825 //            item.el.position('absolute');
30826 //                
30827 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30828 //
30829 //            item.el.setWidth(width);
30830 //
30831 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30832 //
30833 //            item.el.setHeight(height);
30834 //            
30835 //            if(c == 0){
30836 //                item.el.setXY([x, y], isInstant ? false : true);
30837 //            } else {
30838 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30839 //            }
30840 //            
30841 //            y = y + height + this.alternativePadWidth;
30842 //            
30843 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30844 //            
30845 //        }, this);
30846 //        
30847 //        this.el.setHeight(maxHeight);
30848 //        
30849 //    },
30850     
30851     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30852     {
30853         var pos = this.el.getBox(true);
30854         
30855         var minX = pos.x;
30856         var minY = pos.y;
30857         
30858         var maxX = pos.right;
30859         
30860         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30861         
30862         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30863         
30864         Roo.each(queue, function(box, k){
30865             
30866             Roo.each(box, function(b, kk){
30867                 
30868                 b.el.position('absolute');
30869                 
30870                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30871                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30872                 
30873                 if(b.size == 'md-left' || b.size == 'md-right'){
30874                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30875                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30876                 }
30877                 
30878                 b.el.setWidth(width);
30879                 b.el.setHeight(height);
30880                 
30881             }, this);
30882             
30883             if(!box.length){
30884                 return;
30885             }
30886             
30887             var positions = [];
30888             
30889             switch (box.length){
30890                 case 1 :
30891                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30892                     break;
30893                 case 2 :
30894                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30895                     break;
30896                 case 3 :
30897                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30898                     break;
30899                 case 4 :
30900                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30901                     break;
30902                 default :
30903                     break;
30904             }
30905             
30906             Roo.each(box, function(b,kk){
30907                 
30908                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30909                 
30910                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30911                 
30912             }, this);
30913             
30914         }, this);
30915         
30916     },
30917     
30918     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30919     {
30920         Roo.each(eItems, function(b,k){
30921             
30922             b.size = (k == 0) ? 'sm' : 'xs';
30923             b.x = (k == 0) ? 2 : 1;
30924             b.y = (k == 0) ? 2 : 1;
30925             
30926             b.el.position('absolute');
30927             
30928             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30929                 
30930             b.el.setWidth(width);
30931             
30932             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30933             
30934             b.el.setHeight(height);
30935             
30936         }, this);
30937
30938         var positions = [];
30939         
30940         positions.push({
30941             x : maxX - this.unitWidth * 2 - this.gutter,
30942             y : minY
30943         });
30944         
30945         positions.push({
30946             x : maxX - this.unitWidth,
30947             y : minY + (this.unitWidth + this.gutter) * 2
30948         });
30949         
30950         positions.push({
30951             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30952             y : minY
30953         });
30954         
30955         Roo.each(eItems, function(b,k){
30956             
30957             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30958
30959         }, this);
30960         
30961     },
30962     
30963     getVerticalOneBoxColPositions : function(x, y, box)
30964     {
30965         var pos = [];
30966         
30967         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30968         
30969         if(box[0].size == 'md-left'){
30970             rand = 0;
30971         }
30972         
30973         if(box[0].size == 'md-right'){
30974             rand = 1;
30975         }
30976         
30977         pos.push({
30978             x : x + (this.unitWidth + this.gutter) * rand,
30979             y : y
30980         });
30981         
30982         return pos;
30983     },
30984     
30985     getVerticalTwoBoxColPositions : function(x, y, box)
30986     {
30987         var pos = [];
30988         
30989         if(box[0].size == 'xs'){
30990             
30991             pos.push({
30992                 x : x,
30993                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30994             });
30995
30996             pos.push({
30997                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30998                 y : y
30999             });
31000             
31001             return pos;
31002             
31003         }
31004         
31005         pos.push({
31006             x : x,
31007             y : y
31008         });
31009
31010         pos.push({
31011             x : x + (this.unitWidth + this.gutter) * 2,
31012             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31013         });
31014         
31015         return pos;
31016         
31017     },
31018     
31019     getVerticalThreeBoxColPositions : function(x, y, box)
31020     {
31021         var pos = [];
31022         
31023         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31024             
31025             pos.push({
31026                 x : x,
31027                 y : y
31028             });
31029
31030             pos.push({
31031                 x : x + (this.unitWidth + this.gutter) * 1,
31032                 y : y
31033             });
31034             
31035             pos.push({
31036                 x : x + (this.unitWidth + this.gutter) * 2,
31037                 y : y
31038             });
31039             
31040             return pos;
31041             
31042         }
31043         
31044         if(box[0].size == 'xs' && box[1].size == 'xs'){
31045             
31046             pos.push({
31047                 x : x,
31048                 y : y
31049             });
31050
31051             pos.push({
31052                 x : x,
31053                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31054             });
31055             
31056             pos.push({
31057                 x : x + (this.unitWidth + this.gutter) * 1,
31058                 y : y
31059             });
31060             
31061             return pos;
31062             
31063         }
31064         
31065         pos.push({
31066             x : x,
31067             y : y
31068         });
31069
31070         pos.push({
31071             x : x + (this.unitWidth + this.gutter) * 2,
31072             y : y
31073         });
31074
31075         pos.push({
31076             x : x + (this.unitWidth + this.gutter) * 2,
31077             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31078         });
31079             
31080         return pos;
31081         
31082     },
31083     
31084     getVerticalFourBoxColPositions : function(x, y, box)
31085     {
31086         var pos = [];
31087         
31088         if(box[0].size == 'xs'){
31089             
31090             pos.push({
31091                 x : x,
31092                 y : y
31093             });
31094
31095             pos.push({
31096                 x : x,
31097                 y : y + (this.unitHeight + this.gutter) * 1
31098             });
31099             
31100             pos.push({
31101                 x : x,
31102                 y : y + (this.unitHeight + this.gutter) * 2
31103             });
31104             
31105             pos.push({
31106                 x : x + (this.unitWidth + this.gutter) * 1,
31107                 y : y
31108             });
31109             
31110             return pos;
31111             
31112         }
31113         
31114         pos.push({
31115             x : x,
31116             y : y
31117         });
31118
31119         pos.push({
31120             x : x + (this.unitWidth + this.gutter) * 2,
31121             y : y
31122         });
31123
31124         pos.push({
31125             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31126             y : y + (this.unitHeight + this.gutter) * 1
31127         });
31128
31129         pos.push({
31130             x : x + (this.unitWidth + this.gutter) * 2,
31131             y : y + (this.unitWidth + this.gutter) * 2
31132         });
31133
31134         return pos;
31135         
31136     },
31137     
31138     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31139     {
31140         var pos = [];
31141         
31142         if(box[0].size == 'md-left'){
31143             pos.push({
31144                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31145                 y : minY
31146             });
31147             
31148             return pos;
31149         }
31150         
31151         if(box[0].size == 'md-right'){
31152             pos.push({
31153                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31154                 y : minY + (this.unitWidth + this.gutter) * 1
31155             });
31156             
31157             return pos;
31158         }
31159         
31160         var rand = Math.floor(Math.random() * (4 - box[0].y));
31161         
31162         pos.push({
31163             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31164             y : minY + (this.unitWidth + this.gutter) * rand
31165         });
31166         
31167         return pos;
31168         
31169     },
31170     
31171     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31172     {
31173         var pos = [];
31174         
31175         if(box[0].size == 'xs'){
31176             
31177             pos.push({
31178                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31179                 y : minY
31180             });
31181
31182             pos.push({
31183                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31184                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31185             });
31186             
31187             return pos;
31188             
31189         }
31190         
31191         pos.push({
31192             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31193             y : minY
31194         });
31195
31196         pos.push({
31197             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31198             y : minY + (this.unitWidth + this.gutter) * 2
31199         });
31200         
31201         return pos;
31202         
31203     },
31204     
31205     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31206     {
31207         var pos = [];
31208         
31209         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31210             
31211             pos.push({
31212                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31213                 y : minY
31214             });
31215
31216             pos.push({
31217                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31218                 y : minY + (this.unitWidth + this.gutter) * 1
31219             });
31220             
31221             pos.push({
31222                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31223                 y : minY + (this.unitWidth + this.gutter) * 2
31224             });
31225             
31226             return pos;
31227             
31228         }
31229         
31230         if(box[0].size == 'xs' && box[1].size == 'xs'){
31231             
31232             pos.push({
31233                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31234                 y : minY
31235             });
31236
31237             pos.push({
31238                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31239                 y : minY
31240             });
31241             
31242             pos.push({
31243                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31244                 y : minY + (this.unitWidth + this.gutter) * 1
31245             });
31246             
31247             return pos;
31248             
31249         }
31250         
31251         pos.push({
31252             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31253             y : minY
31254         });
31255
31256         pos.push({
31257             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31258             y : minY + (this.unitWidth + this.gutter) * 2
31259         });
31260
31261         pos.push({
31262             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31263             y : minY + (this.unitWidth + this.gutter) * 2
31264         });
31265             
31266         return pos;
31267         
31268     },
31269     
31270     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31271     {
31272         var pos = [];
31273         
31274         if(box[0].size == 'xs'){
31275             
31276             pos.push({
31277                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31278                 y : minY
31279             });
31280
31281             pos.push({
31282                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31283                 y : minY
31284             });
31285             
31286             pos.push({
31287                 x : maxX - this.unitWidth * box[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),
31288                 y : minY
31289             });
31290             
31291             pos.push({
31292                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31293                 y : minY + (this.unitWidth + this.gutter) * 1
31294             });
31295             
31296             return pos;
31297             
31298         }
31299         
31300         pos.push({
31301             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31302             y : minY
31303         });
31304         
31305         pos.push({
31306             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31307             y : minY + (this.unitWidth + this.gutter) * 2
31308         });
31309         
31310         pos.push({
31311             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31312             y : minY + (this.unitWidth + this.gutter) * 2
31313         });
31314         
31315         pos.push({
31316             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),
31317             y : minY + (this.unitWidth + this.gutter) * 2
31318         });
31319
31320         return pos;
31321         
31322     },
31323     
31324     /**
31325     * remove a Masonry Brick
31326     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31327     */
31328     removeBrick : function(brick_id)
31329     {
31330         if (!brick_id) {
31331             return;
31332         }
31333         
31334         for (var i = 0; i<this.bricks.length; i++) {
31335             if (this.bricks[i].id == brick_id) {
31336                 this.bricks.splice(i,1);
31337                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31338                 this.initial();
31339             }
31340         }
31341     },
31342     
31343     /**
31344     * adds a Masonry Brick
31345     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31346     */
31347     addBrick : function(cfg)
31348     {
31349         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31350         //this.register(cn);
31351         cn.parentId = this.id;
31352         cn.onRender(this.el, null);
31353         return cn;
31354     },
31355     
31356     /**
31357     * register a Masonry Brick
31358     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31359     */
31360     
31361     register : function(brick)
31362     {
31363         this.bricks.push(brick);
31364         brick.masonryId = this.id;
31365     },
31366     
31367     /**
31368     * clear all the Masonry Brick
31369     */
31370     clearAll : function()
31371     {
31372         this.bricks = [];
31373         //this.getChildContainer().dom.innerHTML = "";
31374         this.el.dom.innerHTML = '';
31375     },
31376     
31377     getSelected : function()
31378     {
31379         if (!this.selectedBrick) {
31380             return false;
31381         }
31382         
31383         return this.selectedBrick;
31384     }
31385 });
31386
31387 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31388     
31389     groups: {},
31390      /**
31391     * register a Masonry Layout
31392     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31393     */
31394     
31395     register : function(layout)
31396     {
31397         this.groups[layout.id] = layout;
31398     },
31399     /**
31400     * fetch a  Masonry Layout based on the masonry layout ID
31401     * @param {string} the masonry layout to add
31402     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31403     */
31404     
31405     get: function(layout_id) {
31406         if (typeof(this.groups[layout_id]) == 'undefined') {
31407             return false;
31408         }
31409         return this.groups[layout_id] ;
31410     }
31411     
31412     
31413     
31414 });
31415
31416  
31417
31418  /**
31419  *
31420  * This is based on 
31421  * http://masonry.desandro.com
31422  *
31423  * The idea is to render all the bricks based on vertical width...
31424  *
31425  * The original code extends 'outlayer' - we might need to use that....
31426  * 
31427  */
31428
31429
31430 /**
31431  * @class Roo.bootstrap.LayoutMasonryAuto
31432  * @extends Roo.bootstrap.Component
31433  * Bootstrap Layout Masonry class
31434  * 
31435  * @constructor
31436  * Create a new Element
31437  * @param {Object} config The config object
31438  */
31439
31440 Roo.bootstrap.LayoutMasonryAuto = function(config){
31441     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31442 };
31443
31444 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31445     
31446       /**
31447      * @cfg {Boolean} isFitWidth  - resize the width..
31448      */   
31449     isFitWidth : false,  // options..
31450     /**
31451      * @cfg {Boolean} isOriginLeft = left align?
31452      */   
31453     isOriginLeft : true,
31454     /**
31455      * @cfg {Boolean} isOriginTop = top align?
31456      */   
31457     isOriginTop : false,
31458     /**
31459      * @cfg {Boolean} isLayoutInstant = no animation?
31460      */   
31461     isLayoutInstant : false, // needed?
31462     /**
31463      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31464      */   
31465     isResizingContainer : true,
31466     /**
31467      * @cfg {Number} columnWidth  width of the columns 
31468      */   
31469     
31470     columnWidth : 0,
31471     
31472     /**
31473      * @cfg {Number} maxCols maximum number of columns
31474      */   
31475     
31476     maxCols: 0,
31477     /**
31478      * @cfg {Number} padHeight padding below box..
31479      */   
31480     
31481     padHeight : 10, 
31482     
31483     /**
31484      * @cfg {Boolean} isAutoInitial defalut true
31485      */   
31486     
31487     isAutoInitial : true, 
31488     
31489     // private?
31490     gutter : 0,
31491     
31492     containerWidth: 0,
31493     initialColumnWidth : 0,
31494     currentSize : null,
31495     
31496     colYs : null, // array.
31497     maxY : 0,
31498     padWidth: 10,
31499     
31500     
31501     tag: 'div',
31502     cls: '',
31503     bricks: null, //CompositeElement
31504     cols : 0, // array?
31505     // element : null, // wrapped now this.el
31506     _isLayoutInited : null, 
31507     
31508     
31509     getAutoCreate : function(){
31510         
31511         var cfg = {
31512             tag: this.tag,
31513             cls: 'blog-masonary-wrapper ' + this.cls,
31514             cn : {
31515                 cls : 'mas-boxes masonary'
31516             }
31517         };
31518         
31519         return cfg;
31520     },
31521     
31522     getChildContainer: function( )
31523     {
31524         if (this.boxesEl) {
31525             return this.boxesEl;
31526         }
31527         
31528         this.boxesEl = this.el.select('.mas-boxes').first();
31529         
31530         return this.boxesEl;
31531     },
31532     
31533     
31534     initEvents : function()
31535     {
31536         var _this = this;
31537         
31538         if(this.isAutoInitial){
31539             Roo.log('hook children rendered');
31540             this.on('childrenrendered', function() {
31541                 Roo.log('children rendered');
31542                 _this.initial();
31543             } ,this);
31544         }
31545         
31546     },
31547     
31548     initial : function()
31549     {
31550         this.reloadItems();
31551
31552         this.currentSize = this.el.getBox(true);
31553
31554         /// was window resize... - let's see if this works..
31555         Roo.EventManager.onWindowResize(this.resize, this); 
31556
31557         if(!this.isAutoInitial){
31558             this.layout();
31559             return;
31560         }
31561         
31562         this.layout.defer(500,this);
31563     },
31564     
31565     reloadItems: function()
31566     {
31567         this.bricks = this.el.select('.masonry-brick', true);
31568         
31569         this.bricks.each(function(b) {
31570             //Roo.log(b.getSize());
31571             if (!b.attr('originalwidth')) {
31572                 b.attr('originalwidth',  b.getSize().width);
31573             }
31574             
31575         });
31576         
31577         Roo.log(this.bricks.elements.length);
31578     },
31579     
31580     resize : function()
31581     {
31582         Roo.log('resize');
31583         var cs = this.el.getBox(true);
31584         
31585         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31586             Roo.log("no change in with or X");
31587             return;
31588         }
31589         this.currentSize = cs;
31590         this.layout();
31591     },
31592     
31593     layout : function()
31594     {
31595          Roo.log('layout');
31596         this._resetLayout();
31597         //this._manageStamps();
31598       
31599         // don't animate first layout
31600         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31601         this.layoutItems( isInstant );
31602       
31603         // flag for initalized
31604         this._isLayoutInited = true;
31605     },
31606     
31607     layoutItems : function( isInstant )
31608     {
31609         //var items = this._getItemsForLayout( this.items );
31610         // original code supports filtering layout items.. we just ignore it..
31611         
31612         this._layoutItems( this.bricks , isInstant );
31613       
31614         this._postLayout();
31615     },
31616     _layoutItems : function ( items , isInstant)
31617     {
31618        //this.fireEvent( 'layout', this, items );
31619     
31620
31621         if ( !items || !items.elements.length ) {
31622           // no items, emit event with empty array
31623             return;
31624         }
31625
31626         var queue = [];
31627         items.each(function(item) {
31628             Roo.log("layout item");
31629             Roo.log(item);
31630             // get x/y object from method
31631             var position = this._getItemLayoutPosition( item );
31632             // enqueue
31633             position.item = item;
31634             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31635             queue.push( position );
31636         }, this);
31637       
31638         this._processLayoutQueue( queue );
31639     },
31640     /** Sets position of item in DOM
31641     * @param {Element} item
31642     * @param {Number} x - horizontal position
31643     * @param {Number} y - vertical position
31644     * @param {Boolean} isInstant - disables transitions
31645     */
31646     _processLayoutQueue : function( queue )
31647     {
31648         for ( var i=0, len = queue.length; i < len; i++ ) {
31649             var obj = queue[i];
31650             obj.item.position('absolute');
31651             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31652         }
31653     },
31654       
31655     
31656     /**
31657     * Any logic you want to do after each layout,
31658     * i.e. size the container
31659     */
31660     _postLayout : function()
31661     {
31662         this.resizeContainer();
31663     },
31664     
31665     resizeContainer : function()
31666     {
31667         if ( !this.isResizingContainer ) {
31668             return;
31669         }
31670         var size = this._getContainerSize();
31671         if ( size ) {
31672             this.el.setSize(size.width,size.height);
31673             this.boxesEl.setSize(size.width,size.height);
31674         }
31675     },
31676     
31677     
31678     
31679     _resetLayout : function()
31680     {
31681         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31682         this.colWidth = this.el.getWidth();
31683         //this.gutter = this.el.getWidth(); 
31684         
31685         this.measureColumns();
31686
31687         // reset column Y
31688         var i = this.cols;
31689         this.colYs = [];
31690         while (i--) {
31691             this.colYs.push( 0 );
31692         }
31693     
31694         this.maxY = 0;
31695     },
31696
31697     measureColumns : function()
31698     {
31699         this.getContainerWidth();
31700       // if columnWidth is 0, default to outerWidth of first item
31701         if ( !this.columnWidth ) {
31702             var firstItem = this.bricks.first();
31703             Roo.log(firstItem);
31704             this.columnWidth  = this.containerWidth;
31705             if (firstItem && firstItem.attr('originalwidth') ) {
31706                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31707             }
31708             // columnWidth fall back to item of first element
31709             Roo.log("set column width?");
31710                         this.initialColumnWidth = this.columnWidth  ;
31711
31712             // if first elem has no width, default to size of container
31713             
31714         }
31715         
31716         
31717         if (this.initialColumnWidth) {
31718             this.columnWidth = this.initialColumnWidth;
31719         }
31720         
31721         
31722             
31723         // column width is fixed at the top - however if container width get's smaller we should
31724         // reduce it...
31725         
31726         // this bit calcs how man columns..
31727             
31728         var columnWidth = this.columnWidth += this.gutter;
31729       
31730         // calculate columns
31731         var containerWidth = this.containerWidth + this.gutter;
31732         
31733         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31734         // fix rounding errors, typically with gutters
31735         var excess = columnWidth - containerWidth % columnWidth;
31736         
31737         
31738         // if overshoot is less than a pixel, round up, otherwise floor it
31739         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31740         cols = Math[ mathMethod ]( cols );
31741         this.cols = Math.max( cols, 1 );
31742         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31743         
31744          // padding positioning..
31745         var totalColWidth = this.cols * this.columnWidth;
31746         var padavail = this.containerWidth - totalColWidth;
31747         // so for 2 columns - we need 3 'pads'
31748         
31749         var padNeeded = (1+this.cols) * this.padWidth;
31750         
31751         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31752         
31753         this.columnWidth += padExtra
31754         //this.padWidth = Math.floor(padavail /  ( this.cols));
31755         
31756         // adjust colum width so that padding is fixed??
31757         
31758         // we have 3 columns ... total = width * 3
31759         // we have X left over... that should be used by 
31760         
31761         //if (this.expandC) {
31762             
31763         //}
31764         
31765         
31766         
31767     },
31768     
31769     getContainerWidth : function()
31770     {
31771        /* // container is parent if fit width
31772         var container = this.isFitWidth ? this.element.parentNode : this.element;
31773         // check that this.size and size are there
31774         // IE8 triggers resize on body size change, so they might not be
31775         
31776         var size = getSize( container );  //FIXME
31777         this.containerWidth = size && size.innerWidth; //FIXME
31778         */
31779          
31780         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31781         
31782     },
31783     
31784     _getItemLayoutPosition : function( item )  // what is item?
31785     {
31786         // we resize the item to our columnWidth..
31787       
31788         item.setWidth(this.columnWidth);
31789         item.autoBoxAdjust  = false;
31790         
31791         var sz = item.getSize();
31792  
31793         // how many columns does this brick span
31794         var remainder = this.containerWidth % this.columnWidth;
31795         
31796         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31797         // round if off by 1 pixel, otherwise use ceil
31798         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31799         colSpan = Math.min( colSpan, this.cols );
31800         
31801         // normally this should be '1' as we dont' currently allow multi width columns..
31802         
31803         var colGroup = this._getColGroup( colSpan );
31804         // get the minimum Y value from the columns
31805         var minimumY = Math.min.apply( Math, colGroup );
31806         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31807         
31808         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31809          
31810         // position the brick
31811         var position = {
31812             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31813             y: this.currentSize.y + minimumY + this.padHeight
31814         };
31815         
31816         Roo.log(position);
31817         // apply setHeight to necessary columns
31818         var setHeight = minimumY + sz.height + this.padHeight;
31819         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31820         
31821         var setSpan = this.cols + 1 - colGroup.length;
31822         for ( var i = 0; i < setSpan; i++ ) {
31823           this.colYs[ shortColIndex + i ] = setHeight ;
31824         }
31825       
31826         return position;
31827     },
31828     
31829     /**
31830      * @param {Number} colSpan - number of columns the element spans
31831      * @returns {Array} colGroup
31832      */
31833     _getColGroup : function( colSpan )
31834     {
31835         if ( colSpan < 2 ) {
31836           // if brick spans only one column, use all the column Ys
31837           return this.colYs;
31838         }
31839       
31840         var colGroup = [];
31841         // how many different places could this brick fit horizontally
31842         var groupCount = this.cols + 1 - colSpan;
31843         // for each group potential horizontal position
31844         for ( var i = 0; i < groupCount; i++ ) {
31845           // make an array of colY values for that one group
31846           var groupColYs = this.colYs.slice( i, i + colSpan );
31847           // and get the max value of the array
31848           colGroup[i] = Math.max.apply( Math, groupColYs );
31849         }
31850         return colGroup;
31851     },
31852     /*
31853     _manageStamp : function( stamp )
31854     {
31855         var stampSize =  stamp.getSize();
31856         var offset = stamp.getBox();
31857         // get the columns that this stamp affects
31858         var firstX = this.isOriginLeft ? offset.x : offset.right;
31859         var lastX = firstX + stampSize.width;
31860         var firstCol = Math.floor( firstX / this.columnWidth );
31861         firstCol = Math.max( 0, firstCol );
31862         
31863         var lastCol = Math.floor( lastX / this.columnWidth );
31864         // lastCol should not go over if multiple of columnWidth #425
31865         lastCol -= lastX % this.columnWidth ? 0 : 1;
31866         lastCol = Math.min( this.cols - 1, lastCol );
31867         
31868         // set colYs to bottom of the stamp
31869         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31870             stampSize.height;
31871             
31872         for ( var i = firstCol; i <= lastCol; i++ ) {
31873           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31874         }
31875     },
31876     */
31877     
31878     _getContainerSize : function()
31879     {
31880         this.maxY = Math.max.apply( Math, this.colYs );
31881         var size = {
31882             height: this.maxY
31883         };
31884       
31885         if ( this.isFitWidth ) {
31886             size.width = this._getContainerFitWidth();
31887         }
31888       
31889         return size;
31890     },
31891     
31892     _getContainerFitWidth : function()
31893     {
31894         var unusedCols = 0;
31895         // count unused columns
31896         var i = this.cols;
31897         while ( --i ) {
31898           if ( this.colYs[i] !== 0 ) {
31899             break;
31900           }
31901           unusedCols++;
31902         }
31903         // fit container to columns that have been used
31904         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31905     },
31906     
31907     needsResizeLayout : function()
31908     {
31909         var previousWidth = this.containerWidth;
31910         this.getContainerWidth();
31911         return previousWidth !== this.containerWidth;
31912     }
31913  
31914 });
31915
31916  
31917
31918  /*
31919  * - LGPL
31920  *
31921  * element
31922  * 
31923  */
31924
31925 /**
31926  * @class Roo.bootstrap.MasonryBrick
31927  * @extends Roo.bootstrap.Component
31928  * Bootstrap MasonryBrick class
31929  * 
31930  * @constructor
31931  * Create a new MasonryBrick
31932  * @param {Object} config The config object
31933  */
31934
31935 Roo.bootstrap.MasonryBrick = function(config){
31936     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31937     
31938     this.addEvents({
31939         // raw events
31940         /**
31941          * @event click
31942          * When a MasonryBrick is clcik
31943          * @param {Roo.bootstrap.MasonryBrick} this
31944          * @param {Roo.EventObject} e
31945          */
31946         "click" : true
31947     });
31948 };
31949
31950 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31951     
31952     /**
31953      * @cfg {String} title
31954      */   
31955     title : '',
31956     /**
31957      * @cfg {String} html
31958      */   
31959     html : '',
31960     /**
31961      * @cfg {String} bgimage
31962      */   
31963     bgimage : '',
31964     /**
31965      * @cfg {String} videourl
31966      */   
31967     videourl : '',
31968     /**
31969      * @cfg {String} cls
31970      */   
31971     cls : '',
31972     /**
31973      * @cfg {String} href
31974      */   
31975     href : '',
31976     /**
31977      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31978      */   
31979     size : 'xs',
31980     
31981     /**
31982      * @cfg {String} placetitle (center|bottom)
31983      */   
31984     placetitle : '',
31985     
31986     /**
31987      * @cfg {Boolean} isFitContainer defalut true
31988      */   
31989     isFitContainer : true, 
31990     
31991     /**
31992      * @cfg {Boolean} preventDefault defalut false
31993      */   
31994     preventDefault : false, 
31995     
31996     /**
31997      * @cfg {Boolean} inverse defalut false
31998      */   
31999     maskInverse : false, 
32000     
32001     getAutoCreate : function()
32002     {
32003         if(!this.isFitContainer){
32004             return this.getSplitAutoCreate();
32005         }
32006         
32007         var cls = 'masonry-brick masonry-brick-full';
32008         
32009         if(this.href.length){
32010             cls += ' masonry-brick-link';
32011         }
32012         
32013         if(this.bgimage.length){
32014             cls += ' masonry-brick-image';
32015         }
32016         
32017         if(this.maskInverse){
32018             cls += ' mask-inverse';
32019         }
32020         
32021         if(!this.html.length && !this.maskInverse){
32022             cls += ' enable-mask';
32023         }
32024         
32025         if(this.size){
32026             cls += ' masonry-' + this.size + '-brick';
32027         }
32028         
32029         if(this.placetitle.length){
32030             
32031             switch (this.placetitle) {
32032                 case 'center' :
32033                     cls += ' masonry-center-title';
32034                     break;
32035                 case 'bottom' :
32036                     cls += ' masonry-bottom-title';
32037                     break;
32038                 default:
32039                     break;
32040             }
32041             
32042         } else {
32043             if(!this.html.length && !this.bgimage.length){
32044                 cls += ' masonry-center-title';
32045             }
32046
32047             if(!this.html.length && this.bgimage.length){
32048                 cls += ' masonry-bottom-title';
32049             }
32050         }
32051         
32052         if(this.cls){
32053             cls += ' ' + this.cls;
32054         }
32055         
32056         var cfg = {
32057             tag: (this.href.length) ? 'a' : 'div',
32058             cls: cls,
32059             cn: [
32060                 {
32061                     tag: 'div',
32062                     cls: 'masonry-brick-paragraph',
32063                     cn: []
32064                 }
32065             ]
32066         };
32067         
32068         if(this.href.length){
32069             cfg.href = this.href;
32070         }
32071         
32072         var cn = cfg.cn[0].cn;
32073         
32074         if(this.title.length){
32075             cn.push({
32076                 tag: 'h4',
32077                 cls: 'masonry-brick-title',
32078                 html: this.title
32079             });
32080         }
32081         
32082         if(this.html.length){
32083             cn.push({
32084                 tag: 'p',
32085                 cls: 'masonry-brick-text',
32086                 html: this.html
32087             });
32088         }  
32089         if (!this.title.length && !this.html.length) {
32090             cfg.cn[0].cls += ' hide';
32091         }
32092         
32093         if(this.bgimage.length){
32094             cfg.cn.push({
32095                 tag: 'img',
32096                 cls: 'masonry-brick-image-view',
32097                 src: this.bgimage
32098             });
32099         }
32100         
32101         if(this.videourl.length){
32102             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32103             // youtube support only?
32104             cfg.cn.push({
32105                 tag: 'iframe',
32106                 cls: 'masonry-brick-image-view',
32107                 src: vurl,
32108                 frameborder : 0,
32109                 allowfullscreen : true
32110             });
32111             
32112             
32113         }
32114         
32115         cfg.cn.push({
32116             tag: 'div',
32117             cls: 'masonry-brick-mask'
32118         });
32119         
32120         return cfg;
32121         
32122     },
32123     
32124     getSplitAutoCreate : function()
32125     {
32126         var cls = 'masonry-brick masonry-brick-split';
32127         
32128         if(this.href.length){
32129             cls += ' masonry-brick-link';
32130         }
32131         
32132         if(this.bgimage.length){
32133             cls += ' masonry-brick-image';
32134         }
32135         
32136         if(this.size){
32137             cls += ' masonry-' + this.size + '-brick';
32138         }
32139         
32140         switch (this.placetitle) {
32141             case 'center' :
32142                 cls += ' masonry-center-title';
32143                 break;
32144             case 'bottom' :
32145                 cls += ' masonry-bottom-title';
32146                 break;
32147             default:
32148                 if(!this.bgimage.length){
32149                     cls += ' masonry-center-title';
32150                 }
32151
32152                 if(this.bgimage.length){
32153                     cls += ' masonry-bottom-title';
32154                 }
32155                 break;
32156         }
32157         
32158         if(this.cls){
32159             cls += ' ' + this.cls;
32160         }
32161         
32162         var cfg = {
32163             tag: (this.href.length) ? 'a' : 'div',
32164             cls: cls,
32165             cn: [
32166                 {
32167                     tag: 'div',
32168                     cls: 'masonry-brick-split-head',
32169                     cn: [
32170                         {
32171                             tag: 'div',
32172                             cls: 'masonry-brick-paragraph',
32173                             cn: []
32174                         }
32175                     ]
32176                 },
32177                 {
32178                     tag: 'div',
32179                     cls: 'masonry-brick-split-body',
32180                     cn: []
32181                 }
32182             ]
32183         };
32184         
32185         if(this.href.length){
32186             cfg.href = this.href;
32187         }
32188         
32189         if(this.title.length){
32190             cfg.cn[0].cn[0].cn.push({
32191                 tag: 'h4',
32192                 cls: 'masonry-brick-title',
32193                 html: this.title
32194             });
32195         }
32196         
32197         if(this.html.length){
32198             cfg.cn[1].cn.push({
32199                 tag: 'p',
32200                 cls: 'masonry-brick-text',
32201                 html: this.html
32202             });
32203         }
32204
32205         if(this.bgimage.length){
32206             cfg.cn[0].cn.push({
32207                 tag: 'img',
32208                 cls: 'masonry-brick-image-view',
32209                 src: this.bgimage
32210             });
32211         }
32212         
32213         if(this.videourl.length){
32214             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32215             // youtube support only?
32216             cfg.cn[0].cn.cn.push({
32217                 tag: 'iframe',
32218                 cls: 'masonry-brick-image-view',
32219                 src: vurl,
32220                 frameborder : 0,
32221                 allowfullscreen : true
32222             });
32223         }
32224         
32225         return cfg;
32226     },
32227     
32228     initEvents: function() 
32229     {
32230         switch (this.size) {
32231             case 'xs' :
32232                 this.x = 1;
32233                 this.y = 1;
32234                 break;
32235             case 'sm' :
32236                 this.x = 2;
32237                 this.y = 2;
32238                 break;
32239             case 'md' :
32240             case 'md-left' :
32241             case 'md-right' :
32242                 this.x = 3;
32243                 this.y = 3;
32244                 break;
32245             case 'tall' :
32246                 this.x = 2;
32247                 this.y = 3;
32248                 break;
32249             case 'wide' :
32250                 this.x = 3;
32251                 this.y = 2;
32252                 break;
32253             case 'wide-thin' :
32254                 this.x = 3;
32255                 this.y = 1;
32256                 break;
32257                         
32258             default :
32259                 break;
32260         }
32261         
32262         if(Roo.isTouch){
32263             this.el.on('touchstart', this.onTouchStart, this);
32264             this.el.on('touchmove', this.onTouchMove, this);
32265             this.el.on('touchend', this.onTouchEnd, this);
32266             this.el.on('contextmenu', this.onContextMenu, this);
32267         } else {
32268             this.el.on('mouseenter'  ,this.enter, this);
32269             this.el.on('mouseleave', this.leave, this);
32270             this.el.on('click', this.onClick, this);
32271         }
32272         
32273         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32274             this.parent().bricks.push(this);   
32275         }
32276         
32277     },
32278     
32279     onClick: function(e, el)
32280     {
32281         var time = this.endTimer - this.startTimer;
32282         // Roo.log(e.preventDefault());
32283         if(Roo.isTouch){
32284             if(time > 1000){
32285                 e.preventDefault();
32286                 return;
32287             }
32288         }
32289         
32290         if(!this.preventDefault){
32291             return;
32292         }
32293         
32294         e.preventDefault();
32295         
32296         if (this.activcClass != '') {
32297             this.selectBrick();
32298         }
32299         
32300         this.fireEvent('click', this);
32301     },
32302     
32303     enter: function(e, el)
32304     {
32305         e.preventDefault();
32306         
32307         if(!this.isFitContainer || this.maskInverse){
32308             return;
32309         }
32310         
32311         if(this.bgimage.length && this.html.length){
32312             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32313         }
32314     },
32315     
32316     leave: function(e, el)
32317     {
32318         e.preventDefault();
32319         
32320         if(!this.isFitContainer || this.maskInverse){
32321             return;
32322         }
32323         
32324         if(this.bgimage.length && this.html.length){
32325             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32326         }
32327     },
32328     
32329     onTouchStart: function(e, el)
32330     {
32331 //        e.preventDefault();
32332         
32333         this.touchmoved = false;
32334         
32335         if(!this.isFitContainer){
32336             return;
32337         }
32338         
32339         if(!this.bgimage.length || !this.html.length){
32340             return;
32341         }
32342         
32343         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32344         
32345         this.timer = new Date().getTime();
32346         
32347     },
32348     
32349     onTouchMove: function(e, el)
32350     {
32351         this.touchmoved = true;
32352     },
32353     
32354     onContextMenu : function(e,el)
32355     {
32356         e.preventDefault();
32357         e.stopPropagation();
32358         return false;
32359     },
32360     
32361     onTouchEnd: function(e, el)
32362     {
32363 //        e.preventDefault();
32364         
32365         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32366         
32367             this.leave(e,el);
32368             
32369             return;
32370         }
32371         
32372         if(!this.bgimage.length || !this.html.length){
32373             
32374             if(this.href.length){
32375                 window.location.href = this.href;
32376             }
32377             
32378             return;
32379         }
32380         
32381         if(!this.isFitContainer){
32382             return;
32383         }
32384         
32385         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32386         
32387         window.location.href = this.href;
32388     },
32389     
32390     selectBrick : function() {
32391         
32392         if (!this.parentId) {
32393             return;
32394         }
32395         
32396         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32397         var index = m.selectedBrick.indexOf(this.id);
32398         
32399         if ( index > -1) {
32400             m.selectedBrick.splice(index,1);
32401             this.el.removeClass(this.activeClass);
32402             return;
32403         }
32404         
32405         m.selectedBrick.push(this.id);
32406         this.el.addClass(this.activeClass);
32407         return;
32408     }
32409     
32410 });
32411
32412  
32413
32414  /*
32415  * - LGPL
32416  *
32417  * element
32418  * 
32419  */
32420
32421 /**
32422  * @class Roo.bootstrap.Brick
32423  * @extends Roo.bootstrap.Component
32424  * Bootstrap Brick class
32425  * 
32426  * @constructor
32427  * Create a new Brick
32428  * @param {Object} config The config object
32429  */
32430
32431 Roo.bootstrap.Brick = function(config){
32432     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32433     
32434     this.addEvents({
32435         // raw events
32436         /**
32437          * @event click
32438          * When a Brick is click
32439          * @param {Roo.bootstrap.Brick} this
32440          * @param {Roo.EventObject} e
32441          */
32442         "click" : true
32443     });
32444 };
32445
32446 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32447     
32448     /**
32449      * @cfg {String} title
32450      */   
32451     title : '',
32452     /**
32453      * @cfg {String} html
32454      */   
32455     html : '',
32456     /**
32457      * @cfg {String} bgimage
32458      */   
32459     bgimage : '',
32460     /**
32461      * @cfg {String} cls
32462      */   
32463     cls : '',
32464     /**
32465      * @cfg {String} href
32466      */   
32467     href : '',
32468     /**
32469      * @cfg {String} video
32470      */   
32471     video : '',
32472     /**
32473      * @cfg {Boolean} square
32474      */   
32475     square : true,
32476     
32477     getAutoCreate : function()
32478     {
32479         var cls = 'roo-brick';
32480         
32481         if(this.href.length){
32482             cls += ' roo-brick-link';
32483         }
32484         
32485         if(this.bgimage.length){
32486             cls += ' roo-brick-image';
32487         }
32488         
32489         if(!this.html.length && !this.bgimage.length){
32490             cls += ' roo-brick-center-title';
32491         }
32492         
32493         if(!this.html.length && this.bgimage.length){
32494             cls += ' roo-brick-bottom-title';
32495         }
32496         
32497         if(this.cls){
32498             cls += ' ' + this.cls;
32499         }
32500         
32501         var cfg = {
32502             tag: (this.href.length) ? 'a' : 'div',
32503             cls: cls,
32504             cn: [
32505                 {
32506                     tag: 'div',
32507                     cls: 'roo-brick-paragraph',
32508                     cn: []
32509                 }
32510             ]
32511         };
32512         
32513         if(this.href.length){
32514             cfg.href = this.href;
32515         }
32516         
32517         var cn = cfg.cn[0].cn;
32518         
32519         if(this.title.length){
32520             cn.push({
32521                 tag: 'h4',
32522                 cls: 'roo-brick-title',
32523                 html: this.title
32524             });
32525         }
32526         
32527         if(this.html.length){
32528             cn.push({
32529                 tag: 'p',
32530                 cls: 'roo-brick-text',
32531                 html: this.html
32532             });
32533         } else {
32534             cn.cls += ' hide';
32535         }
32536         
32537         if(this.bgimage.length){
32538             cfg.cn.push({
32539                 tag: 'img',
32540                 cls: 'roo-brick-image-view',
32541                 src: this.bgimage
32542             });
32543         }
32544         
32545         return cfg;
32546     },
32547     
32548     initEvents: function() 
32549     {
32550         if(this.title.length || this.html.length){
32551             this.el.on('mouseenter'  ,this.enter, this);
32552             this.el.on('mouseleave', this.leave, this);
32553         }
32554         
32555         
32556         Roo.EventManager.onWindowResize(this.resize, this); 
32557         
32558         this.resize();
32559     },
32560     
32561     resize : function()
32562     {
32563         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32564         
32565         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32566         
32567         if(this.bgimage.length){
32568             var image = this.el.select('.roo-brick-image-view', true).first();
32569             image.setWidth(paragraph.getWidth());
32570             image.setHeight(paragraph.getWidth());
32571             
32572             this.el.setHeight(paragraph.getWidth());
32573             
32574         }
32575         
32576     },
32577     
32578     enter: function(e, el)
32579     {
32580         e.preventDefault();
32581         
32582         if(this.bgimage.length){
32583             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32584             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32585         }
32586     },
32587     
32588     leave: function(e, el)
32589     {
32590         e.preventDefault();
32591         
32592         if(this.bgimage.length){
32593             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32594             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32595         }
32596     }
32597     
32598 });
32599
32600  
32601
32602  /*
32603  * - LGPL
32604  *
32605  * Input
32606  * 
32607  */
32608
32609 /**
32610  * @class Roo.bootstrap.NumberField
32611  * @extends Roo.bootstrap.Input
32612  * Bootstrap NumberField class
32613  * 
32614  * 
32615  * 
32616  * 
32617  * @constructor
32618  * Create a new NumberField
32619  * @param {Object} config The config object
32620  */
32621
32622 Roo.bootstrap.NumberField = function(config){
32623     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32624 };
32625
32626 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32627     
32628     /**
32629      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32630      */
32631     allowDecimals : true,
32632     /**
32633      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32634      */
32635     decimalSeparator : ".",
32636     /**
32637      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32638      */
32639     decimalPrecision : 2,
32640     /**
32641      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32642      */
32643     allowNegative : true,
32644     /**
32645      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32646      */
32647     minValue : Number.NEGATIVE_INFINITY,
32648     /**
32649      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32650      */
32651     maxValue : Number.MAX_VALUE,
32652     /**
32653      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32654      */
32655     minText : "The minimum value for this field is {0}",
32656     /**
32657      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32658      */
32659     maxText : "The maximum value for this field is {0}",
32660     /**
32661      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32662      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32663      */
32664     nanText : "{0} is not a valid number",
32665     /**
32666      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32667      */
32668     castInt : true,
32669
32670     // private
32671     initEvents : function()
32672     {   
32673         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32674         
32675         var allowed = "0123456789";
32676         
32677         if(this.allowDecimals){
32678             allowed += this.decimalSeparator;
32679         }
32680         
32681         if(this.allowNegative){
32682             allowed += "-";
32683         }
32684         
32685         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32686         
32687         var keyPress = function(e){
32688             
32689             var k = e.getKey();
32690             
32691             var c = e.getCharCode();
32692             
32693             if(
32694                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32695                     allowed.indexOf(String.fromCharCode(c)) === -1
32696             ){
32697                 e.stopEvent();
32698                 return;
32699             }
32700             
32701             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32702                 return;
32703             }
32704             
32705             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32706                 e.stopEvent();
32707             }
32708         };
32709         
32710         this.el.on("keypress", keyPress, this);
32711     },
32712     
32713     validateValue : function(value)
32714     {
32715         
32716         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32717             return false;
32718         }
32719         
32720         var num = this.parseValue(value);
32721         
32722         if(isNaN(num)){
32723             this.markInvalid(String.format(this.nanText, value));
32724             return false;
32725         }
32726         
32727         if(num < this.minValue){
32728             this.markInvalid(String.format(this.minText, this.minValue));
32729             return false;
32730         }
32731         
32732         if(num > this.maxValue){
32733             this.markInvalid(String.format(this.maxText, this.maxValue));
32734             return false;
32735         }
32736         
32737         return true;
32738     },
32739
32740     getValue : function()
32741     {
32742         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32743     },
32744
32745     parseValue : function(value)
32746     {
32747         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32748         return isNaN(value) ? '' : value;
32749     },
32750
32751     fixPrecision : function(value)
32752     {
32753         var nan = isNaN(value);
32754         
32755         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32756             return nan ? '' : value;
32757         }
32758         return parseFloat(value).toFixed(this.decimalPrecision);
32759     },
32760
32761     setValue : function(v)
32762     {
32763         v = this.fixPrecision(v);
32764         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32765     },
32766
32767     decimalPrecisionFcn : function(v)
32768     {
32769         return Math.floor(v);
32770     },
32771
32772     beforeBlur : function()
32773     {
32774         if(!this.castInt){
32775             return;
32776         }
32777         
32778         var v = this.parseValue(this.getRawValue());
32779         if(v){
32780             this.setValue(v);
32781         }
32782     }
32783     
32784 });
32785
32786  
32787
32788 /*
32789 * Licence: LGPL
32790 */
32791
32792 /**
32793  * @class Roo.bootstrap.DocumentSlider
32794  * @extends Roo.bootstrap.Component
32795  * Bootstrap DocumentSlider class
32796  * 
32797  * @constructor
32798  * Create a new DocumentViewer
32799  * @param {Object} config The config object
32800  */
32801
32802 Roo.bootstrap.DocumentSlider = function(config){
32803     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32804     
32805     this.files = [];
32806     
32807     this.addEvents({
32808         /**
32809          * @event initial
32810          * Fire after initEvent
32811          * @param {Roo.bootstrap.DocumentSlider} this
32812          */
32813         "initial" : true,
32814         /**
32815          * @event update
32816          * Fire after update
32817          * @param {Roo.bootstrap.DocumentSlider} this
32818          */
32819         "update" : true,
32820         /**
32821          * @event click
32822          * Fire after click
32823          * @param {Roo.bootstrap.DocumentSlider} this
32824          */
32825         "click" : true
32826     });
32827 };
32828
32829 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32830     
32831     files : false,
32832     
32833     indicator : 0,
32834     
32835     getAutoCreate : function()
32836     {
32837         var cfg = {
32838             tag : 'div',
32839             cls : 'roo-document-slider',
32840             cn : [
32841                 {
32842                     tag : 'div',
32843                     cls : 'roo-document-slider-header',
32844                     cn : [
32845                         {
32846                             tag : 'div',
32847                             cls : 'roo-document-slider-header-title'
32848                         }
32849                     ]
32850                 },
32851                 {
32852                     tag : 'div',
32853                     cls : 'roo-document-slider-body',
32854                     cn : [
32855                         {
32856                             tag : 'div',
32857                             cls : 'roo-document-slider-prev',
32858                             cn : [
32859                                 {
32860                                     tag : 'i',
32861                                     cls : 'fa fa-chevron-left'
32862                                 }
32863                             ]
32864                         },
32865                         {
32866                             tag : 'div',
32867                             cls : 'roo-document-slider-thumb',
32868                             cn : [
32869                                 {
32870                                     tag : 'img',
32871                                     cls : 'roo-document-slider-image'
32872                                 }
32873                             ]
32874                         },
32875                         {
32876                             tag : 'div',
32877                             cls : 'roo-document-slider-next',
32878                             cn : [
32879                                 {
32880                                     tag : 'i',
32881                                     cls : 'fa fa-chevron-right'
32882                                 }
32883                             ]
32884                         }
32885                     ]
32886                 }
32887             ]
32888         };
32889         
32890         return cfg;
32891     },
32892     
32893     initEvents : function()
32894     {
32895         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32896         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32897         
32898         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32899         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32900         
32901         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32902         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32903         
32904         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32905         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32906         
32907         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32908         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32909         
32910         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32911         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32912         
32913         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32914         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32915         
32916         this.thumbEl.on('click', this.onClick, this);
32917         
32918         this.prevIndicator.on('click', this.prev, this);
32919         
32920         this.nextIndicator.on('click', this.next, this);
32921         
32922     },
32923     
32924     initial : function()
32925     {
32926         if(this.files.length){
32927             this.indicator = 1;
32928             this.update()
32929         }
32930         
32931         this.fireEvent('initial', this);
32932     },
32933     
32934     update : function()
32935     {
32936         this.imageEl.attr('src', this.files[this.indicator - 1]);
32937         
32938         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32939         
32940         this.prevIndicator.show();
32941         
32942         if(this.indicator == 1){
32943             this.prevIndicator.hide();
32944         }
32945         
32946         this.nextIndicator.show();
32947         
32948         if(this.indicator == this.files.length){
32949             this.nextIndicator.hide();
32950         }
32951         
32952         this.thumbEl.scrollTo('top');
32953         
32954         this.fireEvent('update', this);
32955     },
32956     
32957     onClick : function(e)
32958     {
32959         e.preventDefault();
32960         
32961         this.fireEvent('click', this);
32962     },
32963     
32964     prev : function(e)
32965     {
32966         e.preventDefault();
32967         
32968         this.indicator = Math.max(1, this.indicator - 1);
32969         
32970         this.update();
32971     },
32972     
32973     next : function(e)
32974     {
32975         e.preventDefault();
32976         
32977         this.indicator = Math.min(this.files.length, this.indicator + 1);
32978         
32979         this.update();
32980     }
32981 });
32982 /*
32983  * - LGPL
32984  *
32985  * RadioSet
32986  *
32987  *
32988  */
32989
32990 /**
32991  * @class Roo.bootstrap.RadioSet
32992  * @extends Roo.bootstrap.Input
32993  * Bootstrap RadioSet class
32994  * @cfg {String} indicatorpos (left|right) default left
32995  * @cfg {Boolean} inline (true|false) inline the element (default true)
32996  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32997  * @constructor
32998  * Create a new RadioSet
32999  * @param {Object} config The config object
33000  */
33001
33002 Roo.bootstrap.RadioSet = function(config){
33003     
33004     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33005     
33006     this.radioes = [];
33007     
33008     Roo.bootstrap.RadioSet.register(this);
33009     
33010     this.addEvents({
33011         /**
33012         * @event check
33013         * Fires when the element is checked or unchecked.
33014         * @param {Roo.bootstrap.RadioSet} this This radio
33015         * @param {Roo.bootstrap.Radio} item The checked item
33016         */
33017        check : true
33018     });
33019     
33020 };
33021
33022 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33023
33024     radioes : false,
33025     
33026     inline : true,
33027     
33028     weight : '',
33029     
33030     indicatorpos : 'left',
33031     
33032     getAutoCreate : function()
33033     {
33034         var label = {
33035             tag : 'label',
33036             cls : 'roo-radio-set-label',
33037             cn : [
33038                 {
33039                     tag : 'span',
33040                     html : this.fieldLabel
33041                 }
33042             ]
33043         };
33044         
33045         if(this.indicatorpos == 'left'){
33046             label.cn.unshift({
33047                 tag : 'i',
33048                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33049                 tooltip : 'This field is required'
33050             });
33051         } else {
33052             label.cn.push({
33053                 tag : 'i',
33054                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33055                 tooltip : 'This field is required'
33056             });
33057         }
33058         
33059         var items = {
33060             tag : 'div',
33061             cls : 'roo-radio-set-items'
33062         };
33063         
33064         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33065         
33066         if (align === 'left' && this.fieldLabel.length) {
33067             
33068             items = {
33069                 cls : "roo-radio-set-right", 
33070                 cn: [
33071                     items
33072                 ]
33073             };
33074             
33075             if(this.labelWidth > 12){
33076                 label.style = "width: " + this.labelWidth + 'px';
33077             }
33078             
33079             if(this.labelWidth < 13 && this.labelmd == 0){
33080                 this.labelmd = this.labelWidth;
33081             }
33082             
33083             if(this.labellg > 0){
33084                 label.cls += ' col-lg-' + this.labellg;
33085                 items.cls += ' col-lg-' + (12 - this.labellg);
33086             }
33087             
33088             if(this.labelmd > 0){
33089                 label.cls += ' col-md-' + this.labelmd;
33090                 items.cls += ' col-md-' + (12 - this.labelmd);
33091             }
33092             
33093             if(this.labelsm > 0){
33094                 label.cls += ' col-sm-' + this.labelsm;
33095                 items.cls += ' col-sm-' + (12 - this.labelsm);
33096             }
33097             
33098             if(this.labelxs > 0){
33099                 label.cls += ' col-xs-' + this.labelxs;
33100                 items.cls += ' col-xs-' + (12 - this.labelxs);
33101             }
33102         }
33103         
33104         var cfg = {
33105             tag : 'div',
33106             cls : 'roo-radio-set',
33107             cn : [
33108                 {
33109                     tag : 'input',
33110                     cls : 'roo-radio-set-input',
33111                     type : 'hidden',
33112                     name : this.name,
33113                     value : this.value ? this.value :  ''
33114                 },
33115                 label,
33116                 items
33117             ]
33118         };
33119         
33120         if(this.weight.length){
33121             cfg.cls += ' roo-radio-' + this.weight;
33122         }
33123         
33124         if(this.inline) {
33125             cfg.cls += ' roo-radio-set-inline';
33126         }
33127         
33128         return cfg;
33129         
33130     },
33131
33132     initEvents : function()
33133     {
33134         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33135         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33136         
33137         if(!this.fieldLabel.length){
33138             this.labelEl.hide();
33139         }
33140         
33141         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33142         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33143         
33144         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33145         this.indicatorEl().hide();
33146         
33147         this.originalValue = this.getValue();
33148         
33149     },
33150     
33151     inputEl: function ()
33152     {
33153         return this.el.select('.roo-radio-set-input', true).first();
33154     },
33155     
33156     getChildContainer : function()
33157     {
33158         return this.itemsEl;
33159     },
33160     
33161     register : function(item)
33162     {
33163         this.radioes.push(item);
33164         
33165     },
33166     
33167     validate : function()
33168     {   
33169         var valid = false;
33170         
33171         Roo.each(this.radioes, function(i){
33172             if(!i.checked){
33173                 return;
33174             }
33175             
33176             valid = true;
33177             return false;
33178         });
33179         
33180         if(this.allowBlank) {
33181             return true;
33182         }
33183         
33184         if(this.disabled || valid){
33185             this.markValid();
33186             return true;
33187         }
33188         
33189         this.markInvalid();
33190         return false;
33191         
33192     },
33193     
33194     markValid : function()
33195     {
33196         if(this.labelEl.isVisible(true)){
33197             this.indicatorEl().hide();
33198         }
33199         
33200         this.el.removeClass([this.invalidClass, this.validClass]);
33201         this.el.addClass(this.validClass);
33202         
33203         this.fireEvent('valid', this);
33204     },
33205     
33206     markInvalid : function(msg)
33207     {
33208         if(this.allowBlank || this.disabled){
33209             return;
33210         }
33211         
33212         if(this.labelEl.isVisible(true)){
33213             this.indicatorEl().show();
33214         }
33215         
33216         this.el.removeClass([this.invalidClass, this.validClass]);
33217         this.el.addClass(this.invalidClass);
33218         
33219         this.fireEvent('invalid', this, msg);
33220         
33221     },
33222     
33223     setValue : function(v, suppressEvent)
33224     {   
33225         Roo.each(this.radioes, function(i){
33226             
33227             i.checked = false;
33228             i.el.removeClass('checked');
33229             
33230             if(i.value === v || i.value.toString() === v.toString()){
33231                 i.checked = true;
33232                 i.el.addClass('checked');
33233                 
33234                 if(suppressEvent !== true){
33235                     this.fireEvent('check', this, i);
33236                 }
33237             }
33238             
33239         }, this);
33240         
33241         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
33242         
33243     },
33244     
33245     clearInvalid : function(){
33246         
33247         if(!this.el || this.preventMark){
33248             return;
33249         }
33250         
33251         /*
33252         if(this.labelEl.isVisible(true)){
33253             this.indicatorEl().hide();
33254         }
33255         */
33256         
33257         this.el.removeClass([this.invalidClass]);
33258         
33259         this.fireEvent('valid', this);
33260     }
33261     
33262 });
33263
33264 Roo.apply(Roo.bootstrap.RadioSet, {
33265     
33266     groups: {},
33267     
33268     register : function(set)
33269     {
33270         this.groups[set.name] = set;
33271     },
33272     
33273     get: function(name) 
33274     {
33275         if (typeof(this.groups[name]) == 'undefined') {
33276             return false;
33277         }
33278         
33279         return this.groups[name] ;
33280     }
33281     
33282 });
33283 /*
33284  * Based on:
33285  * Ext JS Library 1.1.1
33286  * Copyright(c) 2006-2007, Ext JS, LLC.
33287  *
33288  * Originally Released Under LGPL - original licence link has changed is not relivant.
33289  *
33290  * Fork - LGPL
33291  * <script type="text/javascript">
33292  */
33293
33294
33295 /**
33296  * @class Roo.bootstrap.SplitBar
33297  * @extends Roo.util.Observable
33298  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33299  * <br><br>
33300  * Usage:
33301  * <pre><code>
33302 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33303                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33304 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33305 split.minSize = 100;
33306 split.maxSize = 600;
33307 split.animate = true;
33308 split.on('moved', splitterMoved);
33309 </code></pre>
33310  * @constructor
33311  * Create a new SplitBar
33312  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33313  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33314  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33315  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33316                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33317                         position of the SplitBar).
33318  */
33319 Roo.bootstrap.SplitBar = function(cfg){
33320     
33321     /** @private */
33322     
33323     //{
33324     //  dragElement : elm
33325     //  resizingElement: el,
33326         // optional..
33327     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33328     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33329         // existingProxy ???
33330     //}
33331     
33332     this.el = Roo.get(cfg.dragElement, true);
33333     this.el.dom.unselectable = "on";
33334     /** @private */
33335     this.resizingEl = Roo.get(cfg.resizingElement, true);
33336
33337     /**
33338      * @private
33339      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33340      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33341      * @type Number
33342      */
33343     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33344     
33345     /**
33346      * The minimum size of the resizing element. (Defaults to 0)
33347      * @type Number
33348      */
33349     this.minSize = 0;
33350     
33351     /**
33352      * The maximum size of the resizing element. (Defaults to 2000)
33353      * @type Number
33354      */
33355     this.maxSize = 2000;
33356     
33357     /**
33358      * Whether to animate the transition to the new size
33359      * @type Boolean
33360      */
33361     this.animate = false;
33362     
33363     /**
33364      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33365      * @type Boolean
33366      */
33367     this.useShim = false;
33368     
33369     /** @private */
33370     this.shim = null;
33371     
33372     if(!cfg.existingProxy){
33373         /** @private */
33374         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33375     }else{
33376         this.proxy = Roo.get(cfg.existingProxy).dom;
33377     }
33378     /** @private */
33379     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33380     
33381     /** @private */
33382     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33383     
33384     /** @private */
33385     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33386     
33387     /** @private */
33388     this.dragSpecs = {};
33389     
33390     /**
33391      * @private The adapter to use to positon and resize elements
33392      */
33393     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33394     this.adapter.init(this);
33395     
33396     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33397         /** @private */
33398         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33399         this.el.addClass("roo-splitbar-h");
33400     }else{
33401         /** @private */
33402         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33403         this.el.addClass("roo-splitbar-v");
33404     }
33405     
33406     this.addEvents({
33407         /**
33408          * @event resize
33409          * Fires when the splitter is moved (alias for {@link #event-moved})
33410          * @param {Roo.bootstrap.SplitBar} this
33411          * @param {Number} newSize the new width or height
33412          */
33413         "resize" : true,
33414         /**
33415          * @event moved
33416          * Fires when the splitter is moved
33417          * @param {Roo.bootstrap.SplitBar} this
33418          * @param {Number} newSize the new width or height
33419          */
33420         "moved" : true,
33421         /**
33422          * @event beforeresize
33423          * Fires before the splitter is dragged
33424          * @param {Roo.bootstrap.SplitBar} this
33425          */
33426         "beforeresize" : true,
33427
33428         "beforeapply" : true
33429     });
33430
33431     Roo.util.Observable.call(this);
33432 };
33433
33434 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33435     onStartProxyDrag : function(x, y){
33436         this.fireEvent("beforeresize", this);
33437         if(!this.overlay){
33438             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33439             o.unselectable();
33440             o.enableDisplayMode("block");
33441             // all splitbars share the same overlay
33442             Roo.bootstrap.SplitBar.prototype.overlay = o;
33443         }
33444         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33445         this.overlay.show();
33446         Roo.get(this.proxy).setDisplayed("block");
33447         var size = this.adapter.getElementSize(this);
33448         this.activeMinSize = this.getMinimumSize();;
33449         this.activeMaxSize = this.getMaximumSize();;
33450         var c1 = size - this.activeMinSize;
33451         var c2 = Math.max(this.activeMaxSize - size, 0);
33452         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33453             this.dd.resetConstraints();
33454             this.dd.setXConstraint(
33455                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33456                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33457             );
33458             this.dd.setYConstraint(0, 0);
33459         }else{
33460             this.dd.resetConstraints();
33461             this.dd.setXConstraint(0, 0);
33462             this.dd.setYConstraint(
33463                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33464                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33465             );
33466          }
33467         this.dragSpecs.startSize = size;
33468         this.dragSpecs.startPoint = [x, y];
33469         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33470     },
33471     
33472     /** 
33473      * @private Called after the drag operation by the DDProxy
33474      */
33475     onEndProxyDrag : function(e){
33476         Roo.get(this.proxy).setDisplayed(false);
33477         var endPoint = Roo.lib.Event.getXY(e);
33478         if(this.overlay){
33479             this.overlay.hide();
33480         }
33481         var newSize;
33482         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33483             newSize = this.dragSpecs.startSize + 
33484                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33485                     endPoint[0] - this.dragSpecs.startPoint[0] :
33486                     this.dragSpecs.startPoint[0] - endPoint[0]
33487                 );
33488         }else{
33489             newSize = this.dragSpecs.startSize + 
33490                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33491                     endPoint[1] - this.dragSpecs.startPoint[1] :
33492                     this.dragSpecs.startPoint[1] - endPoint[1]
33493                 );
33494         }
33495         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33496         if(newSize != this.dragSpecs.startSize){
33497             if(this.fireEvent('beforeapply', this, newSize) !== false){
33498                 this.adapter.setElementSize(this, newSize);
33499                 this.fireEvent("moved", this, newSize);
33500                 this.fireEvent("resize", this, newSize);
33501             }
33502         }
33503     },
33504     
33505     /**
33506      * Get the adapter this SplitBar uses
33507      * @return The adapter object
33508      */
33509     getAdapter : function(){
33510         return this.adapter;
33511     },
33512     
33513     /**
33514      * Set the adapter this SplitBar uses
33515      * @param {Object} adapter A SplitBar adapter object
33516      */
33517     setAdapter : function(adapter){
33518         this.adapter = adapter;
33519         this.adapter.init(this);
33520     },
33521     
33522     /**
33523      * Gets the minimum size for the resizing element
33524      * @return {Number} The minimum size
33525      */
33526     getMinimumSize : function(){
33527         return this.minSize;
33528     },
33529     
33530     /**
33531      * Sets the minimum size for the resizing element
33532      * @param {Number} minSize The minimum size
33533      */
33534     setMinimumSize : function(minSize){
33535         this.minSize = minSize;
33536     },
33537     
33538     /**
33539      * Gets the maximum size for the resizing element
33540      * @return {Number} The maximum size
33541      */
33542     getMaximumSize : function(){
33543         return this.maxSize;
33544     },
33545     
33546     /**
33547      * Sets the maximum size for the resizing element
33548      * @param {Number} maxSize The maximum size
33549      */
33550     setMaximumSize : function(maxSize){
33551         this.maxSize = maxSize;
33552     },
33553     
33554     /**
33555      * Sets the initialize size for the resizing element
33556      * @param {Number} size The initial size
33557      */
33558     setCurrentSize : function(size){
33559         var oldAnimate = this.animate;
33560         this.animate = false;
33561         this.adapter.setElementSize(this, size);
33562         this.animate = oldAnimate;
33563     },
33564     
33565     /**
33566      * Destroy this splitbar. 
33567      * @param {Boolean} removeEl True to remove the element
33568      */
33569     destroy : function(removeEl){
33570         if(this.shim){
33571             this.shim.remove();
33572         }
33573         this.dd.unreg();
33574         this.proxy.parentNode.removeChild(this.proxy);
33575         if(removeEl){
33576             this.el.remove();
33577         }
33578     }
33579 });
33580
33581 /**
33582  * @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.
33583  */
33584 Roo.bootstrap.SplitBar.createProxy = function(dir){
33585     var proxy = new Roo.Element(document.createElement("div"));
33586     proxy.unselectable();
33587     var cls = 'roo-splitbar-proxy';
33588     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33589     document.body.appendChild(proxy.dom);
33590     return proxy.dom;
33591 };
33592
33593 /** 
33594  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33595  * Default Adapter. It assumes the splitter and resizing element are not positioned
33596  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33597  */
33598 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33599 };
33600
33601 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33602     // do nothing for now
33603     init : function(s){
33604     
33605     },
33606     /**
33607      * Called before drag operations to get the current size of the resizing element. 
33608      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33609      */
33610      getElementSize : function(s){
33611         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33612             return s.resizingEl.getWidth();
33613         }else{
33614             return s.resizingEl.getHeight();
33615         }
33616     },
33617     
33618     /**
33619      * Called after drag operations to set the size of the resizing element.
33620      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33621      * @param {Number} newSize The new size to set
33622      * @param {Function} onComplete A function to be invoked when resizing is complete
33623      */
33624     setElementSize : function(s, newSize, onComplete){
33625         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33626             if(!s.animate){
33627                 s.resizingEl.setWidth(newSize);
33628                 if(onComplete){
33629                     onComplete(s, newSize);
33630                 }
33631             }else{
33632                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33633             }
33634         }else{
33635             
33636             if(!s.animate){
33637                 s.resizingEl.setHeight(newSize);
33638                 if(onComplete){
33639                     onComplete(s, newSize);
33640                 }
33641             }else{
33642                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33643             }
33644         }
33645     }
33646 };
33647
33648 /** 
33649  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33650  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33651  * Adapter that  moves the splitter element to align with the resized sizing element. 
33652  * Used with an absolute positioned SplitBar.
33653  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33654  * document.body, make sure you assign an id to the body element.
33655  */
33656 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33657     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33658     this.container = Roo.get(container);
33659 };
33660
33661 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33662     init : function(s){
33663         this.basic.init(s);
33664     },
33665     
33666     getElementSize : function(s){
33667         return this.basic.getElementSize(s);
33668     },
33669     
33670     setElementSize : function(s, newSize, onComplete){
33671         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33672     },
33673     
33674     moveSplitter : function(s){
33675         var yes = Roo.bootstrap.SplitBar;
33676         switch(s.placement){
33677             case yes.LEFT:
33678                 s.el.setX(s.resizingEl.getRight());
33679                 break;
33680             case yes.RIGHT:
33681                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33682                 break;
33683             case yes.TOP:
33684                 s.el.setY(s.resizingEl.getBottom());
33685                 break;
33686             case yes.BOTTOM:
33687                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33688                 break;
33689         }
33690     }
33691 };
33692
33693 /**
33694  * Orientation constant - Create a vertical SplitBar
33695  * @static
33696  * @type Number
33697  */
33698 Roo.bootstrap.SplitBar.VERTICAL = 1;
33699
33700 /**
33701  * Orientation constant - Create a horizontal SplitBar
33702  * @static
33703  * @type Number
33704  */
33705 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33706
33707 /**
33708  * Placement constant - The resizing element is to the left of the splitter element
33709  * @static
33710  * @type Number
33711  */
33712 Roo.bootstrap.SplitBar.LEFT = 1;
33713
33714 /**
33715  * Placement constant - The resizing element is to the right of the splitter element
33716  * @static
33717  * @type Number
33718  */
33719 Roo.bootstrap.SplitBar.RIGHT = 2;
33720
33721 /**
33722  * Placement constant - The resizing element is positioned above the splitter element
33723  * @static
33724  * @type Number
33725  */
33726 Roo.bootstrap.SplitBar.TOP = 3;
33727
33728 /**
33729  * Placement constant - The resizing element is positioned under splitter element
33730  * @static
33731  * @type Number
33732  */
33733 Roo.bootstrap.SplitBar.BOTTOM = 4;
33734 Roo.namespace("Roo.bootstrap.layout");/*
33735  * Based on:
33736  * Ext JS Library 1.1.1
33737  * Copyright(c) 2006-2007, Ext JS, LLC.
33738  *
33739  * Originally Released Under LGPL - original licence link has changed is not relivant.
33740  *
33741  * Fork - LGPL
33742  * <script type="text/javascript">
33743  */
33744
33745 /**
33746  * @class Roo.bootstrap.layout.Manager
33747  * @extends Roo.bootstrap.Component
33748  * Base class for layout managers.
33749  */
33750 Roo.bootstrap.layout.Manager = function(config)
33751 {
33752     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33753
33754
33755
33756
33757
33758     /** false to disable window resize monitoring @type Boolean */
33759     this.monitorWindowResize = true;
33760     this.regions = {};
33761     this.addEvents({
33762         /**
33763          * @event layout
33764          * Fires when a layout is performed.
33765          * @param {Roo.LayoutManager} this
33766          */
33767         "layout" : true,
33768         /**
33769          * @event regionresized
33770          * Fires when the user resizes a region.
33771          * @param {Roo.LayoutRegion} region The resized region
33772          * @param {Number} newSize The new size (width for east/west, height for north/south)
33773          */
33774         "regionresized" : true,
33775         /**
33776          * @event regioncollapsed
33777          * Fires when a region is collapsed.
33778          * @param {Roo.LayoutRegion} region The collapsed region
33779          */
33780         "regioncollapsed" : true,
33781         /**
33782          * @event regionexpanded
33783          * Fires when a region is expanded.
33784          * @param {Roo.LayoutRegion} region The expanded region
33785          */
33786         "regionexpanded" : true
33787     });
33788     this.updating = false;
33789
33790     if (config.el) {
33791         this.el = Roo.get(config.el);
33792         this.initEvents();
33793     }
33794
33795 };
33796
33797 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33798
33799
33800     regions : null,
33801
33802     monitorWindowResize : true,
33803
33804
33805     updating : false,
33806
33807
33808     onRender : function(ct, position)
33809     {
33810         if(!this.el){
33811             this.el = Roo.get(ct);
33812             this.initEvents();
33813         }
33814         //this.fireEvent('render',this);
33815     },
33816
33817
33818     initEvents: function()
33819     {
33820
33821
33822         // ie scrollbar fix
33823         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33824             document.body.scroll = "no";
33825         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33826             this.el.position('relative');
33827         }
33828         this.id = this.el.id;
33829         this.el.addClass("roo-layout-container");
33830         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33831         if(this.el.dom != document.body ) {
33832             this.el.on('resize', this.layout,this);
33833             this.el.on('show', this.layout,this);
33834         }
33835
33836     },
33837
33838     /**
33839      * Returns true if this layout is currently being updated
33840      * @return {Boolean}
33841      */
33842     isUpdating : function(){
33843         return this.updating;
33844     },
33845
33846     /**
33847      * Suspend the LayoutManager from doing auto-layouts while
33848      * making multiple add or remove calls
33849      */
33850     beginUpdate : function(){
33851         this.updating = true;
33852     },
33853
33854     /**
33855      * Restore auto-layouts and optionally disable the manager from performing a layout
33856      * @param {Boolean} noLayout true to disable a layout update
33857      */
33858     endUpdate : function(noLayout){
33859         this.updating = false;
33860         if(!noLayout){
33861             this.layout();
33862         }
33863     },
33864
33865     layout: function(){
33866         // abstract...
33867     },
33868
33869     onRegionResized : function(region, newSize){
33870         this.fireEvent("regionresized", region, newSize);
33871         this.layout();
33872     },
33873
33874     onRegionCollapsed : function(region){
33875         this.fireEvent("regioncollapsed", region);
33876     },
33877
33878     onRegionExpanded : function(region){
33879         this.fireEvent("regionexpanded", region);
33880     },
33881
33882     /**
33883      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33884      * performs box-model adjustments.
33885      * @return {Object} The size as an object {width: (the width), height: (the height)}
33886      */
33887     getViewSize : function()
33888     {
33889         var size;
33890         if(this.el.dom != document.body){
33891             size = this.el.getSize();
33892         }else{
33893             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33894         }
33895         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33896         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33897         return size;
33898     },
33899
33900     /**
33901      * Returns the Element this layout is bound to.
33902      * @return {Roo.Element}
33903      */
33904     getEl : function(){
33905         return this.el;
33906     },
33907
33908     /**
33909      * Returns the specified region.
33910      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33911      * @return {Roo.LayoutRegion}
33912      */
33913     getRegion : function(target){
33914         return this.regions[target.toLowerCase()];
33915     },
33916
33917     onWindowResize : function(){
33918         if(this.monitorWindowResize){
33919             this.layout();
33920         }
33921     }
33922 });
33923 /*
33924  * Based on:
33925  * Ext JS Library 1.1.1
33926  * Copyright(c) 2006-2007, Ext JS, LLC.
33927  *
33928  * Originally Released Under LGPL - original licence link has changed is not relivant.
33929  *
33930  * Fork - LGPL
33931  * <script type="text/javascript">
33932  */
33933 /**
33934  * @class Roo.bootstrap.layout.Border
33935  * @extends Roo.bootstrap.layout.Manager
33936  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33937  * please see: examples/bootstrap/nested.html<br><br>
33938  
33939 <b>The container the layout is rendered into can be either the body element or any other element.
33940 If it is not the body element, the container needs to either be an absolute positioned element,
33941 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33942 the container size if it is not the body element.</b>
33943
33944 * @constructor
33945 * Create a new Border
33946 * @param {Object} config Configuration options
33947  */
33948 Roo.bootstrap.layout.Border = function(config){
33949     config = config || {};
33950     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33951     
33952     
33953     
33954     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33955         if(config[region]){
33956             config[region].region = region;
33957             this.addRegion(config[region]);
33958         }
33959     },this);
33960     
33961 };
33962
33963 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33964
33965 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33966     /**
33967      * Creates and adds a new region if it doesn't already exist.
33968      * @param {String} target The target region key (north, south, east, west or center).
33969      * @param {Object} config The regions config object
33970      * @return {BorderLayoutRegion} The new region
33971      */
33972     addRegion : function(config)
33973     {
33974         if(!this.regions[config.region]){
33975             var r = this.factory(config);
33976             this.bindRegion(r);
33977         }
33978         return this.regions[config.region];
33979     },
33980
33981     // private (kinda)
33982     bindRegion : function(r){
33983         this.regions[r.config.region] = r;
33984         
33985         r.on("visibilitychange",    this.layout, this);
33986         r.on("paneladded",          this.layout, this);
33987         r.on("panelremoved",        this.layout, this);
33988         r.on("invalidated",         this.layout, this);
33989         r.on("resized",             this.onRegionResized, this);
33990         r.on("collapsed",           this.onRegionCollapsed, this);
33991         r.on("expanded",            this.onRegionExpanded, this);
33992     },
33993
33994     /**
33995      * Performs a layout update.
33996      */
33997     layout : function()
33998     {
33999         if(this.updating) {
34000             return;
34001         }
34002         
34003         // render all the rebions if they have not been done alreayd?
34004         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34005             if(this.regions[region] && !this.regions[region].bodyEl){
34006                 this.regions[region].onRender(this.el)
34007             }
34008         },this);
34009         
34010         var size = this.getViewSize();
34011         var w = size.width;
34012         var h = size.height;
34013         var centerW = w;
34014         var centerH = h;
34015         var centerY = 0;
34016         var centerX = 0;
34017         //var x = 0, y = 0;
34018
34019         var rs = this.regions;
34020         var north = rs["north"];
34021         var south = rs["south"]; 
34022         var west = rs["west"];
34023         var east = rs["east"];
34024         var center = rs["center"];
34025         //if(this.hideOnLayout){ // not supported anymore
34026             //c.el.setStyle("display", "none");
34027         //}
34028         if(north && north.isVisible()){
34029             var b = north.getBox();
34030             var m = north.getMargins();
34031             b.width = w - (m.left+m.right);
34032             b.x = m.left;
34033             b.y = m.top;
34034             centerY = b.height + b.y + m.bottom;
34035             centerH -= centerY;
34036             north.updateBox(this.safeBox(b));
34037         }
34038         if(south && south.isVisible()){
34039             var b = south.getBox();
34040             var m = south.getMargins();
34041             b.width = w - (m.left+m.right);
34042             b.x = m.left;
34043             var totalHeight = (b.height + m.top + m.bottom);
34044             b.y = h - totalHeight + m.top;
34045             centerH -= totalHeight;
34046             south.updateBox(this.safeBox(b));
34047         }
34048         if(west && west.isVisible()){
34049             var b = west.getBox();
34050             var m = west.getMargins();
34051             b.height = centerH - (m.top+m.bottom);
34052             b.x = m.left;
34053             b.y = centerY + m.top;
34054             var totalWidth = (b.width + m.left + m.right);
34055             centerX += totalWidth;
34056             centerW -= totalWidth;
34057             west.updateBox(this.safeBox(b));
34058         }
34059         if(east && east.isVisible()){
34060             var b = east.getBox();
34061             var m = east.getMargins();
34062             b.height = centerH - (m.top+m.bottom);
34063             var totalWidth = (b.width + m.left + m.right);
34064             b.x = w - totalWidth + m.left;
34065             b.y = centerY + m.top;
34066             centerW -= totalWidth;
34067             east.updateBox(this.safeBox(b));
34068         }
34069         if(center){
34070             var m = center.getMargins();
34071             var centerBox = {
34072                 x: centerX + m.left,
34073                 y: centerY + m.top,
34074                 width: centerW - (m.left+m.right),
34075                 height: centerH - (m.top+m.bottom)
34076             };
34077             //if(this.hideOnLayout){
34078                 //center.el.setStyle("display", "block");
34079             //}
34080             center.updateBox(this.safeBox(centerBox));
34081         }
34082         this.el.repaint();
34083         this.fireEvent("layout", this);
34084     },
34085
34086     // private
34087     safeBox : function(box){
34088         box.width = Math.max(0, box.width);
34089         box.height = Math.max(0, box.height);
34090         return box;
34091     },
34092
34093     /**
34094      * Adds a ContentPanel (or subclass) to this layout.
34095      * @param {String} target The target region key (north, south, east, west or center).
34096      * @param {Roo.ContentPanel} panel The panel to add
34097      * @return {Roo.ContentPanel} The added panel
34098      */
34099     add : function(target, panel){
34100          
34101         target = target.toLowerCase();
34102         return this.regions[target].add(panel);
34103     },
34104
34105     /**
34106      * Remove a ContentPanel (or subclass) to this layout.
34107      * @param {String} target The target region key (north, south, east, west or center).
34108      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34109      * @return {Roo.ContentPanel} The removed panel
34110      */
34111     remove : function(target, panel){
34112         target = target.toLowerCase();
34113         return this.regions[target].remove(panel);
34114     },
34115
34116     /**
34117      * Searches all regions for a panel with the specified id
34118      * @param {String} panelId
34119      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34120      */
34121     findPanel : function(panelId){
34122         var rs = this.regions;
34123         for(var target in rs){
34124             if(typeof rs[target] != "function"){
34125                 var p = rs[target].getPanel(panelId);
34126                 if(p){
34127                     return p;
34128                 }
34129             }
34130         }
34131         return null;
34132     },
34133
34134     /**
34135      * Searches all regions for a panel with the specified id and activates (shows) it.
34136      * @param {String/ContentPanel} panelId The panels id or the panel itself
34137      * @return {Roo.ContentPanel} The shown panel or null
34138      */
34139     showPanel : function(panelId) {
34140       var rs = this.regions;
34141       for(var target in rs){
34142          var r = rs[target];
34143          if(typeof r != "function"){
34144             if(r.hasPanel(panelId)){
34145                return r.showPanel(panelId);
34146             }
34147          }
34148       }
34149       return null;
34150    },
34151
34152    /**
34153      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34154      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34155      */
34156    /*
34157     restoreState : function(provider){
34158         if(!provider){
34159             provider = Roo.state.Manager;
34160         }
34161         var sm = new Roo.LayoutStateManager();
34162         sm.init(this, provider);
34163     },
34164 */
34165  
34166  
34167     /**
34168      * Adds a xtype elements to the layout.
34169      * <pre><code>
34170
34171 layout.addxtype({
34172        xtype : 'ContentPanel',
34173        region: 'west',
34174        items: [ .... ]
34175    }
34176 );
34177
34178 layout.addxtype({
34179         xtype : 'NestedLayoutPanel',
34180         region: 'west',
34181         layout: {
34182            center: { },
34183            west: { }   
34184         },
34185         items : [ ... list of content panels or nested layout panels.. ]
34186    }
34187 );
34188 </code></pre>
34189      * @param {Object} cfg Xtype definition of item to add.
34190      */
34191     addxtype : function(cfg)
34192     {
34193         // basically accepts a pannel...
34194         // can accept a layout region..!?!?
34195         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34196         
34197         
34198         // theory?  children can only be panels??
34199         
34200         //if (!cfg.xtype.match(/Panel$/)) {
34201         //    return false;
34202         //}
34203         var ret = false;
34204         
34205         if (typeof(cfg.region) == 'undefined') {
34206             Roo.log("Failed to add Panel, region was not set");
34207             Roo.log(cfg);
34208             return false;
34209         }
34210         var region = cfg.region;
34211         delete cfg.region;
34212         
34213           
34214         var xitems = [];
34215         if (cfg.items) {
34216             xitems = cfg.items;
34217             delete cfg.items;
34218         }
34219         var nb = false;
34220         
34221         switch(cfg.xtype) 
34222         {
34223             case 'Content':  // ContentPanel (el, cfg)
34224             case 'Scroll':  // ContentPanel (el, cfg)
34225             case 'View': 
34226                 cfg.autoCreate = true;
34227                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34228                 //} else {
34229                 //    var el = this.el.createChild();
34230                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34231                 //}
34232                 
34233                 this.add(region, ret);
34234                 break;
34235             
34236             /*
34237             case 'TreePanel': // our new panel!
34238                 cfg.el = this.el.createChild();
34239                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34240                 this.add(region, ret);
34241                 break;
34242             */
34243             
34244             case 'Nest': 
34245                 // create a new Layout (which is  a Border Layout...
34246                 
34247                 var clayout = cfg.layout;
34248                 clayout.el  = this.el.createChild();
34249                 clayout.items   = clayout.items  || [];
34250                 
34251                 delete cfg.layout;
34252                 
34253                 // replace this exitems with the clayout ones..
34254                 xitems = clayout.items;
34255                  
34256                 // force background off if it's in center...
34257                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34258                     cfg.background = false;
34259                 }
34260                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34261                 
34262                 
34263                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34264                 //console.log('adding nested layout panel '  + cfg.toSource());
34265                 this.add(region, ret);
34266                 nb = {}; /// find first...
34267                 break;
34268             
34269             case 'Grid':
34270                 
34271                 // needs grid and region
34272                 
34273                 //var el = this.getRegion(region).el.createChild();
34274                 /*
34275                  *var el = this.el.createChild();
34276                 // create the grid first...
34277                 cfg.grid.container = el;
34278                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34279                 */
34280                 
34281                 if (region == 'center' && this.active ) {
34282                     cfg.background = false;
34283                 }
34284                 
34285                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34286                 
34287                 this.add(region, ret);
34288                 /*
34289                 if (cfg.background) {
34290                     // render grid on panel activation (if panel background)
34291                     ret.on('activate', function(gp) {
34292                         if (!gp.grid.rendered) {
34293                     //        gp.grid.render(el);
34294                         }
34295                     });
34296                 } else {
34297                   //  cfg.grid.render(el);
34298                 }
34299                 */
34300                 break;
34301            
34302            
34303             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34304                 // it was the old xcomponent building that caused this before.
34305                 // espeically if border is the top element in the tree.
34306                 ret = this;
34307                 break; 
34308                 
34309                     
34310                 
34311                 
34312                 
34313             default:
34314                 /*
34315                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34316                     
34317                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34318                     this.add(region, ret);
34319                 } else {
34320                 */
34321                     Roo.log(cfg);
34322                     throw "Can not add '" + cfg.xtype + "' to Border";
34323                     return null;
34324              
34325                                 
34326              
34327         }
34328         this.beginUpdate();
34329         // add children..
34330         var region = '';
34331         var abn = {};
34332         Roo.each(xitems, function(i)  {
34333             region = nb && i.region ? i.region : false;
34334             
34335             var add = ret.addxtype(i);
34336            
34337             if (region) {
34338                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34339                 if (!i.background) {
34340                     abn[region] = nb[region] ;
34341                 }
34342             }
34343             
34344         });
34345         this.endUpdate();
34346
34347         // make the last non-background panel active..
34348         //if (nb) { Roo.log(abn); }
34349         if (nb) {
34350             
34351             for(var r in abn) {
34352                 region = this.getRegion(r);
34353                 if (region) {
34354                     // tried using nb[r], but it does not work..
34355                      
34356                     region.showPanel(abn[r]);
34357                    
34358                 }
34359             }
34360         }
34361         return ret;
34362         
34363     },
34364     
34365     
34366 // private
34367     factory : function(cfg)
34368     {
34369         
34370         var validRegions = Roo.bootstrap.layout.Border.regions;
34371
34372         var target = cfg.region;
34373         cfg.mgr = this;
34374         
34375         var r = Roo.bootstrap.layout;
34376         Roo.log(target);
34377         switch(target){
34378             case "north":
34379                 return new r.North(cfg);
34380             case "south":
34381                 return new r.South(cfg);
34382             case "east":
34383                 return new r.East(cfg);
34384             case "west":
34385                 return new r.West(cfg);
34386             case "center":
34387                 return new r.Center(cfg);
34388         }
34389         throw 'Layout region "'+target+'" not supported.';
34390     }
34391     
34392     
34393 });
34394  /*
34395  * Based on:
34396  * Ext JS Library 1.1.1
34397  * Copyright(c) 2006-2007, Ext JS, LLC.
34398  *
34399  * Originally Released Under LGPL - original licence link has changed is not relivant.
34400  *
34401  * Fork - LGPL
34402  * <script type="text/javascript">
34403  */
34404  
34405 /**
34406  * @class Roo.bootstrap.layout.Basic
34407  * @extends Roo.util.Observable
34408  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34409  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34410  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34411  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34412  * @cfg {string}   region  the region that it inhabits..
34413  * @cfg {bool}   skipConfig skip config?
34414  * 
34415
34416  */
34417 Roo.bootstrap.layout.Basic = function(config){
34418     
34419     this.mgr = config.mgr;
34420     
34421     this.position = config.region;
34422     
34423     var skipConfig = config.skipConfig;
34424     
34425     this.events = {
34426         /**
34427          * @scope Roo.BasicLayoutRegion
34428          */
34429         
34430         /**
34431          * @event beforeremove
34432          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34433          * @param {Roo.LayoutRegion} this
34434          * @param {Roo.ContentPanel} panel The panel
34435          * @param {Object} e The cancel event object
34436          */
34437         "beforeremove" : true,
34438         /**
34439          * @event invalidated
34440          * Fires when the layout for this region is changed.
34441          * @param {Roo.LayoutRegion} this
34442          */
34443         "invalidated" : true,
34444         /**
34445          * @event visibilitychange
34446          * Fires when this region is shown or hidden 
34447          * @param {Roo.LayoutRegion} this
34448          * @param {Boolean} visibility true or false
34449          */
34450         "visibilitychange" : true,
34451         /**
34452          * @event paneladded
34453          * Fires when a panel is added. 
34454          * @param {Roo.LayoutRegion} this
34455          * @param {Roo.ContentPanel} panel The panel
34456          */
34457         "paneladded" : true,
34458         /**
34459          * @event panelremoved
34460          * Fires when a panel is removed. 
34461          * @param {Roo.LayoutRegion} this
34462          * @param {Roo.ContentPanel} panel The panel
34463          */
34464         "panelremoved" : true,
34465         /**
34466          * @event beforecollapse
34467          * Fires when this region before collapse.
34468          * @param {Roo.LayoutRegion} this
34469          */
34470         "beforecollapse" : true,
34471         /**
34472          * @event collapsed
34473          * Fires when this region is collapsed.
34474          * @param {Roo.LayoutRegion} this
34475          */
34476         "collapsed" : true,
34477         /**
34478          * @event expanded
34479          * Fires when this region is expanded.
34480          * @param {Roo.LayoutRegion} this
34481          */
34482         "expanded" : true,
34483         /**
34484          * @event slideshow
34485          * Fires when this region is slid into view.
34486          * @param {Roo.LayoutRegion} this
34487          */
34488         "slideshow" : true,
34489         /**
34490          * @event slidehide
34491          * Fires when this region slides out of view. 
34492          * @param {Roo.LayoutRegion} this
34493          */
34494         "slidehide" : true,
34495         /**
34496          * @event panelactivated
34497          * Fires when a panel is activated. 
34498          * @param {Roo.LayoutRegion} this
34499          * @param {Roo.ContentPanel} panel The activated panel
34500          */
34501         "panelactivated" : true,
34502         /**
34503          * @event resized
34504          * Fires when the user resizes this region. 
34505          * @param {Roo.LayoutRegion} this
34506          * @param {Number} newSize The new size (width for east/west, height for north/south)
34507          */
34508         "resized" : true
34509     };
34510     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34511     this.panels = new Roo.util.MixedCollection();
34512     this.panels.getKey = this.getPanelId.createDelegate(this);
34513     this.box = null;
34514     this.activePanel = null;
34515     // ensure listeners are added...
34516     
34517     if (config.listeners || config.events) {
34518         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34519             listeners : config.listeners || {},
34520             events : config.events || {}
34521         });
34522     }
34523     
34524     if(skipConfig !== true){
34525         this.applyConfig(config);
34526     }
34527 };
34528
34529 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34530 {
34531     getPanelId : function(p){
34532         return p.getId();
34533     },
34534     
34535     applyConfig : function(config){
34536         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34537         this.config = config;
34538         
34539     },
34540     
34541     /**
34542      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34543      * the width, for horizontal (north, south) the height.
34544      * @param {Number} newSize The new width or height
34545      */
34546     resizeTo : function(newSize){
34547         var el = this.el ? this.el :
34548                  (this.activePanel ? this.activePanel.getEl() : null);
34549         if(el){
34550             switch(this.position){
34551                 case "east":
34552                 case "west":
34553                     el.setWidth(newSize);
34554                     this.fireEvent("resized", this, newSize);
34555                 break;
34556                 case "north":
34557                 case "south":
34558                     el.setHeight(newSize);
34559                     this.fireEvent("resized", this, newSize);
34560                 break;                
34561             }
34562         }
34563     },
34564     
34565     getBox : function(){
34566         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34567     },
34568     
34569     getMargins : function(){
34570         return this.margins;
34571     },
34572     
34573     updateBox : function(box){
34574         this.box = box;
34575         var el = this.activePanel.getEl();
34576         el.dom.style.left = box.x + "px";
34577         el.dom.style.top = box.y + "px";
34578         this.activePanel.setSize(box.width, box.height);
34579     },
34580     
34581     /**
34582      * Returns the container element for this region.
34583      * @return {Roo.Element}
34584      */
34585     getEl : function(){
34586         return this.activePanel;
34587     },
34588     
34589     /**
34590      * Returns true if this region is currently visible.
34591      * @return {Boolean}
34592      */
34593     isVisible : function(){
34594         return this.activePanel ? true : false;
34595     },
34596     
34597     setActivePanel : function(panel){
34598         panel = this.getPanel(panel);
34599         if(this.activePanel && this.activePanel != panel){
34600             this.activePanel.setActiveState(false);
34601             this.activePanel.getEl().setLeftTop(-10000,-10000);
34602         }
34603         this.activePanel = panel;
34604         panel.setActiveState(true);
34605         if(this.box){
34606             panel.setSize(this.box.width, this.box.height);
34607         }
34608         this.fireEvent("panelactivated", this, panel);
34609         this.fireEvent("invalidated");
34610     },
34611     
34612     /**
34613      * Show the specified panel.
34614      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34615      * @return {Roo.ContentPanel} The shown panel or null
34616      */
34617     showPanel : function(panel){
34618         panel = this.getPanel(panel);
34619         if(panel){
34620             this.setActivePanel(panel);
34621         }
34622         return panel;
34623     },
34624     
34625     /**
34626      * Get the active panel for this region.
34627      * @return {Roo.ContentPanel} The active panel or null
34628      */
34629     getActivePanel : function(){
34630         return this.activePanel;
34631     },
34632     
34633     /**
34634      * Add the passed ContentPanel(s)
34635      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34636      * @return {Roo.ContentPanel} The panel added (if only one was added)
34637      */
34638     add : function(panel){
34639         if(arguments.length > 1){
34640             for(var i = 0, len = arguments.length; i < len; i++) {
34641                 this.add(arguments[i]);
34642             }
34643             return null;
34644         }
34645         if(this.hasPanel(panel)){
34646             this.showPanel(panel);
34647             return panel;
34648         }
34649         var el = panel.getEl();
34650         if(el.dom.parentNode != this.mgr.el.dom){
34651             this.mgr.el.dom.appendChild(el.dom);
34652         }
34653         if(panel.setRegion){
34654             panel.setRegion(this);
34655         }
34656         this.panels.add(panel);
34657         el.setStyle("position", "absolute");
34658         if(!panel.background){
34659             this.setActivePanel(panel);
34660             if(this.config.initialSize && this.panels.getCount()==1){
34661                 this.resizeTo(this.config.initialSize);
34662             }
34663         }
34664         this.fireEvent("paneladded", this, panel);
34665         return panel;
34666     },
34667     
34668     /**
34669      * Returns true if the panel is in this region.
34670      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34671      * @return {Boolean}
34672      */
34673     hasPanel : function(panel){
34674         if(typeof panel == "object"){ // must be panel obj
34675             panel = panel.getId();
34676         }
34677         return this.getPanel(panel) ? true : false;
34678     },
34679     
34680     /**
34681      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34682      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34683      * @param {Boolean} preservePanel Overrides the config preservePanel option
34684      * @return {Roo.ContentPanel} The panel that was removed
34685      */
34686     remove : function(panel, preservePanel){
34687         panel = this.getPanel(panel);
34688         if(!panel){
34689             return null;
34690         }
34691         var e = {};
34692         this.fireEvent("beforeremove", this, panel, e);
34693         if(e.cancel === true){
34694             return null;
34695         }
34696         var panelId = panel.getId();
34697         this.panels.removeKey(panelId);
34698         return panel;
34699     },
34700     
34701     /**
34702      * Returns the panel specified or null if it's not in this region.
34703      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34704      * @return {Roo.ContentPanel}
34705      */
34706     getPanel : function(id){
34707         if(typeof id == "object"){ // must be panel obj
34708             return id;
34709         }
34710         return this.panels.get(id);
34711     },
34712     
34713     /**
34714      * Returns this regions position (north/south/east/west/center).
34715      * @return {String} 
34716      */
34717     getPosition: function(){
34718         return this.position;    
34719     }
34720 });/*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730  
34731 /**
34732  * @class Roo.bootstrap.layout.Region
34733  * @extends Roo.bootstrap.layout.Basic
34734  * This class represents a region in a layout manager.
34735  
34736  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34737  * @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})
34738  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34739  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34740  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34741  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34742  * @cfg {String}    title           The title for the region (overrides panel titles)
34743  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34744  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34745  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34746  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34747  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34748  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34749  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34750  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34751  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34752  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34753
34754  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34755  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34756  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34757  * @cfg {Number}    width           For East/West panels
34758  * @cfg {Number}    height          For North/South panels
34759  * @cfg {Boolean}   split           To show the splitter
34760  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34761  * 
34762  * @cfg {string}   cls             Extra CSS classes to add to region
34763  * 
34764  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34765  * @cfg {string}   region  the region that it inhabits..
34766  *
34767
34768  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34769  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34770
34771  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34772  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34773  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34774  */
34775 Roo.bootstrap.layout.Region = function(config)
34776 {
34777     this.applyConfig(config);
34778
34779     var mgr = config.mgr;
34780     var pos = config.region;
34781     config.skipConfig = true;
34782     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34783     
34784     if (mgr.el) {
34785         this.onRender(mgr.el);   
34786     }
34787      
34788     this.visible = true;
34789     this.collapsed = false;
34790     this.unrendered_panels = [];
34791 };
34792
34793 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34794
34795     position: '', // set by wrapper (eg. north/south etc..)
34796     unrendered_panels : null,  // unrendered panels.
34797     createBody : function(){
34798         /** This region's body element 
34799         * @type Roo.Element */
34800         this.bodyEl = this.el.createChild({
34801                 tag: "div",
34802                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34803         });
34804     },
34805
34806     onRender: function(ctr, pos)
34807     {
34808         var dh = Roo.DomHelper;
34809         /** This region's container element 
34810         * @type Roo.Element */
34811         this.el = dh.append(ctr.dom, {
34812                 tag: "div",
34813                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34814             }, true);
34815         /** This region's title element 
34816         * @type Roo.Element */
34817     
34818         this.titleEl = dh.append(this.el.dom,
34819             {
34820                     tag: "div",
34821                     unselectable: "on",
34822                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34823                     children:[
34824                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34825                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34826                     ]}, true);
34827         
34828         this.titleEl.enableDisplayMode();
34829         /** This region's title text element 
34830         * @type HTMLElement */
34831         this.titleTextEl = this.titleEl.dom.firstChild;
34832         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34833         /*
34834         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34835         this.closeBtn.enableDisplayMode();
34836         this.closeBtn.on("click", this.closeClicked, this);
34837         this.closeBtn.hide();
34838     */
34839         this.createBody(this.config);
34840         if(this.config.hideWhenEmpty){
34841             this.hide();
34842             this.on("paneladded", this.validateVisibility, this);
34843             this.on("panelremoved", this.validateVisibility, this);
34844         }
34845         if(this.autoScroll){
34846             this.bodyEl.setStyle("overflow", "auto");
34847         }else{
34848             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34849         }
34850         //if(c.titlebar !== false){
34851             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34852                 this.titleEl.hide();
34853             }else{
34854                 this.titleEl.show();
34855                 if(this.config.title){
34856                     this.titleTextEl.innerHTML = this.config.title;
34857                 }
34858             }
34859         //}
34860         if(this.config.collapsed){
34861             this.collapse(true);
34862         }
34863         if(this.config.hidden){
34864             this.hide();
34865         }
34866         
34867         if (this.unrendered_panels && this.unrendered_panels.length) {
34868             for (var i =0;i< this.unrendered_panels.length; i++) {
34869                 this.add(this.unrendered_panels[i]);
34870             }
34871             this.unrendered_panels = null;
34872             
34873         }
34874         
34875     },
34876     
34877     applyConfig : function(c)
34878     {
34879         /*
34880          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34881             var dh = Roo.DomHelper;
34882             if(c.titlebar !== false){
34883                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34884                 this.collapseBtn.on("click", this.collapse, this);
34885                 this.collapseBtn.enableDisplayMode();
34886                 /*
34887                 if(c.showPin === true || this.showPin){
34888                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34889                     this.stickBtn.enableDisplayMode();
34890                     this.stickBtn.on("click", this.expand, this);
34891                     this.stickBtn.hide();
34892                 }
34893                 
34894             }
34895             */
34896             /** This region's collapsed element
34897             * @type Roo.Element */
34898             /*
34899              *
34900             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34901                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34902             ]}, true);
34903             
34904             if(c.floatable !== false){
34905                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34906                this.collapsedEl.on("click", this.collapseClick, this);
34907             }
34908
34909             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34910                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34911                    id: "message", unselectable: "on", style:{"float":"left"}});
34912                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34913              }
34914             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34915             this.expandBtn.on("click", this.expand, this);
34916             
34917         }
34918         
34919         if(this.collapseBtn){
34920             this.collapseBtn.setVisible(c.collapsible == true);
34921         }
34922         
34923         this.cmargins = c.cmargins || this.cmargins ||
34924                          (this.position == "west" || this.position == "east" ?
34925                              {top: 0, left: 2, right:2, bottom: 0} :
34926                              {top: 2, left: 0, right:0, bottom: 2});
34927         */
34928         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34929         
34930         
34931         this.bottomTabs = c.tabPosition != "top";
34932         
34933         this.autoScroll = c.autoScroll || false;
34934         
34935         
34936        
34937         
34938         this.duration = c.duration || .30;
34939         this.slideDuration = c.slideDuration || .45;
34940         this.config = c;
34941        
34942     },
34943     /**
34944      * Returns true if this region is currently visible.
34945      * @return {Boolean}
34946      */
34947     isVisible : function(){
34948         return this.visible;
34949     },
34950
34951     /**
34952      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34953      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34954      */
34955     //setCollapsedTitle : function(title){
34956     //    title = title || "&#160;";
34957      //   if(this.collapsedTitleTextEl){
34958       //      this.collapsedTitleTextEl.innerHTML = title;
34959        // }
34960     //},
34961
34962     getBox : function(){
34963         var b;
34964       //  if(!this.collapsed){
34965             b = this.el.getBox(false, true);
34966        // }else{
34967           //  b = this.collapsedEl.getBox(false, true);
34968         //}
34969         return b;
34970     },
34971
34972     getMargins : function(){
34973         return this.margins;
34974         //return this.collapsed ? this.cmargins : this.margins;
34975     },
34976 /*
34977     highlight : function(){
34978         this.el.addClass("x-layout-panel-dragover");
34979     },
34980
34981     unhighlight : function(){
34982         this.el.removeClass("x-layout-panel-dragover");
34983     },
34984 */
34985     updateBox : function(box)
34986     {
34987         if (!this.bodyEl) {
34988             return; // not rendered yet..
34989         }
34990         
34991         this.box = box;
34992         if(!this.collapsed){
34993             this.el.dom.style.left = box.x + "px";
34994             this.el.dom.style.top = box.y + "px";
34995             this.updateBody(box.width, box.height);
34996         }else{
34997             this.collapsedEl.dom.style.left = box.x + "px";
34998             this.collapsedEl.dom.style.top = box.y + "px";
34999             this.collapsedEl.setSize(box.width, box.height);
35000         }
35001         if(this.tabs){
35002             this.tabs.autoSizeTabs();
35003         }
35004     },
35005
35006     updateBody : function(w, h)
35007     {
35008         if(w !== null){
35009             this.el.setWidth(w);
35010             w -= this.el.getBorderWidth("rl");
35011             if(this.config.adjustments){
35012                 w += this.config.adjustments[0];
35013             }
35014         }
35015         if(h !== null && h > 0){
35016             this.el.setHeight(h);
35017             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35018             h -= this.el.getBorderWidth("tb");
35019             if(this.config.adjustments){
35020                 h += this.config.adjustments[1];
35021             }
35022             this.bodyEl.setHeight(h);
35023             if(this.tabs){
35024                 h = this.tabs.syncHeight(h);
35025             }
35026         }
35027         if(this.panelSize){
35028             w = w !== null ? w : this.panelSize.width;
35029             h = h !== null ? h : this.panelSize.height;
35030         }
35031         if(this.activePanel){
35032             var el = this.activePanel.getEl();
35033             w = w !== null ? w : el.getWidth();
35034             h = h !== null ? h : el.getHeight();
35035             this.panelSize = {width: w, height: h};
35036             this.activePanel.setSize(w, h);
35037         }
35038         if(Roo.isIE && this.tabs){
35039             this.tabs.el.repaint();
35040         }
35041     },
35042
35043     /**
35044      * Returns the container element for this region.
35045      * @return {Roo.Element}
35046      */
35047     getEl : function(){
35048         return this.el;
35049     },
35050
35051     /**
35052      * Hides this region.
35053      */
35054     hide : function(){
35055         //if(!this.collapsed){
35056             this.el.dom.style.left = "-2000px";
35057             this.el.hide();
35058         //}else{
35059          //   this.collapsedEl.dom.style.left = "-2000px";
35060          //   this.collapsedEl.hide();
35061        // }
35062         this.visible = false;
35063         this.fireEvent("visibilitychange", this, false);
35064     },
35065
35066     /**
35067      * Shows this region if it was previously hidden.
35068      */
35069     show : function(){
35070         //if(!this.collapsed){
35071             this.el.show();
35072         //}else{
35073         //    this.collapsedEl.show();
35074        // }
35075         this.visible = true;
35076         this.fireEvent("visibilitychange", this, true);
35077     },
35078 /*
35079     closeClicked : function(){
35080         if(this.activePanel){
35081             this.remove(this.activePanel);
35082         }
35083     },
35084
35085     collapseClick : function(e){
35086         if(this.isSlid){
35087            e.stopPropagation();
35088            this.slideIn();
35089         }else{
35090            e.stopPropagation();
35091            this.slideOut();
35092         }
35093     },
35094 */
35095     /**
35096      * Collapses this region.
35097      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35098      */
35099     /*
35100     collapse : function(skipAnim, skipCheck = false){
35101         if(this.collapsed) {
35102             return;
35103         }
35104         
35105         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35106             
35107             this.collapsed = true;
35108             if(this.split){
35109                 this.split.el.hide();
35110             }
35111             if(this.config.animate && skipAnim !== true){
35112                 this.fireEvent("invalidated", this);
35113                 this.animateCollapse();
35114             }else{
35115                 this.el.setLocation(-20000,-20000);
35116                 this.el.hide();
35117                 this.collapsedEl.show();
35118                 this.fireEvent("collapsed", this);
35119                 this.fireEvent("invalidated", this);
35120             }
35121         }
35122         
35123     },
35124 */
35125     animateCollapse : function(){
35126         // overridden
35127     },
35128
35129     /**
35130      * Expands this region if it was previously collapsed.
35131      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35132      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35133      */
35134     /*
35135     expand : function(e, skipAnim){
35136         if(e) {
35137             e.stopPropagation();
35138         }
35139         if(!this.collapsed || this.el.hasActiveFx()) {
35140             return;
35141         }
35142         if(this.isSlid){
35143             this.afterSlideIn();
35144             skipAnim = true;
35145         }
35146         this.collapsed = false;
35147         if(this.config.animate && skipAnim !== true){
35148             this.animateExpand();
35149         }else{
35150             this.el.show();
35151             if(this.split){
35152                 this.split.el.show();
35153             }
35154             this.collapsedEl.setLocation(-2000,-2000);
35155             this.collapsedEl.hide();
35156             this.fireEvent("invalidated", this);
35157             this.fireEvent("expanded", this);
35158         }
35159     },
35160 */
35161     animateExpand : function(){
35162         // overridden
35163     },
35164
35165     initTabs : function()
35166     {
35167         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35168         
35169         var ts = new Roo.bootstrap.panel.Tabs({
35170                 el: this.bodyEl.dom,
35171                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35172                 disableTooltips: this.config.disableTabTips,
35173                 toolbar : this.config.toolbar
35174             });
35175         
35176         if(this.config.hideTabs){
35177             ts.stripWrap.setDisplayed(false);
35178         }
35179         this.tabs = ts;
35180         ts.resizeTabs = this.config.resizeTabs === true;
35181         ts.minTabWidth = this.config.minTabWidth || 40;
35182         ts.maxTabWidth = this.config.maxTabWidth || 250;
35183         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35184         ts.monitorResize = false;
35185         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35186         ts.bodyEl.addClass('roo-layout-tabs-body');
35187         this.panels.each(this.initPanelAsTab, this);
35188     },
35189
35190     initPanelAsTab : function(panel){
35191         var ti = this.tabs.addTab(
35192             panel.getEl().id,
35193             panel.getTitle(),
35194             null,
35195             this.config.closeOnTab && panel.isClosable(),
35196             panel.tpl
35197         );
35198         if(panel.tabTip !== undefined){
35199             ti.setTooltip(panel.tabTip);
35200         }
35201         ti.on("activate", function(){
35202               this.setActivePanel(panel);
35203         }, this);
35204         
35205         if(this.config.closeOnTab){
35206             ti.on("beforeclose", function(t, e){
35207                 e.cancel = true;
35208                 this.remove(panel);
35209             }, this);
35210         }
35211         
35212         panel.tabItem = ti;
35213         
35214         return ti;
35215     },
35216
35217     updatePanelTitle : function(panel, title)
35218     {
35219         if(this.activePanel == panel){
35220             this.updateTitle(title);
35221         }
35222         if(this.tabs){
35223             var ti = this.tabs.getTab(panel.getEl().id);
35224             ti.setText(title);
35225             if(panel.tabTip !== undefined){
35226                 ti.setTooltip(panel.tabTip);
35227             }
35228         }
35229     },
35230
35231     updateTitle : function(title){
35232         if(this.titleTextEl && !this.config.title){
35233             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35234         }
35235     },
35236
35237     setActivePanel : function(panel)
35238     {
35239         panel = this.getPanel(panel);
35240         if(this.activePanel && this.activePanel != panel){
35241             this.activePanel.setActiveState(false);
35242         }
35243         this.activePanel = panel;
35244         panel.setActiveState(true);
35245         if(this.panelSize){
35246             panel.setSize(this.panelSize.width, this.panelSize.height);
35247         }
35248         if(this.closeBtn){
35249             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35250         }
35251         this.updateTitle(panel.getTitle());
35252         if(this.tabs){
35253             this.fireEvent("invalidated", this);
35254         }
35255         this.fireEvent("panelactivated", this, panel);
35256     },
35257
35258     /**
35259      * Shows the specified panel.
35260      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35261      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35262      */
35263     showPanel : function(panel)
35264     {
35265         panel = this.getPanel(panel);
35266         if(panel){
35267             if(this.tabs){
35268                 var tab = this.tabs.getTab(panel.getEl().id);
35269                 if(tab.isHidden()){
35270                     this.tabs.unhideTab(tab.id);
35271                 }
35272                 tab.activate();
35273             }else{
35274                 this.setActivePanel(panel);
35275             }
35276         }
35277         return panel;
35278     },
35279
35280     /**
35281      * Get the active panel for this region.
35282      * @return {Roo.ContentPanel} The active panel or null
35283      */
35284     getActivePanel : function(){
35285         return this.activePanel;
35286     },
35287
35288     validateVisibility : function(){
35289         if(this.panels.getCount() < 1){
35290             this.updateTitle("&#160;");
35291             this.closeBtn.hide();
35292             this.hide();
35293         }else{
35294             if(!this.isVisible()){
35295                 this.show();
35296             }
35297         }
35298     },
35299
35300     /**
35301      * Adds the passed ContentPanel(s) to this region.
35302      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35303      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35304      */
35305     add : function(panel)
35306     {
35307         if(arguments.length > 1){
35308             for(var i = 0, len = arguments.length; i < len; i++) {
35309                 this.add(arguments[i]);
35310             }
35311             return null;
35312         }
35313         
35314         // if we have not been rendered yet, then we can not really do much of this..
35315         if (!this.bodyEl) {
35316             this.unrendered_panels.push(panel);
35317             return panel;
35318         }
35319         
35320         
35321         
35322         
35323         if(this.hasPanel(panel)){
35324             this.showPanel(panel);
35325             return panel;
35326         }
35327         panel.setRegion(this);
35328         this.panels.add(panel);
35329        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35330             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35331             // and hide them... ???
35332             this.bodyEl.dom.appendChild(panel.getEl().dom);
35333             if(panel.background !== true){
35334                 this.setActivePanel(panel);
35335             }
35336             this.fireEvent("paneladded", this, panel);
35337             return panel;
35338         }
35339         */
35340         if(!this.tabs){
35341             this.initTabs();
35342         }else{
35343             this.initPanelAsTab(panel);
35344         }
35345         
35346         
35347         if(panel.background !== true){
35348             this.tabs.activate(panel.getEl().id);
35349         }
35350         this.fireEvent("paneladded", this, panel);
35351         return panel;
35352     },
35353
35354     /**
35355      * Hides the tab for the specified panel.
35356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35357      */
35358     hidePanel : function(panel){
35359         if(this.tabs && (panel = this.getPanel(panel))){
35360             this.tabs.hideTab(panel.getEl().id);
35361         }
35362     },
35363
35364     /**
35365      * Unhides the tab for a previously hidden panel.
35366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35367      */
35368     unhidePanel : function(panel){
35369         if(this.tabs && (panel = this.getPanel(panel))){
35370             this.tabs.unhideTab(panel.getEl().id);
35371         }
35372     },
35373
35374     clearPanels : function(){
35375         while(this.panels.getCount() > 0){
35376              this.remove(this.panels.first());
35377         }
35378     },
35379
35380     /**
35381      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35382      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35383      * @param {Boolean} preservePanel Overrides the config preservePanel option
35384      * @return {Roo.ContentPanel} The panel that was removed
35385      */
35386     remove : function(panel, preservePanel)
35387     {
35388         panel = this.getPanel(panel);
35389         if(!panel){
35390             return null;
35391         }
35392         var e = {};
35393         this.fireEvent("beforeremove", this, panel, e);
35394         if(e.cancel === true){
35395             return null;
35396         }
35397         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35398         var panelId = panel.getId();
35399         this.panels.removeKey(panelId);
35400         if(preservePanel){
35401             document.body.appendChild(panel.getEl().dom);
35402         }
35403         if(this.tabs){
35404             this.tabs.removeTab(panel.getEl().id);
35405         }else if (!preservePanel){
35406             this.bodyEl.dom.removeChild(panel.getEl().dom);
35407         }
35408         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35409             var p = this.panels.first();
35410             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35411             tempEl.appendChild(p.getEl().dom);
35412             this.bodyEl.update("");
35413             this.bodyEl.dom.appendChild(p.getEl().dom);
35414             tempEl = null;
35415             this.updateTitle(p.getTitle());
35416             this.tabs = null;
35417             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35418             this.setActivePanel(p);
35419         }
35420         panel.setRegion(null);
35421         if(this.activePanel == panel){
35422             this.activePanel = null;
35423         }
35424         if(this.config.autoDestroy !== false && preservePanel !== true){
35425             try{panel.destroy();}catch(e){}
35426         }
35427         this.fireEvent("panelremoved", this, panel);
35428         return panel;
35429     },
35430
35431     /**
35432      * Returns the TabPanel component used by this region
35433      * @return {Roo.TabPanel}
35434      */
35435     getTabs : function(){
35436         return this.tabs;
35437     },
35438
35439     createTool : function(parentEl, className){
35440         var btn = Roo.DomHelper.append(parentEl, {
35441             tag: "div",
35442             cls: "x-layout-tools-button",
35443             children: [ {
35444                 tag: "div",
35445                 cls: "roo-layout-tools-button-inner " + className,
35446                 html: "&#160;"
35447             }]
35448         }, true);
35449         btn.addClassOnOver("roo-layout-tools-button-over");
35450         return btn;
35451     }
35452 });/*
35453  * Based on:
35454  * Ext JS Library 1.1.1
35455  * Copyright(c) 2006-2007, Ext JS, LLC.
35456  *
35457  * Originally Released Under LGPL - original licence link has changed is not relivant.
35458  *
35459  * Fork - LGPL
35460  * <script type="text/javascript">
35461  */
35462  
35463
35464
35465 /**
35466  * @class Roo.SplitLayoutRegion
35467  * @extends Roo.LayoutRegion
35468  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35469  */
35470 Roo.bootstrap.layout.Split = function(config){
35471     this.cursor = config.cursor;
35472     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35473 };
35474
35475 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35476 {
35477     splitTip : "Drag to resize.",
35478     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35479     useSplitTips : false,
35480
35481     applyConfig : function(config){
35482         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35483     },
35484     
35485     onRender : function(ctr,pos) {
35486         
35487         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35488         if(!this.config.split){
35489             return;
35490         }
35491         if(!this.split){
35492             
35493             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35494                             tag: "div",
35495                             id: this.el.id + "-split",
35496                             cls: "roo-layout-split roo-layout-split-"+this.position,
35497                             html: "&#160;"
35498             });
35499             /** The SplitBar for this region 
35500             * @type Roo.SplitBar */
35501             // does not exist yet...
35502             Roo.log([this.position, this.orientation]);
35503             
35504             this.split = new Roo.bootstrap.SplitBar({
35505                 dragElement : splitEl,
35506                 resizingElement: this.el,
35507                 orientation : this.orientation
35508             });
35509             
35510             this.split.on("moved", this.onSplitMove, this);
35511             this.split.useShim = this.config.useShim === true;
35512             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35513             if(this.useSplitTips){
35514                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35515             }
35516             //if(config.collapsible){
35517             //    this.split.el.on("dblclick", this.collapse,  this);
35518             //}
35519         }
35520         if(typeof this.config.minSize != "undefined"){
35521             this.split.minSize = this.config.minSize;
35522         }
35523         if(typeof this.config.maxSize != "undefined"){
35524             this.split.maxSize = this.config.maxSize;
35525         }
35526         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35527             this.hideSplitter();
35528         }
35529         
35530     },
35531
35532     getHMaxSize : function(){
35533          var cmax = this.config.maxSize || 10000;
35534          var center = this.mgr.getRegion("center");
35535          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35536     },
35537
35538     getVMaxSize : function(){
35539          var cmax = this.config.maxSize || 10000;
35540          var center = this.mgr.getRegion("center");
35541          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35542     },
35543
35544     onSplitMove : function(split, newSize){
35545         this.fireEvent("resized", this, newSize);
35546     },
35547     
35548     /** 
35549      * Returns the {@link Roo.SplitBar} for this region.
35550      * @return {Roo.SplitBar}
35551      */
35552     getSplitBar : function(){
35553         return this.split;
35554     },
35555     
35556     hide : function(){
35557         this.hideSplitter();
35558         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35559     },
35560
35561     hideSplitter : function(){
35562         if(this.split){
35563             this.split.el.setLocation(-2000,-2000);
35564             this.split.el.hide();
35565         }
35566     },
35567
35568     show : function(){
35569         if(this.split){
35570             this.split.el.show();
35571         }
35572         Roo.bootstrap.layout.Split.superclass.show.call(this);
35573     },
35574     
35575     beforeSlide: function(){
35576         if(Roo.isGecko){// firefox overflow auto bug workaround
35577             this.bodyEl.clip();
35578             if(this.tabs) {
35579                 this.tabs.bodyEl.clip();
35580             }
35581             if(this.activePanel){
35582                 this.activePanel.getEl().clip();
35583                 
35584                 if(this.activePanel.beforeSlide){
35585                     this.activePanel.beforeSlide();
35586                 }
35587             }
35588         }
35589     },
35590     
35591     afterSlide : function(){
35592         if(Roo.isGecko){// firefox overflow auto bug workaround
35593             this.bodyEl.unclip();
35594             if(this.tabs) {
35595                 this.tabs.bodyEl.unclip();
35596             }
35597             if(this.activePanel){
35598                 this.activePanel.getEl().unclip();
35599                 if(this.activePanel.afterSlide){
35600                     this.activePanel.afterSlide();
35601                 }
35602             }
35603         }
35604     },
35605
35606     initAutoHide : function(){
35607         if(this.autoHide !== false){
35608             if(!this.autoHideHd){
35609                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35610                 this.autoHideHd = {
35611                     "mouseout": function(e){
35612                         if(!e.within(this.el, true)){
35613                             st.delay(500);
35614                         }
35615                     },
35616                     "mouseover" : function(e){
35617                         st.cancel();
35618                     },
35619                     scope : this
35620                 };
35621             }
35622             this.el.on(this.autoHideHd);
35623         }
35624     },
35625
35626     clearAutoHide : function(){
35627         if(this.autoHide !== false){
35628             this.el.un("mouseout", this.autoHideHd.mouseout);
35629             this.el.un("mouseover", this.autoHideHd.mouseover);
35630         }
35631     },
35632
35633     clearMonitor : function(){
35634         Roo.get(document).un("click", this.slideInIf, this);
35635     },
35636
35637     // these names are backwards but not changed for compat
35638     slideOut : function(){
35639         if(this.isSlid || this.el.hasActiveFx()){
35640             return;
35641         }
35642         this.isSlid = true;
35643         if(this.collapseBtn){
35644             this.collapseBtn.hide();
35645         }
35646         this.closeBtnState = this.closeBtn.getStyle('display');
35647         this.closeBtn.hide();
35648         if(this.stickBtn){
35649             this.stickBtn.show();
35650         }
35651         this.el.show();
35652         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35653         this.beforeSlide();
35654         this.el.setStyle("z-index", 10001);
35655         this.el.slideIn(this.getSlideAnchor(), {
35656             callback: function(){
35657                 this.afterSlide();
35658                 this.initAutoHide();
35659                 Roo.get(document).on("click", this.slideInIf, this);
35660                 this.fireEvent("slideshow", this);
35661             },
35662             scope: this,
35663             block: true
35664         });
35665     },
35666
35667     afterSlideIn : function(){
35668         this.clearAutoHide();
35669         this.isSlid = false;
35670         this.clearMonitor();
35671         this.el.setStyle("z-index", "");
35672         if(this.collapseBtn){
35673             this.collapseBtn.show();
35674         }
35675         this.closeBtn.setStyle('display', this.closeBtnState);
35676         if(this.stickBtn){
35677             this.stickBtn.hide();
35678         }
35679         this.fireEvent("slidehide", this);
35680     },
35681
35682     slideIn : function(cb){
35683         if(!this.isSlid || this.el.hasActiveFx()){
35684             Roo.callback(cb);
35685             return;
35686         }
35687         this.isSlid = false;
35688         this.beforeSlide();
35689         this.el.slideOut(this.getSlideAnchor(), {
35690             callback: function(){
35691                 this.el.setLeftTop(-10000, -10000);
35692                 this.afterSlide();
35693                 this.afterSlideIn();
35694                 Roo.callback(cb);
35695             },
35696             scope: this,
35697             block: true
35698         });
35699     },
35700     
35701     slideInIf : function(e){
35702         if(!e.within(this.el)){
35703             this.slideIn();
35704         }
35705     },
35706
35707     animateCollapse : function(){
35708         this.beforeSlide();
35709         this.el.setStyle("z-index", 20000);
35710         var anchor = this.getSlideAnchor();
35711         this.el.slideOut(anchor, {
35712             callback : function(){
35713                 this.el.setStyle("z-index", "");
35714                 this.collapsedEl.slideIn(anchor, {duration:.3});
35715                 this.afterSlide();
35716                 this.el.setLocation(-10000,-10000);
35717                 this.el.hide();
35718                 this.fireEvent("collapsed", this);
35719             },
35720             scope: this,
35721             block: true
35722         });
35723     },
35724
35725     animateExpand : function(){
35726         this.beforeSlide();
35727         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35728         this.el.setStyle("z-index", 20000);
35729         this.collapsedEl.hide({
35730             duration:.1
35731         });
35732         this.el.slideIn(this.getSlideAnchor(), {
35733             callback : function(){
35734                 this.el.setStyle("z-index", "");
35735                 this.afterSlide();
35736                 if(this.split){
35737                     this.split.el.show();
35738                 }
35739                 this.fireEvent("invalidated", this);
35740                 this.fireEvent("expanded", this);
35741             },
35742             scope: this,
35743             block: true
35744         });
35745     },
35746
35747     anchors : {
35748         "west" : "left",
35749         "east" : "right",
35750         "north" : "top",
35751         "south" : "bottom"
35752     },
35753
35754     sanchors : {
35755         "west" : "l",
35756         "east" : "r",
35757         "north" : "t",
35758         "south" : "b"
35759     },
35760
35761     canchors : {
35762         "west" : "tl-tr",
35763         "east" : "tr-tl",
35764         "north" : "tl-bl",
35765         "south" : "bl-tl"
35766     },
35767
35768     getAnchor : function(){
35769         return this.anchors[this.position];
35770     },
35771
35772     getCollapseAnchor : function(){
35773         return this.canchors[this.position];
35774     },
35775
35776     getSlideAnchor : function(){
35777         return this.sanchors[this.position];
35778     },
35779
35780     getAlignAdj : function(){
35781         var cm = this.cmargins;
35782         switch(this.position){
35783             case "west":
35784                 return [0, 0];
35785             break;
35786             case "east":
35787                 return [0, 0];
35788             break;
35789             case "north":
35790                 return [0, 0];
35791             break;
35792             case "south":
35793                 return [0, 0];
35794             break;
35795         }
35796     },
35797
35798     getExpandAdj : function(){
35799         var c = this.collapsedEl, cm = this.cmargins;
35800         switch(this.position){
35801             case "west":
35802                 return [-(cm.right+c.getWidth()+cm.left), 0];
35803             break;
35804             case "east":
35805                 return [cm.right+c.getWidth()+cm.left, 0];
35806             break;
35807             case "north":
35808                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35809             break;
35810             case "south":
35811                 return [0, cm.top+cm.bottom+c.getHeight()];
35812             break;
35813         }
35814     }
35815 });/*
35816  * Based on:
35817  * Ext JS Library 1.1.1
35818  * Copyright(c) 2006-2007, Ext JS, LLC.
35819  *
35820  * Originally Released Under LGPL - original licence link has changed is not relivant.
35821  *
35822  * Fork - LGPL
35823  * <script type="text/javascript">
35824  */
35825 /*
35826  * These classes are private internal classes
35827  */
35828 Roo.bootstrap.layout.Center = function(config){
35829     config.region = "center";
35830     Roo.bootstrap.layout.Region.call(this, config);
35831     this.visible = true;
35832     this.minWidth = config.minWidth || 20;
35833     this.minHeight = config.minHeight || 20;
35834 };
35835
35836 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35837     hide : function(){
35838         // center panel can't be hidden
35839     },
35840     
35841     show : function(){
35842         // center panel can't be hidden
35843     },
35844     
35845     getMinWidth: function(){
35846         return this.minWidth;
35847     },
35848     
35849     getMinHeight: function(){
35850         return this.minHeight;
35851     }
35852 });
35853
35854
35855
35856
35857  
35858
35859
35860
35861
35862
35863 Roo.bootstrap.layout.North = function(config)
35864 {
35865     config.region = 'north';
35866     config.cursor = 'n-resize';
35867     
35868     Roo.bootstrap.layout.Split.call(this, config);
35869     
35870     
35871     if(this.split){
35872         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35873         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35874         this.split.el.addClass("roo-layout-split-v");
35875     }
35876     var size = config.initialSize || config.height;
35877     if(typeof size != "undefined"){
35878         this.el.setHeight(size);
35879     }
35880 };
35881 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35882 {
35883     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35884     
35885     
35886     
35887     getBox : function(){
35888         if(this.collapsed){
35889             return this.collapsedEl.getBox();
35890         }
35891         var box = this.el.getBox();
35892         if(this.split){
35893             box.height += this.split.el.getHeight();
35894         }
35895         return box;
35896     },
35897     
35898     updateBox : function(box){
35899         if(this.split && !this.collapsed){
35900             box.height -= this.split.el.getHeight();
35901             this.split.el.setLeft(box.x);
35902             this.split.el.setTop(box.y+box.height);
35903             this.split.el.setWidth(box.width);
35904         }
35905         if(this.collapsed){
35906             this.updateBody(box.width, null);
35907         }
35908         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35909     }
35910 });
35911
35912
35913
35914
35915
35916 Roo.bootstrap.layout.South = function(config){
35917     config.region = 'south';
35918     config.cursor = 's-resize';
35919     Roo.bootstrap.layout.Split.call(this, config);
35920     if(this.split){
35921         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35922         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35923         this.split.el.addClass("roo-layout-split-v");
35924     }
35925     var size = config.initialSize || config.height;
35926     if(typeof size != "undefined"){
35927         this.el.setHeight(size);
35928     }
35929 };
35930
35931 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35932     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35933     getBox : function(){
35934         if(this.collapsed){
35935             return this.collapsedEl.getBox();
35936         }
35937         var box = this.el.getBox();
35938         if(this.split){
35939             var sh = this.split.el.getHeight();
35940             box.height += sh;
35941             box.y -= sh;
35942         }
35943         return box;
35944     },
35945     
35946     updateBox : function(box){
35947         if(this.split && !this.collapsed){
35948             var sh = this.split.el.getHeight();
35949             box.height -= sh;
35950             box.y += sh;
35951             this.split.el.setLeft(box.x);
35952             this.split.el.setTop(box.y-sh);
35953             this.split.el.setWidth(box.width);
35954         }
35955         if(this.collapsed){
35956             this.updateBody(box.width, null);
35957         }
35958         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35959     }
35960 });
35961
35962 Roo.bootstrap.layout.East = function(config){
35963     config.region = "east";
35964     config.cursor = "e-resize";
35965     Roo.bootstrap.layout.Split.call(this, config);
35966     if(this.split){
35967         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35968         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35969         this.split.el.addClass("roo-layout-split-h");
35970     }
35971     var size = config.initialSize || config.width;
35972     if(typeof size != "undefined"){
35973         this.el.setWidth(size);
35974     }
35975 };
35976 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35977     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35978     getBox : function(){
35979         if(this.collapsed){
35980             return this.collapsedEl.getBox();
35981         }
35982         var box = this.el.getBox();
35983         if(this.split){
35984             var sw = this.split.el.getWidth();
35985             box.width += sw;
35986             box.x -= sw;
35987         }
35988         return box;
35989     },
35990
35991     updateBox : function(box){
35992         if(this.split && !this.collapsed){
35993             var sw = this.split.el.getWidth();
35994             box.width -= sw;
35995             this.split.el.setLeft(box.x);
35996             this.split.el.setTop(box.y);
35997             this.split.el.setHeight(box.height);
35998             box.x += sw;
35999         }
36000         if(this.collapsed){
36001             this.updateBody(null, box.height);
36002         }
36003         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36004     }
36005 });
36006
36007 Roo.bootstrap.layout.West = function(config){
36008     config.region = "west";
36009     config.cursor = "w-resize";
36010     
36011     Roo.bootstrap.layout.Split.call(this, config);
36012     if(this.split){
36013         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36014         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36015         this.split.el.addClass("roo-layout-split-h");
36016     }
36017     
36018 };
36019 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36020     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36021     
36022     onRender: function(ctr, pos)
36023     {
36024         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36025         var size = this.config.initialSize || this.config.width;
36026         if(typeof size != "undefined"){
36027             this.el.setWidth(size);
36028         }
36029     },
36030     
36031     getBox : function(){
36032         if(this.collapsed){
36033             return this.collapsedEl.getBox();
36034         }
36035         var box = this.el.getBox();
36036         if(this.split){
36037             box.width += this.split.el.getWidth();
36038         }
36039         return box;
36040     },
36041     
36042     updateBox : function(box){
36043         if(this.split && !this.collapsed){
36044             var sw = this.split.el.getWidth();
36045             box.width -= sw;
36046             this.split.el.setLeft(box.x+box.width);
36047             this.split.el.setTop(box.y);
36048             this.split.el.setHeight(box.height);
36049         }
36050         if(this.collapsed){
36051             this.updateBody(null, box.height);
36052         }
36053         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36054     }
36055 });
36056 Roo.namespace("Roo.bootstrap.panel");/*
36057  * Based on:
36058  * Ext JS Library 1.1.1
36059  * Copyright(c) 2006-2007, Ext JS, LLC.
36060  *
36061  * Originally Released Under LGPL - original licence link has changed is not relivant.
36062  *
36063  * Fork - LGPL
36064  * <script type="text/javascript">
36065  */
36066 /**
36067  * @class Roo.ContentPanel
36068  * @extends Roo.util.Observable
36069  * A basic ContentPanel element.
36070  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36071  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36072  * @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
36073  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36074  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36075  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36076  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36077  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36078  * @cfg {String} title          The title for this panel
36079  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36080  * @cfg {String} url            Calls {@link #setUrl} with this value
36081  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36082  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36083  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36084  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36085  * @cfg {Boolean} badges render the badges
36086
36087  * @constructor
36088  * Create a new ContentPanel.
36089  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36090  * @param {String/Object} config A string to set only the title or a config object
36091  * @param {String} content (optional) Set the HTML content for this panel
36092  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36093  */
36094 Roo.bootstrap.panel.Content = function( config){
36095     
36096     this.tpl = config.tpl || false;
36097     
36098     var el = config.el;
36099     var content = config.content;
36100
36101     if(config.autoCreate){ // xtype is available if this is called from factory
36102         el = Roo.id();
36103     }
36104     this.el = Roo.get(el);
36105     if(!this.el && config && config.autoCreate){
36106         if(typeof config.autoCreate == "object"){
36107             if(!config.autoCreate.id){
36108                 config.autoCreate.id = config.id||el;
36109             }
36110             this.el = Roo.DomHelper.append(document.body,
36111                         config.autoCreate, true);
36112         }else{
36113             var elcfg =  {   tag: "div",
36114                             cls: "roo-layout-inactive-content",
36115                             id: config.id||el
36116                             };
36117             if (config.html) {
36118                 elcfg.html = config.html;
36119                 
36120             }
36121                         
36122             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36123         }
36124     } 
36125     this.closable = false;
36126     this.loaded = false;
36127     this.active = false;
36128    
36129       
36130     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36131         
36132         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36133         
36134         this.wrapEl = this.el; //this.el.wrap();
36135         var ti = [];
36136         if (config.toolbar.items) {
36137             ti = config.toolbar.items ;
36138             delete config.toolbar.items ;
36139         }
36140         
36141         var nitems = [];
36142         this.toolbar.render(this.wrapEl, 'before');
36143         for(var i =0;i < ti.length;i++) {
36144           //  Roo.log(['add child', items[i]]);
36145             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36146         }
36147         this.toolbar.items = nitems;
36148         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36149         delete config.toolbar;
36150         
36151     }
36152     /*
36153     // xtype created footer. - not sure if will work as we normally have to render first..
36154     if (this.footer && !this.footer.el && this.footer.xtype) {
36155         if (!this.wrapEl) {
36156             this.wrapEl = this.el.wrap();
36157         }
36158     
36159         this.footer.container = this.wrapEl.createChild();
36160          
36161         this.footer = Roo.factory(this.footer, Roo);
36162         
36163     }
36164     */
36165     
36166      if(typeof config == "string"){
36167         this.title = config;
36168     }else{
36169         Roo.apply(this, config);
36170     }
36171     
36172     if(this.resizeEl){
36173         this.resizeEl = Roo.get(this.resizeEl, true);
36174     }else{
36175         this.resizeEl = this.el;
36176     }
36177     // handle view.xtype
36178     
36179  
36180     
36181     
36182     this.addEvents({
36183         /**
36184          * @event activate
36185          * Fires when this panel is activated. 
36186          * @param {Roo.ContentPanel} this
36187          */
36188         "activate" : true,
36189         /**
36190          * @event deactivate
36191          * Fires when this panel is activated. 
36192          * @param {Roo.ContentPanel} this
36193          */
36194         "deactivate" : true,
36195
36196         /**
36197          * @event resize
36198          * Fires when this panel is resized if fitToFrame is true.
36199          * @param {Roo.ContentPanel} this
36200          * @param {Number} width The width after any component adjustments
36201          * @param {Number} height The height after any component adjustments
36202          */
36203         "resize" : true,
36204         
36205          /**
36206          * @event render
36207          * Fires when this tab is created
36208          * @param {Roo.ContentPanel} this
36209          */
36210         "render" : true
36211         
36212         
36213         
36214     });
36215     
36216
36217     
36218     
36219     if(this.autoScroll){
36220         this.resizeEl.setStyle("overflow", "auto");
36221     } else {
36222         // fix randome scrolling
36223         //this.el.on('scroll', function() {
36224         //    Roo.log('fix random scolling');
36225         //    this.scrollTo('top',0); 
36226         //});
36227     }
36228     content = content || this.content;
36229     if(content){
36230         this.setContent(content);
36231     }
36232     if(config && config.url){
36233         this.setUrl(this.url, this.params, this.loadOnce);
36234     }
36235     
36236     
36237     
36238     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36239     
36240     if (this.view && typeof(this.view.xtype) != 'undefined') {
36241         this.view.el = this.el.appendChild(document.createElement("div"));
36242         this.view = Roo.factory(this.view); 
36243         this.view.render  &&  this.view.render(false, '');  
36244     }
36245     
36246     
36247     this.fireEvent('render', this);
36248 };
36249
36250 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36251     
36252     tabTip : '',
36253     
36254     setRegion : function(region){
36255         this.region = region;
36256         this.setActiveClass(region && !this.background);
36257     },
36258     
36259     
36260     setActiveClass: function(state)
36261     {
36262         if(state){
36263            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36264            this.el.setStyle('position','relative');
36265         }else{
36266            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36267            this.el.setStyle('position', 'absolute');
36268         } 
36269     },
36270     
36271     /**
36272      * Returns the toolbar for this Panel if one was configured. 
36273      * @return {Roo.Toolbar} 
36274      */
36275     getToolbar : function(){
36276         return this.toolbar;
36277     },
36278     
36279     setActiveState : function(active)
36280     {
36281         this.active = active;
36282         this.setActiveClass(active);
36283         if(!active){
36284             this.fireEvent("deactivate", this);
36285         }else{
36286             this.fireEvent("activate", this);
36287         }
36288     },
36289     /**
36290      * Updates this panel's element
36291      * @param {String} content The new content
36292      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36293     */
36294     setContent : function(content, loadScripts){
36295         this.el.update(content, loadScripts);
36296     },
36297
36298     ignoreResize : function(w, h){
36299         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36300             return true;
36301         }else{
36302             this.lastSize = {width: w, height: h};
36303             return false;
36304         }
36305     },
36306     /**
36307      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36308      * @return {Roo.UpdateManager} The UpdateManager
36309      */
36310     getUpdateManager : function(){
36311         return this.el.getUpdateManager();
36312     },
36313      /**
36314      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36315      * @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:
36316 <pre><code>
36317 panel.load({
36318     url: "your-url.php",
36319     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36320     callback: yourFunction,
36321     scope: yourObject, //(optional scope)
36322     discardUrl: false,
36323     nocache: false,
36324     text: "Loading...",
36325     timeout: 30,
36326     scripts: false
36327 });
36328 </code></pre>
36329      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36330      * 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.
36331      * @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}
36332      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36333      * @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.
36334      * @return {Roo.ContentPanel} this
36335      */
36336     load : function(){
36337         var um = this.el.getUpdateManager();
36338         um.update.apply(um, arguments);
36339         return this;
36340     },
36341
36342
36343     /**
36344      * 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.
36345      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36346      * @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)
36347      * @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)
36348      * @return {Roo.UpdateManager} The UpdateManager
36349      */
36350     setUrl : function(url, params, loadOnce){
36351         if(this.refreshDelegate){
36352             this.removeListener("activate", this.refreshDelegate);
36353         }
36354         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36355         this.on("activate", this.refreshDelegate);
36356         return this.el.getUpdateManager();
36357     },
36358     
36359     _handleRefresh : function(url, params, loadOnce){
36360         if(!loadOnce || !this.loaded){
36361             var updater = this.el.getUpdateManager();
36362             updater.update(url, params, this._setLoaded.createDelegate(this));
36363         }
36364     },
36365     
36366     _setLoaded : function(){
36367         this.loaded = true;
36368     }, 
36369     
36370     /**
36371      * Returns this panel's id
36372      * @return {String} 
36373      */
36374     getId : function(){
36375         return this.el.id;
36376     },
36377     
36378     /** 
36379      * Returns this panel's element - used by regiosn to add.
36380      * @return {Roo.Element} 
36381      */
36382     getEl : function(){
36383         return this.wrapEl || this.el;
36384     },
36385     
36386    
36387     
36388     adjustForComponents : function(width, height)
36389     {
36390         //Roo.log('adjustForComponents ');
36391         if(this.resizeEl != this.el){
36392             width -= this.el.getFrameWidth('lr');
36393             height -= this.el.getFrameWidth('tb');
36394         }
36395         if(this.toolbar){
36396             var te = this.toolbar.getEl();
36397             te.setWidth(width);
36398             height -= te.getHeight();
36399         }
36400         if(this.footer){
36401             var te = this.footer.getEl();
36402             te.setWidth(width);
36403             height -= te.getHeight();
36404         }
36405         
36406         
36407         if(this.adjustments){
36408             width += this.adjustments[0];
36409             height += this.adjustments[1];
36410         }
36411         return {"width": width, "height": height};
36412     },
36413     
36414     setSize : function(width, height){
36415         if(this.fitToFrame && !this.ignoreResize(width, height)){
36416             if(this.fitContainer && this.resizeEl != this.el){
36417                 this.el.setSize(width, height);
36418             }
36419             var size = this.adjustForComponents(width, height);
36420             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36421             this.fireEvent('resize', this, size.width, size.height);
36422         }
36423     },
36424     
36425     /**
36426      * Returns this panel's title
36427      * @return {String} 
36428      */
36429     getTitle : function(){
36430         
36431         if (typeof(this.title) != 'object') {
36432             return this.title;
36433         }
36434         
36435         var t = '';
36436         for (var k in this.title) {
36437             if (!this.title.hasOwnProperty(k)) {
36438                 continue;
36439             }
36440             
36441             if (k.indexOf('-') >= 0) {
36442                 var s = k.split('-');
36443                 for (var i = 0; i<s.length; i++) {
36444                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36445                 }
36446             } else {
36447                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36448             }
36449         }
36450         return t;
36451     },
36452     
36453     /**
36454      * Set this panel's title
36455      * @param {String} title
36456      */
36457     setTitle : function(title){
36458         this.title = title;
36459         if(this.region){
36460             this.region.updatePanelTitle(this, title);
36461         }
36462     },
36463     
36464     /**
36465      * Returns true is this panel was configured to be closable
36466      * @return {Boolean} 
36467      */
36468     isClosable : function(){
36469         return this.closable;
36470     },
36471     
36472     beforeSlide : function(){
36473         this.el.clip();
36474         this.resizeEl.clip();
36475     },
36476     
36477     afterSlide : function(){
36478         this.el.unclip();
36479         this.resizeEl.unclip();
36480     },
36481     
36482     /**
36483      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36484      *   Will fail silently if the {@link #setUrl} method has not been called.
36485      *   This does not activate the panel, just updates its content.
36486      */
36487     refresh : function(){
36488         if(this.refreshDelegate){
36489            this.loaded = false;
36490            this.refreshDelegate();
36491         }
36492     },
36493     
36494     /**
36495      * Destroys this panel
36496      */
36497     destroy : function(){
36498         this.el.removeAllListeners();
36499         var tempEl = document.createElement("span");
36500         tempEl.appendChild(this.el.dom);
36501         tempEl.innerHTML = "";
36502         this.el.remove();
36503         this.el = null;
36504     },
36505     
36506     /**
36507      * form - if the content panel contains a form - this is a reference to it.
36508      * @type {Roo.form.Form}
36509      */
36510     form : false,
36511     /**
36512      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36513      *    This contains a reference to it.
36514      * @type {Roo.View}
36515      */
36516     view : false,
36517     
36518       /**
36519      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36520      * <pre><code>
36521
36522 layout.addxtype({
36523        xtype : 'Form',
36524        items: [ .... ]
36525    }
36526 );
36527
36528 </code></pre>
36529      * @param {Object} cfg Xtype definition of item to add.
36530      */
36531     
36532     
36533     getChildContainer: function () {
36534         return this.getEl();
36535     }
36536     
36537     
36538     /*
36539         var  ret = new Roo.factory(cfg);
36540         return ret;
36541         
36542         
36543         // add form..
36544         if (cfg.xtype.match(/^Form$/)) {
36545             
36546             var el;
36547             //if (this.footer) {
36548             //    el = this.footer.container.insertSibling(false, 'before');
36549             //} else {
36550                 el = this.el.createChild();
36551             //}
36552
36553             this.form = new  Roo.form.Form(cfg);
36554             
36555             
36556             if ( this.form.allItems.length) {
36557                 this.form.render(el.dom);
36558             }
36559             return this.form;
36560         }
36561         // should only have one of theses..
36562         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36563             // views.. should not be just added - used named prop 'view''
36564             
36565             cfg.el = this.el.appendChild(document.createElement("div"));
36566             // factory?
36567             
36568             var ret = new Roo.factory(cfg);
36569              
36570              ret.render && ret.render(false, ''); // render blank..
36571             this.view = ret;
36572             return ret;
36573         }
36574         return false;
36575     }
36576     \*/
36577 });
36578  
36579 /**
36580  * @class Roo.bootstrap.panel.Grid
36581  * @extends Roo.bootstrap.panel.Content
36582  * @constructor
36583  * Create a new GridPanel.
36584  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36585  * @param {Object} config A the config object
36586   
36587  */
36588
36589
36590
36591 Roo.bootstrap.panel.Grid = function(config)
36592 {
36593     
36594       
36595     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36596         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36597
36598     config.el = this.wrapper;
36599     //this.el = this.wrapper;
36600     
36601       if (config.container) {
36602         // ctor'ed from a Border/panel.grid
36603         
36604         
36605         this.wrapper.setStyle("overflow", "hidden");
36606         this.wrapper.addClass('roo-grid-container');
36607
36608     }
36609     
36610     
36611     if(config.toolbar){
36612         var tool_el = this.wrapper.createChild();    
36613         this.toolbar = Roo.factory(config.toolbar);
36614         var ti = [];
36615         if (config.toolbar.items) {
36616             ti = config.toolbar.items ;
36617             delete config.toolbar.items ;
36618         }
36619         
36620         var nitems = [];
36621         this.toolbar.render(tool_el);
36622         for(var i =0;i < ti.length;i++) {
36623           //  Roo.log(['add child', items[i]]);
36624             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36625         }
36626         this.toolbar.items = nitems;
36627         
36628         delete config.toolbar;
36629     }
36630     
36631     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36632     config.grid.scrollBody = true;;
36633     config.grid.monitorWindowResize = false; // turn off autosizing
36634     config.grid.autoHeight = false;
36635     config.grid.autoWidth = false;
36636     
36637     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36638     
36639     if (config.background) {
36640         // render grid on panel activation (if panel background)
36641         this.on('activate', function(gp) {
36642             if (!gp.grid.rendered) {
36643                 gp.grid.render(this.wrapper);
36644                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36645             }
36646         });
36647             
36648     } else {
36649         this.grid.render(this.wrapper);
36650         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36651
36652     }
36653     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36654     // ??? needed ??? config.el = this.wrapper;
36655     
36656     
36657     
36658   
36659     // xtype created footer. - not sure if will work as we normally have to render first..
36660     if (this.footer && !this.footer.el && this.footer.xtype) {
36661         
36662         var ctr = this.grid.getView().getFooterPanel(true);
36663         this.footer.dataSource = this.grid.dataSource;
36664         this.footer = Roo.factory(this.footer, Roo);
36665         this.footer.render(ctr);
36666         
36667     }
36668     
36669     
36670     
36671     
36672      
36673 };
36674
36675 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36676     getId : function(){
36677         return this.grid.id;
36678     },
36679     
36680     /**
36681      * Returns the grid for this panel
36682      * @return {Roo.bootstrap.Table} 
36683      */
36684     getGrid : function(){
36685         return this.grid;    
36686     },
36687     
36688     setSize : function(width, height){
36689         if(!this.ignoreResize(width, height)){
36690             var grid = this.grid;
36691             var size = this.adjustForComponents(width, height);
36692             var gridel = grid.getGridEl();
36693             gridel.setSize(size.width, size.height);
36694             /*
36695             var thd = grid.getGridEl().select('thead',true).first();
36696             var tbd = grid.getGridEl().select('tbody', true).first();
36697             if (tbd) {
36698                 tbd.setSize(width, height - thd.getHeight());
36699             }
36700             */
36701             grid.autoSize();
36702         }
36703     },
36704      
36705     
36706     
36707     beforeSlide : function(){
36708         this.grid.getView().scroller.clip();
36709     },
36710     
36711     afterSlide : function(){
36712         this.grid.getView().scroller.unclip();
36713     },
36714     
36715     destroy : function(){
36716         this.grid.destroy();
36717         delete this.grid;
36718         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36719     }
36720 });
36721
36722 /**
36723  * @class Roo.bootstrap.panel.Nest
36724  * @extends Roo.bootstrap.panel.Content
36725  * @constructor
36726  * Create a new Panel, that can contain a layout.Border.
36727  * 
36728  * 
36729  * @param {Roo.BorderLayout} layout The layout for this panel
36730  * @param {String/Object} config A string to set only the title or a config object
36731  */
36732 Roo.bootstrap.panel.Nest = function(config)
36733 {
36734     // construct with only one argument..
36735     /* FIXME - implement nicer consturctors
36736     if (layout.layout) {
36737         config = layout;
36738         layout = config.layout;
36739         delete config.layout;
36740     }
36741     if (layout.xtype && !layout.getEl) {
36742         // then layout needs constructing..
36743         layout = Roo.factory(layout, Roo);
36744     }
36745     */
36746     
36747     config.el =  config.layout.getEl();
36748     
36749     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36750     
36751     config.layout.monitorWindowResize = false; // turn off autosizing
36752     this.layout = config.layout;
36753     this.layout.getEl().addClass("roo-layout-nested-layout");
36754     
36755     
36756     
36757     
36758 };
36759
36760 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36761
36762     setSize : function(width, height){
36763         if(!this.ignoreResize(width, height)){
36764             var size = this.adjustForComponents(width, height);
36765             var el = this.layout.getEl();
36766             if (size.height < 1) {
36767                 el.setWidth(size.width);   
36768             } else {
36769                 el.setSize(size.width, size.height);
36770             }
36771             var touch = el.dom.offsetWidth;
36772             this.layout.layout();
36773             // ie requires a double layout on the first pass
36774             if(Roo.isIE && !this.initialized){
36775                 this.initialized = true;
36776                 this.layout.layout();
36777             }
36778         }
36779     },
36780     
36781     // activate all subpanels if not currently active..
36782     
36783     setActiveState : function(active){
36784         this.active = active;
36785         this.setActiveClass(active);
36786         
36787         if(!active){
36788             this.fireEvent("deactivate", this);
36789             return;
36790         }
36791         
36792         this.fireEvent("activate", this);
36793         // not sure if this should happen before or after..
36794         if (!this.layout) {
36795             return; // should not happen..
36796         }
36797         var reg = false;
36798         for (var r in this.layout.regions) {
36799             reg = this.layout.getRegion(r);
36800             if (reg.getActivePanel()) {
36801                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36802                 reg.setActivePanel(reg.getActivePanel());
36803                 continue;
36804             }
36805             if (!reg.panels.length) {
36806                 continue;
36807             }
36808             reg.showPanel(reg.getPanel(0));
36809         }
36810         
36811         
36812         
36813         
36814     },
36815     
36816     /**
36817      * Returns the nested BorderLayout for this panel
36818      * @return {Roo.BorderLayout} 
36819      */
36820     getLayout : function(){
36821         return this.layout;
36822     },
36823     
36824      /**
36825      * Adds a xtype elements to the layout of the nested panel
36826      * <pre><code>
36827
36828 panel.addxtype({
36829        xtype : 'ContentPanel',
36830        region: 'west',
36831        items: [ .... ]
36832    }
36833 );
36834
36835 panel.addxtype({
36836         xtype : 'NestedLayoutPanel',
36837         region: 'west',
36838         layout: {
36839            center: { },
36840            west: { }   
36841         },
36842         items : [ ... list of content panels or nested layout panels.. ]
36843    }
36844 );
36845 </code></pre>
36846      * @param {Object} cfg Xtype definition of item to add.
36847      */
36848     addxtype : function(cfg) {
36849         return this.layout.addxtype(cfg);
36850     
36851     }
36852 });        /*
36853  * Based on:
36854  * Ext JS Library 1.1.1
36855  * Copyright(c) 2006-2007, Ext JS, LLC.
36856  *
36857  * Originally Released Under LGPL - original licence link has changed is not relivant.
36858  *
36859  * Fork - LGPL
36860  * <script type="text/javascript">
36861  */
36862 /**
36863  * @class Roo.TabPanel
36864  * @extends Roo.util.Observable
36865  * A lightweight tab container.
36866  * <br><br>
36867  * Usage:
36868  * <pre><code>
36869 // basic tabs 1, built from existing content
36870 var tabs = new Roo.TabPanel("tabs1");
36871 tabs.addTab("script", "View Script");
36872 tabs.addTab("markup", "View Markup");
36873 tabs.activate("script");
36874
36875 // more advanced tabs, built from javascript
36876 var jtabs = new Roo.TabPanel("jtabs");
36877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36878
36879 // set up the UpdateManager
36880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36881 var updater = tab2.getUpdateManager();
36882 updater.setDefaultUrl("ajax1.htm");
36883 tab2.on('activate', updater.refresh, updater, true);
36884
36885 // Use setUrl for Ajax loading
36886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36887 tab3.setUrl("ajax2.htm", null, true);
36888
36889 // Disabled tab
36890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36891 tab4.disable();
36892
36893 jtabs.activate("jtabs-1");
36894  * </code></pre>
36895  * @constructor
36896  * Create a new TabPanel.
36897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36899  */
36900 Roo.bootstrap.panel.Tabs = function(config){
36901     /**
36902     * The container element for this TabPanel.
36903     * @type Roo.Element
36904     */
36905     this.el = Roo.get(config.el);
36906     delete config.el;
36907     if(config){
36908         if(typeof config == "boolean"){
36909             this.tabPosition = config ? "bottom" : "top";
36910         }else{
36911             Roo.apply(this, config);
36912         }
36913     }
36914     
36915     if(this.tabPosition == "bottom"){
36916         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36917         this.el.addClass("roo-tabs-bottom");
36918     }
36919     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36920     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36921     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36922     if(Roo.isIE){
36923         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36924     }
36925     if(this.tabPosition != "bottom"){
36926         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36927          * @type Roo.Element
36928          */
36929         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36930         this.el.addClass("roo-tabs-top");
36931     }
36932     this.items = [];
36933
36934     this.bodyEl.setStyle("position", "relative");
36935
36936     this.active = null;
36937     this.activateDelegate = this.activate.createDelegate(this);
36938
36939     this.addEvents({
36940         /**
36941          * @event tabchange
36942          * Fires when the active tab changes
36943          * @param {Roo.TabPanel} this
36944          * @param {Roo.TabPanelItem} activePanel The new active tab
36945          */
36946         "tabchange": true,
36947         /**
36948          * @event beforetabchange
36949          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36950          * @param {Roo.TabPanel} this
36951          * @param {Object} e Set cancel to true on this object to cancel the tab change
36952          * @param {Roo.TabPanelItem} tab The tab being changed to
36953          */
36954         "beforetabchange" : true
36955     });
36956
36957     Roo.EventManager.onWindowResize(this.onResize, this);
36958     this.cpad = this.el.getPadding("lr");
36959     this.hiddenCount = 0;
36960
36961
36962     // toolbar on the tabbar support...
36963     if (this.toolbar) {
36964         alert("no toolbar support yet");
36965         this.toolbar  = false;
36966         /*
36967         var tcfg = this.toolbar;
36968         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36969         this.toolbar = new Roo.Toolbar(tcfg);
36970         if (Roo.isSafari) {
36971             var tbl = tcfg.container.child('table', true);
36972             tbl.setAttribute('width', '100%');
36973         }
36974         */
36975         
36976     }
36977    
36978
36979
36980     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36981 };
36982
36983 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36984     /*
36985      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36986      */
36987     tabPosition : "top",
36988     /*
36989      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36990      */
36991     currentTabWidth : 0,
36992     /*
36993      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36994      */
36995     minTabWidth : 40,
36996     /*
36997      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36998      */
36999     maxTabWidth : 250,
37000     /*
37001      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37002      */
37003     preferredTabWidth : 175,
37004     /*
37005      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37006      */
37007     resizeTabs : false,
37008     /*
37009      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37010      */
37011     monitorResize : true,
37012     /*
37013      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37014      */
37015     toolbar : false,
37016
37017     /**
37018      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37019      * @param {String} id The id of the div to use <b>or create</b>
37020      * @param {String} text The text for the tab
37021      * @param {String} content (optional) Content to put in the TabPanelItem body
37022      * @param {Boolean} closable (optional) True to create a close icon on the tab
37023      * @return {Roo.TabPanelItem} The created TabPanelItem
37024      */
37025     addTab : function(id, text, content, closable, tpl)
37026     {
37027         var item = new Roo.bootstrap.panel.TabItem({
37028             panel: this,
37029             id : id,
37030             text : text,
37031             closable : closable,
37032             tpl : tpl
37033         });
37034         this.addTabItem(item);
37035         if(content){
37036             item.setContent(content);
37037         }
37038         return item;
37039     },
37040
37041     /**
37042      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37043      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37044      * @return {Roo.TabPanelItem}
37045      */
37046     getTab : function(id){
37047         return this.items[id];
37048     },
37049
37050     /**
37051      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37052      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37053      */
37054     hideTab : function(id){
37055         var t = this.items[id];
37056         if(!t.isHidden()){
37057            t.setHidden(true);
37058            this.hiddenCount++;
37059            this.autoSizeTabs();
37060         }
37061     },
37062
37063     /**
37064      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37065      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37066      */
37067     unhideTab : function(id){
37068         var t = this.items[id];
37069         if(t.isHidden()){
37070            t.setHidden(false);
37071            this.hiddenCount--;
37072            this.autoSizeTabs();
37073         }
37074     },
37075
37076     /**
37077      * Adds an existing {@link Roo.TabPanelItem}.
37078      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37079      */
37080     addTabItem : function(item){
37081         this.items[item.id] = item;
37082         this.items.push(item);
37083       //  if(this.resizeTabs){
37084     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37085   //         this.autoSizeTabs();
37086 //        }else{
37087 //            item.autoSize();
37088        // }
37089     },
37090
37091     /**
37092      * Removes a {@link Roo.TabPanelItem}.
37093      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37094      */
37095     removeTab : function(id){
37096         var items = this.items;
37097         var tab = items[id];
37098         if(!tab) { return; }
37099         var index = items.indexOf(tab);
37100         if(this.active == tab && items.length > 1){
37101             var newTab = this.getNextAvailable(index);
37102             if(newTab) {
37103                 newTab.activate();
37104             }
37105         }
37106         this.stripEl.dom.removeChild(tab.pnode.dom);
37107         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37108             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37109         }
37110         items.splice(index, 1);
37111         delete this.items[tab.id];
37112         tab.fireEvent("close", tab);
37113         tab.purgeListeners();
37114         this.autoSizeTabs();
37115     },
37116
37117     getNextAvailable : function(start){
37118         var items = this.items;
37119         var index = start;
37120         // look for a next tab that will slide over to
37121         // replace the one being removed
37122         while(index < items.length){
37123             var item = items[++index];
37124             if(item && !item.isHidden()){
37125                 return item;
37126             }
37127         }
37128         // if one isn't found select the previous tab (on the left)
37129         index = start;
37130         while(index >= 0){
37131             var item = items[--index];
37132             if(item && !item.isHidden()){
37133                 return item;
37134             }
37135         }
37136         return null;
37137     },
37138
37139     /**
37140      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37141      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37142      */
37143     disableTab : function(id){
37144         var tab = this.items[id];
37145         if(tab && this.active != tab){
37146             tab.disable();
37147         }
37148     },
37149
37150     /**
37151      * Enables a {@link Roo.TabPanelItem} that is disabled.
37152      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37153      */
37154     enableTab : function(id){
37155         var tab = this.items[id];
37156         tab.enable();
37157     },
37158
37159     /**
37160      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37161      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37162      * @return {Roo.TabPanelItem} The TabPanelItem.
37163      */
37164     activate : function(id){
37165         var tab = this.items[id];
37166         if(!tab){
37167             return null;
37168         }
37169         if(tab == this.active || tab.disabled){
37170             return tab;
37171         }
37172         var e = {};
37173         this.fireEvent("beforetabchange", this, e, tab);
37174         if(e.cancel !== true && !tab.disabled){
37175             if(this.active){
37176                 this.active.hide();
37177             }
37178             this.active = this.items[id];
37179             this.active.show();
37180             this.fireEvent("tabchange", this, this.active);
37181         }
37182         return tab;
37183     },
37184
37185     /**
37186      * Gets the active {@link Roo.TabPanelItem}.
37187      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37188      */
37189     getActiveTab : function(){
37190         return this.active;
37191     },
37192
37193     /**
37194      * Updates the tab body element to fit the height of the container element
37195      * for overflow scrolling
37196      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37197      */
37198     syncHeight : function(targetHeight){
37199         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37200         var bm = this.bodyEl.getMargins();
37201         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37202         this.bodyEl.setHeight(newHeight);
37203         return newHeight;
37204     },
37205
37206     onResize : function(){
37207         if(this.monitorResize){
37208             this.autoSizeTabs();
37209         }
37210     },
37211
37212     /**
37213      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37214      */
37215     beginUpdate : function(){
37216         this.updating = true;
37217     },
37218
37219     /**
37220      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37221      */
37222     endUpdate : function(){
37223         this.updating = false;
37224         this.autoSizeTabs();
37225     },
37226
37227     /**
37228      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37229      */
37230     autoSizeTabs : function(){
37231         var count = this.items.length;
37232         var vcount = count - this.hiddenCount;
37233         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37234             return;
37235         }
37236         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37237         var availWidth = Math.floor(w / vcount);
37238         var b = this.stripBody;
37239         if(b.getWidth() > w){
37240             var tabs = this.items;
37241             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37242             if(availWidth < this.minTabWidth){
37243                 /*if(!this.sleft){    // incomplete scrolling code
37244                     this.createScrollButtons();
37245                 }
37246                 this.showScroll();
37247                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37248             }
37249         }else{
37250             if(this.currentTabWidth < this.preferredTabWidth){
37251                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37252             }
37253         }
37254     },
37255
37256     /**
37257      * Returns the number of tabs in this TabPanel.
37258      * @return {Number}
37259      */
37260      getCount : function(){
37261          return this.items.length;
37262      },
37263
37264     /**
37265      * Resizes all the tabs to the passed width
37266      * @param {Number} The new width
37267      */
37268     setTabWidth : function(width){
37269         this.currentTabWidth = width;
37270         for(var i = 0, len = this.items.length; i < len; i++) {
37271                 if(!this.items[i].isHidden()) {
37272                 this.items[i].setWidth(width);
37273             }
37274         }
37275     },
37276
37277     /**
37278      * Destroys this TabPanel
37279      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37280      */
37281     destroy : function(removeEl){
37282         Roo.EventManager.removeResizeListener(this.onResize, this);
37283         for(var i = 0, len = this.items.length; i < len; i++){
37284             this.items[i].purgeListeners();
37285         }
37286         if(removeEl === true){
37287             this.el.update("");
37288             this.el.remove();
37289         }
37290     },
37291     
37292     createStrip : function(container)
37293     {
37294         var strip = document.createElement("nav");
37295         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37296         container.appendChild(strip);
37297         return strip;
37298     },
37299     
37300     createStripList : function(strip)
37301     {
37302         // div wrapper for retard IE
37303         // returns the "tr" element.
37304         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37305         //'<div class="x-tabs-strip-wrap">'+
37306           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37307           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37308         return strip.firstChild; //.firstChild.firstChild.firstChild;
37309     },
37310     createBody : function(container)
37311     {
37312         var body = document.createElement("div");
37313         Roo.id(body, "tab-body");
37314         //Roo.fly(body).addClass("x-tabs-body");
37315         Roo.fly(body).addClass("tab-content");
37316         container.appendChild(body);
37317         return body;
37318     },
37319     createItemBody :function(bodyEl, id){
37320         var body = Roo.getDom(id);
37321         if(!body){
37322             body = document.createElement("div");
37323             body.id = id;
37324         }
37325         //Roo.fly(body).addClass("x-tabs-item-body");
37326         Roo.fly(body).addClass("tab-pane");
37327          bodyEl.insertBefore(body, bodyEl.firstChild);
37328         return body;
37329     },
37330     /** @private */
37331     createStripElements :  function(stripEl, text, closable, tpl)
37332     {
37333         var td = document.createElement("li"); // was td..
37334         
37335         
37336         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37337         
37338         
37339         stripEl.appendChild(td);
37340         /*if(closable){
37341             td.className = "x-tabs-closable";
37342             if(!this.closeTpl){
37343                 this.closeTpl = new Roo.Template(
37344                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37345                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37346                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37347                 );
37348             }
37349             var el = this.closeTpl.overwrite(td, {"text": text});
37350             var close = el.getElementsByTagName("div")[0];
37351             var inner = el.getElementsByTagName("em")[0];
37352             return {"el": el, "close": close, "inner": inner};
37353         } else {
37354         */
37355         // not sure what this is..
37356 //            if(!this.tabTpl){
37357                 //this.tabTpl = new Roo.Template(
37358                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37359                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37360                 //);
37361 //                this.tabTpl = new Roo.Template(
37362 //                   '<a href="#">' +
37363 //                   '<span unselectable="on"' +
37364 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37365 //                            ' >{text}</span></a>'
37366 //                );
37367 //                
37368 //            }
37369
37370
37371             var template = tpl || this.tabTpl || false;
37372             
37373             if(!template){
37374                 
37375                 template = new Roo.Template(
37376                    '<a href="#">' +
37377                    '<span unselectable="on"' +
37378                             (this.disableTooltips ? '' : ' title="{text}"') +
37379                             ' >{text}</span></a>'
37380                 );
37381             }
37382             
37383             switch (typeof(template)) {
37384                 case 'object' :
37385                     break;
37386                 case 'string' :
37387                     template = new Roo.Template(template);
37388                     break;
37389                 default :
37390                     break;
37391             }
37392             
37393             var el = template.overwrite(td, {"text": text});
37394             
37395             var inner = el.getElementsByTagName("span")[0];
37396             
37397             return {"el": el, "inner": inner};
37398             
37399     }
37400         
37401     
37402 });
37403
37404 /**
37405  * @class Roo.TabPanelItem
37406  * @extends Roo.util.Observable
37407  * Represents an individual item (tab plus body) in a TabPanel.
37408  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37409  * @param {String} id The id of this TabPanelItem
37410  * @param {String} text The text for the tab of this TabPanelItem
37411  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37412  */
37413 Roo.bootstrap.panel.TabItem = function(config){
37414     /**
37415      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37416      * @type Roo.TabPanel
37417      */
37418     this.tabPanel = config.panel;
37419     /**
37420      * The id for this TabPanelItem
37421      * @type String
37422      */
37423     this.id = config.id;
37424     /** @private */
37425     this.disabled = false;
37426     /** @private */
37427     this.text = config.text;
37428     /** @private */
37429     this.loaded = false;
37430     this.closable = config.closable;
37431
37432     /**
37433      * The body element for this TabPanelItem.
37434      * @type Roo.Element
37435      */
37436     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37437     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37438     this.bodyEl.setStyle("display", "block");
37439     this.bodyEl.setStyle("zoom", "1");
37440     //this.hideAction();
37441
37442     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37443     /** @private */
37444     this.el = Roo.get(els.el);
37445     this.inner = Roo.get(els.inner, true);
37446     this.textEl = Roo.get(this.el.dom.firstChild, true);
37447     this.pnode = Roo.get(els.el.parentNode, true);
37448     this.el.on("mousedown", this.onTabMouseDown, this);
37449     this.el.on("click", this.onTabClick, this);
37450     /** @private */
37451     if(config.closable){
37452         var c = Roo.get(els.close, true);
37453         c.dom.title = this.closeText;
37454         c.addClassOnOver("close-over");
37455         c.on("click", this.closeClick, this);
37456      }
37457
37458     this.addEvents({
37459          /**
37460          * @event activate
37461          * Fires when this tab becomes the active tab.
37462          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37463          * @param {Roo.TabPanelItem} this
37464          */
37465         "activate": true,
37466         /**
37467          * @event beforeclose
37468          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37469          * @param {Roo.TabPanelItem} this
37470          * @param {Object} e Set cancel to true on this object to cancel the close.
37471          */
37472         "beforeclose": true,
37473         /**
37474          * @event close
37475          * Fires when this tab is closed.
37476          * @param {Roo.TabPanelItem} this
37477          */
37478          "close": true,
37479         /**
37480          * @event deactivate
37481          * Fires when this tab is no longer the active tab.
37482          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37483          * @param {Roo.TabPanelItem} this
37484          */
37485          "deactivate" : true
37486     });
37487     this.hidden = false;
37488
37489     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37490 };
37491
37492 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37493            {
37494     purgeListeners : function(){
37495        Roo.util.Observable.prototype.purgeListeners.call(this);
37496        this.el.removeAllListeners();
37497     },
37498     /**
37499      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37500      */
37501     show : function(){
37502         this.pnode.addClass("active");
37503         this.showAction();
37504         if(Roo.isOpera){
37505             this.tabPanel.stripWrap.repaint();
37506         }
37507         this.fireEvent("activate", this.tabPanel, this);
37508     },
37509
37510     /**
37511      * Returns true if this tab is the active tab.
37512      * @return {Boolean}
37513      */
37514     isActive : function(){
37515         return this.tabPanel.getActiveTab() == this;
37516     },
37517
37518     /**
37519      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37520      */
37521     hide : function(){
37522         this.pnode.removeClass("active");
37523         this.hideAction();
37524         this.fireEvent("deactivate", this.tabPanel, this);
37525     },
37526
37527     hideAction : function(){
37528         this.bodyEl.hide();
37529         this.bodyEl.setStyle("position", "absolute");
37530         this.bodyEl.setLeft("-20000px");
37531         this.bodyEl.setTop("-20000px");
37532     },
37533
37534     showAction : function(){
37535         this.bodyEl.setStyle("position", "relative");
37536         this.bodyEl.setTop("");
37537         this.bodyEl.setLeft("");
37538         this.bodyEl.show();
37539     },
37540
37541     /**
37542      * Set the tooltip for the tab.
37543      * @param {String} tooltip The tab's tooltip
37544      */
37545     setTooltip : function(text){
37546         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37547             this.textEl.dom.qtip = text;
37548             this.textEl.dom.removeAttribute('title');
37549         }else{
37550             this.textEl.dom.title = text;
37551         }
37552     },
37553
37554     onTabClick : function(e){
37555         e.preventDefault();
37556         this.tabPanel.activate(this.id);
37557     },
37558
37559     onTabMouseDown : function(e){
37560         e.preventDefault();
37561         this.tabPanel.activate(this.id);
37562     },
37563 /*
37564     getWidth : function(){
37565         return this.inner.getWidth();
37566     },
37567
37568     setWidth : function(width){
37569         var iwidth = width - this.pnode.getPadding("lr");
37570         this.inner.setWidth(iwidth);
37571         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37572         this.pnode.setWidth(width);
37573     },
37574 */
37575     /**
37576      * Show or hide the tab
37577      * @param {Boolean} hidden True to hide or false to show.
37578      */
37579     setHidden : function(hidden){
37580         this.hidden = hidden;
37581         this.pnode.setStyle("display", hidden ? "none" : "");
37582     },
37583
37584     /**
37585      * Returns true if this tab is "hidden"
37586      * @return {Boolean}
37587      */
37588     isHidden : function(){
37589         return this.hidden;
37590     },
37591
37592     /**
37593      * Returns the text for this tab
37594      * @return {String}
37595      */
37596     getText : function(){
37597         return this.text;
37598     },
37599     /*
37600     autoSize : function(){
37601         //this.el.beginMeasure();
37602         this.textEl.setWidth(1);
37603         /*
37604          *  #2804 [new] Tabs in Roojs
37605          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37606          */
37607         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37608         //this.el.endMeasure();
37609     //},
37610
37611     /**
37612      * Sets the text for the tab (Note: this also sets the tooltip text)
37613      * @param {String} text The tab's text and tooltip
37614      */
37615     setText : function(text){
37616         this.text = text;
37617         this.textEl.update(text);
37618         this.setTooltip(text);
37619         //if(!this.tabPanel.resizeTabs){
37620         //    this.autoSize();
37621         //}
37622     },
37623     /**
37624      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37625      */
37626     activate : function(){
37627         this.tabPanel.activate(this.id);
37628     },
37629
37630     /**
37631      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37632      */
37633     disable : function(){
37634         if(this.tabPanel.active != this){
37635             this.disabled = true;
37636             this.pnode.addClass("disabled");
37637         }
37638     },
37639
37640     /**
37641      * Enables this TabPanelItem if it was previously disabled.
37642      */
37643     enable : function(){
37644         this.disabled = false;
37645         this.pnode.removeClass("disabled");
37646     },
37647
37648     /**
37649      * Sets the content for this TabPanelItem.
37650      * @param {String} content The content
37651      * @param {Boolean} loadScripts true to look for and load scripts
37652      */
37653     setContent : function(content, loadScripts){
37654         this.bodyEl.update(content, loadScripts);
37655     },
37656
37657     /**
37658      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37659      * @return {Roo.UpdateManager} The UpdateManager
37660      */
37661     getUpdateManager : function(){
37662         return this.bodyEl.getUpdateManager();
37663     },
37664
37665     /**
37666      * Set a URL to be used to load the content for this TabPanelItem.
37667      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37668      * @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)
37669      * @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)
37670      * @return {Roo.UpdateManager} The UpdateManager
37671      */
37672     setUrl : function(url, params, loadOnce){
37673         if(this.refreshDelegate){
37674             this.un('activate', this.refreshDelegate);
37675         }
37676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37677         this.on("activate", this.refreshDelegate);
37678         return this.bodyEl.getUpdateManager();
37679     },
37680
37681     /** @private */
37682     _handleRefresh : function(url, params, loadOnce){
37683         if(!loadOnce || !this.loaded){
37684             var updater = this.bodyEl.getUpdateManager();
37685             updater.update(url, params, this._setLoaded.createDelegate(this));
37686         }
37687     },
37688
37689     /**
37690      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37691      *   Will fail silently if the setUrl method has not been called.
37692      *   This does not activate the panel, just updates its content.
37693      */
37694     refresh : function(){
37695         if(this.refreshDelegate){
37696            this.loaded = false;
37697            this.refreshDelegate();
37698         }
37699     },
37700
37701     /** @private */
37702     _setLoaded : function(){
37703         this.loaded = true;
37704     },
37705
37706     /** @private */
37707     closeClick : function(e){
37708         var o = {};
37709         e.stopEvent();
37710         this.fireEvent("beforeclose", this, o);
37711         if(o.cancel !== true){
37712             this.tabPanel.removeTab(this.id);
37713         }
37714     },
37715     /**
37716      * The text displayed in the tooltip for the close icon.
37717      * @type String
37718      */
37719     closeText : "Close this tab"
37720 });