roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @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)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * @constructor
4573  * Create a new Navbar Button
4574  * @param {Object} config The config object
4575  */
4576 Roo.bootstrap.NavSidebarItem = function(config){
4577     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4578     this.addEvents({
4579         // raw events
4580         /**
4581          * @event click
4582          * The raw click event for the entire grid.
4583          * @param {Roo.EventObject} e
4584          */
4585         "click" : true,
4586          /**
4587             * @event changed
4588             * Fires when the active item active state changes
4589             * @param {Roo.bootstrap.NavSidebarItem} this
4590             * @param {boolean} state the new state
4591              
4592          */
4593         'changed': true
4594     });
4595    
4596 };
4597
4598 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4599     
4600     badgeWeight : 'default',
4601     
4602     open: false,
4603     
4604     buttonView : false,
4605     
4606     buttonWeight : 'default',
4607     
4608     buttonSize : 'md',
4609     
4610     getAutoCreate : function(){
4611         
4612         
4613         var a = {
4614                 tag: 'a',
4615                 href : this.href || '#',
4616                 cls: '',
4617                 html : '',
4618                 cn : []
4619         };
4620         
4621         if(this.buttonView){
4622             a = {
4623                 tag: 'button',
4624                 href : this.href || '#',
4625                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize,
4626                 html : this.html,
4627                 cn : []
4628             };
4629         }
4630         
4631         var cfg = {
4632             tag: 'li',
4633             cls: '',
4634             cn: [ a ]
4635         };
4636         
4637         if (this.active) {
4638             cfg.cls += ' active';
4639         }
4640         
4641         if (this.disabled) {
4642             cfg.cls += ' disabled';
4643         }
4644         if (this.open) {
4645             cfg.cls += ' open x-open';
4646         }
4647         // left icon..
4648         if (this.glyphicon || this.icon) {
4649             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4650             a.cn.push({ tag : 'i', cls : c }) ;
4651         }
4652         
4653         if(!this.buttonView){
4654             var span = {
4655                 tag: 'span',
4656                 html : this.html || ''
4657             };
4658
4659             a.cn.push(span);
4660             
4661             if (this.badge !== '') {
4662                 a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4663             }
4664         }
4665         
4666         // then badge..
4667         
4668         // fi
4669         if (this.menu) {
4670             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4671             a.cls += 'dropdown-toggle treeview' ;
4672         }
4673         
4674         return cfg;
4675          
4676            
4677     },
4678     
4679     initEvents : function()
4680     { 
4681         if (typeof (this.menu) != 'undefined') {
4682             this.menu.parentType = this.xtype;
4683             this.menu.triggerEl = this.el;
4684             this.menu = this.addxtype(Roo.apply({}, this.menu));
4685         }
4686         
4687         this.el.on('click', this.onClick, this);
4688         
4689         if(this.badge !== ''){
4690  
4691             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4692         }
4693         
4694     },
4695     
4696     onClick : function(e)
4697     {
4698         if(this.disabled){
4699             e.preventDefault();
4700             return;
4701         }
4702         
4703         if(this.preventDefault){
4704             e.preventDefault();
4705         }
4706         
4707         this.fireEvent('click', this);
4708     },
4709     
4710     disable : function()
4711     {
4712         this.setDisabled(true);
4713     },
4714     
4715     enable : function()
4716     {
4717         this.setDisabled(false);
4718     },
4719     
4720     setDisabled : function(state)
4721     {
4722         if(this.disabled == state){
4723             return;
4724         }
4725         
4726         this.disabled = state;
4727         
4728         if (state) {
4729             this.el.addClass('disabled');
4730             return;
4731         }
4732         
4733         this.el.removeClass('disabled');
4734         
4735         return;
4736     },
4737     
4738     setActive : function(state)
4739     {
4740         if(this.active == state){
4741             return;
4742         }
4743         
4744         this.active = state;
4745         
4746         if (state) {
4747             this.el.addClass('active');
4748             return;
4749         }
4750         
4751         this.el.removeClass('active');
4752         
4753         return;
4754     },
4755     
4756     isActive: function () 
4757     {
4758         return this.active;
4759     },
4760     
4761     setBadge : function(str)
4762     {
4763         if(!this.badgeEl){
4764             return;
4765         }
4766         
4767         this.badgeEl.dom.innerHTML = str;
4768     }
4769     
4770    
4771      
4772  
4773 });
4774  
4775
4776  /*
4777  * - LGPL
4778  *
4779  * row
4780  * 
4781  */
4782
4783 /**
4784  * @class Roo.bootstrap.Row
4785  * @extends Roo.bootstrap.Component
4786  * Bootstrap Row class (contains columns...)
4787  * 
4788  * @constructor
4789  * Create a new Row
4790  * @param {Object} config The config object
4791  */
4792
4793 Roo.bootstrap.Row = function(config){
4794     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4795 };
4796
4797 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4798     
4799     getAutoCreate : function(){
4800        return {
4801             cls: 'row clearfix'
4802        };
4803     }
4804     
4805     
4806 });
4807
4808  
4809
4810  /*
4811  * - LGPL
4812  *
4813  * element
4814  * 
4815  */
4816
4817 /**
4818  * @class Roo.bootstrap.Element
4819  * @extends Roo.bootstrap.Component
4820  * Bootstrap Element class
4821  * @cfg {String} html contents of the element
4822  * @cfg {String} tag tag of the element
4823  * @cfg {String} cls class of the element
4824  * @cfg {Boolean} preventDefault (true|false) default false
4825  * @cfg {Boolean} clickable (true|false) default false
4826  * 
4827  * @constructor
4828  * Create a new Element
4829  * @param {Object} config The config object
4830  */
4831
4832 Roo.bootstrap.Element = function(config){
4833     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4834     
4835     this.addEvents({
4836         // raw events
4837         /**
4838          * @event click
4839          * When a element is chick
4840          * @param {Roo.bootstrap.Element} this
4841          * @param {Roo.EventObject} e
4842          */
4843         "click" : true
4844     });
4845 };
4846
4847 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4848     
4849     tag: 'div',
4850     cls: '',
4851     html: '',
4852     preventDefault: false, 
4853     clickable: false,
4854     
4855     getAutoCreate : function(){
4856         
4857         var cfg = {
4858             tag: this.tag,
4859             cls: this.cls,
4860             html: this.html
4861         };
4862         
4863         return cfg;
4864     },
4865     
4866     initEvents: function() 
4867     {
4868         Roo.bootstrap.Element.superclass.initEvents.call(this);
4869         
4870         if(this.clickable){
4871             this.el.on('click', this.onClick, this);
4872         }
4873         
4874     },
4875     
4876     onClick : function(e)
4877     {
4878         if(this.preventDefault){
4879             e.preventDefault();
4880         }
4881         
4882         this.fireEvent('click', this, e);
4883     },
4884     
4885     getValue : function()
4886     {
4887         return this.el.dom.innerHTML;
4888     },
4889     
4890     setValue : function(value)
4891     {
4892         this.el.dom.innerHTML = value;
4893     }
4894    
4895 });
4896
4897  
4898
4899  /*
4900  * - LGPL
4901  *
4902  * pagination
4903  * 
4904  */
4905
4906 /**
4907  * @class Roo.bootstrap.Pagination
4908  * @extends Roo.bootstrap.Component
4909  * Bootstrap Pagination class
4910  * @cfg {String} size xs | sm | md | lg
4911  * @cfg {Boolean} inverse false | true
4912  * 
4913  * @constructor
4914  * Create a new Pagination
4915  * @param {Object} config The config object
4916  */
4917
4918 Roo.bootstrap.Pagination = function(config){
4919     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4920 };
4921
4922 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4923     
4924     cls: false,
4925     size: false,
4926     inverse: false,
4927     
4928     getAutoCreate : function(){
4929         var cfg = {
4930             tag: 'ul',
4931                 cls: 'pagination'
4932         };
4933         if (this.inverse) {
4934             cfg.cls += ' inverse';
4935         }
4936         if (this.html) {
4937             cfg.html=this.html;
4938         }
4939         if (this.cls) {
4940             cfg.cls += " " + this.cls;
4941         }
4942         return cfg;
4943     }
4944    
4945 });
4946
4947  
4948
4949  /*
4950  * - LGPL
4951  *
4952  * Pagination item
4953  * 
4954  */
4955
4956
4957 /**
4958  * @class Roo.bootstrap.PaginationItem
4959  * @extends Roo.bootstrap.Component
4960  * Bootstrap PaginationItem class
4961  * @cfg {String} html text
4962  * @cfg {String} href the link
4963  * @cfg {Boolean} preventDefault (true | false) default true
4964  * @cfg {Boolean} active (true | false) default false
4965  * @cfg {Boolean} disabled default false
4966  * 
4967  * 
4968  * @constructor
4969  * Create a new PaginationItem
4970  * @param {Object} config The config object
4971  */
4972
4973
4974 Roo.bootstrap.PaginationItem = function(config){
4975     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4976     this.addEvents({
4977         // raw events
4978         /**
4979          * @event click
4980          * The raw click event for the entire grid.
4981          * @param {Roo.EventObject} e
4982          */
4983         "click" : true
4984     });
4985 };
4986
4987 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4988     
4989     href : false,
4990     html : false,
4991     preventDefault: true,
4992     active : false,
4993     cls : false,
4994     disabled: false,
4995     
4996     getAutoCreate : function(){
4997         var cfg= {
4998             tag: 'li',
4999             cn: [
5000                 {
5001                     tag : 'a',
5002                     href : this.href ? this.href : '#',
5003                     html : this.html ? this.html : ''
5004                 }
5005             ]
5006         };
5007         
5008         if(this.cls){
5009             cfg.cls = this.cls;
5010         }
5011         
5012         if(this.disabled){
5013             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5014         }
5015         
5016         if(this.active){
5017             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5018         }
5019         
5020         return cfg;
5021     },
5022     
5023     initEvents: function() {
5024         
5025         this.el.on('click', this.onClick, this);
5026         
5027     },
5028     onClick : function(e)
5029     {
5030         Roo.log('PaginationItem on click ');
5031         if(this.preventDefault){
5032             e.preventDefault();
5033         }
5034         
5035         if(this.disabled){
5036             return;
5037         }
5038         
5039         this.fireEvent('click', this, e);
5040     }
5041    
5042 });
5043
5044  
5045
5046  /*
5047  * - LGPL
5048  *
5049  * slider
5050  * 
5051  */
5052
5053
5054 /**
5055  * @class Roo.bootstrap.Slider
5056  * @extends Roo.bootstrap.Component
5057  * Bootstrap Slider class
5058  *    
5059  * @constructor
5060  * Create a new Slider
5061  * @param {Object} config The config object
5062  */
5063
5064 Roo.bootstrap.Slider = function(config){
5065     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5066 };
5067
5068 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5069     
5070     getAutoCreate : function(){
5071         
5072         var cfg = {
5073             tag: 'div',
5074             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5075             cn: [
5076                 {
5077                     tag: 'a',
5078                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5079                 }
5080             ]
5081         };
5082         
5083         return cfg;
5084     }
5085    
5086 });
5087
5088  /*
5089  * Based on:
5090  * Ext JS Library 1.1.1
5091  * Copyright(c) 2006-2007, Ext JS, LLC.
5092  *
5093  * Originally Released Under LGPL - original licence link has changed is not relivant.
5094  *
5095  * Fork - LGPL
5096  * <script type="text/javascript">
5097  */
5098  
5099
5100 /**
5101  * @class Roo.grid.ColumnModel
5102  * @extends Roo.util.Observable
5103  * This is the default implementation of a ColumnModel used by the Grid. It defines
5104  * the columns in the grid.
5105  * <br>Usage:<br>
5106  <pre><code>
5107  var colModel = new Roo.grid.ColumnModel([
5108         {header: "Ticker", width: 60, sortable: true, locked: true},
5109         {header: "Company Name", width: 150, sortable: true},
5110         {header: "Market Cap.", width: 100, sortable: true},
5111         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5112         {header: "Employees", width: 100, sortable: true, resizable: false}
5113  ]);
5114  </code></pre>
5115  * <p>
5116  
5117  * The config options listed for this class are options which may appear in each
5118  * individual column definition.
5119  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5120  * @constructor
5121  * @param {Object} config An Array of column config objects. See this class's
5122  * config objects for details.
5123 */
5124 Roo.grid.ColumnModel = function(config){
5125         /**
5126      * The config passed into the constructor
5127      */
5128     this.config = config;
5129     this.lookup = {};
5130
5131     // if no id, create one
5132     // if the column does not have a dataIndex mapping,
5133     // map it to the order it is in the config
5134     for(var i = 0, len = config.length; i < len; i++){
5135         var c = config[i];
5136         if(typeof c.dataIndex == "undefined"){
5137             c.dataIndex = i;
5138         }
5139         if(typeof c.renderer == "string"){
5140             c.renderer = Roo.util.Format[c.renderer];
5141         }
5142         if(typeof c.id == "undefined"){
5143             c.id = Roo.id();
5144         }
5145         if(c.editor && c.editor.xtype){
5146             c.editor  = Roo.factory(c.editor, Roo.grid);
5147         }
5148         if(c.editor && c.editor.isFormField){
5149             c.editor = new Roo.grid.GridEditor(c.editor);
5150         }
5151         this.lookup[c.id] = c;
5152     }
5153
5154     /**
5155      * The width of columns which have no width specified (defaults to 100)
5156      * @type Number
5157      */
5158     this.defaultWidth = 100;
5159
5160     /**
5161      * Default sortable of columns which have no sortable specified (defaults to false)
5162      * @type Boolean
5163      */
5164     this.defaultSortable = false;
5165
5166     this.addEvents({
5167         /**
5168              * @event widthchange
5169              * Fires when the width of a column changes.
5170              * @param {ColumnModel} this
5171              * @param {Number} columnIndex The column index
5172              * @param {Number} newWidth The new width
5173              */
5174             "widthchange": true,
5175         /**
5176              * @event headerchange
5177              * Fires when the text of a header changes.
5178              * @param {ColumnModel} this
5179              * @param {Number} columnIndex The column index
5180              * @param {Number} newText The new header text
5181              */
5182             "headerchange": true,
5183         /**
5184              * @event hiddenchange
5185              * Fires when a column is hidden or "unhidden".
5186              * @param {ColumnModel} this
5187              * @param {Number} columnIndex The column index
5188              * @param {Boolean} hidden true if hidden, false otherwise
5189              */
5190             "hiddenchange": true,
5191             /**
5192          * @event columnmoved
5193          * Fires when a column is moved.
5194          * @param {ColumnModel} this
5195          * @param {Number} oldIndex
5196          * @param {Number} newIndex
5197          */
5198         "columnmoved" : true,
5199         /**
5200          * @event columlockchange
5201          * Fires when a column's locked state is changed
5202          * @param {ColumnModel} this
5203          * @param {Number} colIndex
5204          * @param {Boolean} locked true if locked
5205          */
5206         "columnlockchange" : true
5207     });
5208     Roo.grid.ColumnModel.superclass.constructor.call(this);
5209 };
5210 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5211     /**
5212      * @cfg {String} header The header text to display in the Grid view.
5213      */
5214     /**
5215      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5216      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5217      * specified, the column's index is used as an index into the Record's data Array.
5218      */
5219     /**
5220      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5221      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5222      */
5223     /**
5224      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5225      * Defaults to the value of the {@link #defaultSortable} property.
5226      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5227      */
5228     /**
5229      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5230      */
5231     /**
5232      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5233      */
5234     /**
5235      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5236      */
5237     /**
5238      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5239      */
5240     /**
5241      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5242      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5243      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5244      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5245      */
5246        /**
5247      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5248      */
5249     /**
5250      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5251      */
5252     /**
5253      * @cfg {String} cursor (Optional)
5254      */
5255     /**
5256      * @cfg {String} tooltip (Optional)
5257      */
5258     /**
5259      * @cfg {Number} xs (Optional)
5260      */
5261     /**
5262      * @cfg {Number} sm (Optional)
5263      */
5264     /**
5265      * @cfg {Number} md (Optional)
5266      */
5267     /**
5268      * @cfg {Number} lg (Optional)
5269      */
5270     /**
5271      * Returns the id of the column at the specified index.
5272      * @param {Number} index The column index
5273      * @return {String} the id
5274      */
5275     getColumnId : function(index){
5276         return this.config[index].id;
5277     },
5278
5279     /**
5280      * Returns the column for a specified id.
5281      * @param {String} id The column id
5282      * @return {Object} the column
5283      */
5284     getColumnById : function(id){
5285         return this.lookup[id];
5286     },
5287
5288     
5289     /**
5290      * Returns the column for a specified dataIndex.
5291      * @param {String} dataIndex The column dataIndex
5292      * @return {Object|Boolean} the column or false if not found
5293      */
5294     getColumnByDataIndex: function(dataIndex){
5295         var index = this.findColumnIndex(dataIndex);
5296         return index > -1 ? this.config[index] : false;
5297     },
5298     
5299     /**
5300      * Returns the index for a specified column id.
5301      * @param {String} id The column id
5302      * @return {Number} the index, or -1 if not found
5303      */
5304     getIndexById : function(id){
5305         for(var i = 0, len = this.config.length; i < len; i++){
5306             if(this.config[i].id == id){
5307                 return i;
5308             }
5309         }
5310         return -1;
5311     },
5312     
5313     /**
5314      * Returns the index for a specified column dataIndex.
5315      * @param {String} dataIndex The column dataIndex
5316      * @return {Number} the index, or -1 if not found
5317      */
5318     
5319     findColumnIndex : function(dataIndex){
5320         for(var i = 0, len = this.config.length; i < len; i++){
5321             if(this.config[i].dataIndex == dataIndex){
5322                 return i;
5323             }
5324         }
5325         return -1;
5326     },
5327     
5328     
5329     moveColumn : function(oldIndex, newIndex){
5330         var c = this.config[oldIndex];
5331         this.config.splice(oldIndex, 1);
5332         this.config.splice(newIndex, 0, c);
5333         this.dataMap = null;
5334         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5335     },
5336
5337     isLocked : function(colIndex){
5338         return this.config[colIndex].locked === true;
5339     },
5340
5341     setLocked : function(colIndex, value, suppressEvent){
5342         if(this.isLocked(colIndex) == value){
5343             return;
5344         }
5345         this.config[colIndex].locked = value;
5346         if(!suppressEvent){
5347             this.fireEvent("columnlockchange", this, colIndex, value);
5348         }
5349     },
5350
5351     getTotalLockedWidth : function(){
5352         var totalWidth = 0;
5353         for(var i = 0; i < this.config.length; i++){
5354             if(this.isLocked(i) && !this.isHidden(i)){
5355                 this.totalWidth += this.getColumnWidth(i);
5356             }
5357         }
5358         return totalWidth;
5359     },
5360
5361     getLockedCount : function(){
5362         for(var i = 0, len = this.config.length; i < len; i++){
5363             if(!this.isLocked(i)){
5364                 return i;
5365             }
5366         }
5367         
5368         return this.config.length;
5369     },
5370
5371     /**
5372      * Returns the number of columns.
5373      * @return {Number}
5374      */
5375     getColumnCount : function(visibleOnly){
5376         if(visibleOnly === true){
5377             var c = 0;
5378             for(var i = 0, len = this.config.length; i < len; i++){
5379                 if(!this.isHidden(i)){
5380                     c++;
5381                 }
5382             }
5383             return c;
5384         }
5385         return this.config.length;
5386     },
5387
5388     /**
5389      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5390      * @param {Function} fn
5391      * @param {Object} scope (optional)
5392      * @return {Array} result
5393      */
5394     getColumnsBy : function(fn, scope){
5395         var r = [];
5396         for(var i = 0, len = this.config.length; i < len; i++){
5397             var c = this.config[i];
5398             if(fn.call(scope||this, c, i) === true){
5399                 r[r.length] = c;
5400             }
5401         }
5402         return r;
5403     },
5404
5405     /**
5406      * Returns true if the specified column is sortable.
5407      * @param {Number} col The column index
5408      * @return {Boolean}
5409      */
5410     isSortable : function(col){
5411         if(typeof this.config[col].sortable == "undefined"){
5412             return this.defaultSortable;
5413         }
5414         return this.config[col].sortable;
5415     },
5416
5417     /**
5418      * Returns the rendering (formatting) function defined for the column.
5419      * @param {Number} col The column index.
5420      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5421      */
5422     getRenderer : function(col){
5423         if(!this.config[col].renderer){
5424             return Roo.grid.ColumnModel.defaultRenderer;
5425         }
5426         return this.config[col].renderer;
5427     },
5428
5429     /**
5430      * Sets the rendering (formatting) function for a column.
5431      * @param {Number} col The column index
5432      * @param {Function} fn The function to use to process the cell's raw data
5433      * to return HTML markup for the grid view. The render function is called with
5434      * the following parameters:<ul>
5435      * <li>Data value.</li>
5436      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5437      * <li>css A CSS style string to apply to the table cell.</li>
5438      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5439      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5440      * <li>Row index</li>
5441      * <li>Column index</li>
5442      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5443      */
5444     setRenderer : function(col, fn){
5445         this.config[col].renderer = fn;
5446     },
5447
5448     /**
5449      * Returns the width for the specified column.
5450      * @param {Number} col The column index
5451      * @return {Number}
5452      */
5453     getColumnWidth : function(col){
5454         return this.config[col].width * 1 || this.defaultWidth;
5455     },
5456
5457     /**
5458      * Sets the width for a column.
5459      * @param {Number} col The column index
5460      * @param {Number} width The new width
5461      */
5462     setColumnWidth : function(col, width, suppressEvent){
5463         this.config[col].width = width;
5464         this.totalWidth = null;
5465         if(!suppressEvent){
5466              this.fireEvent("widthchange", this, col, width);
5467         }
5468     },
5469
5470     /**
5471      * Returns the total width of all columns.
5472      * @param {Boolean} includeHidden True to include hidden column widths
5473      * @return {Number}
5474      */
5475     getTotalWidth : function(includeHidden){
5476         if(!this.totalWidth){
5477             this.totalWidth = 0;
5478             for(var i = 0, len = this.config.length; i < len; i++){
5479                 if(includeHidden || !this.isHidden(i)){
5480                     this.totalWidth += this.getColumnWidth(i);
5481                 }
5482             }
5483         }
5484         return this.totalWidth;
5485     },
5486
5487     /**
5488      * Returns the header for the specified column.
5489      * @param {Number} col The column index
5490      * @return {String}
5491      */
5492     getColumnHeader : function(col){
5493         return this.config[col].header;
5494     },
5495
5496     /**
5497      * Sets the header for a column.
5498      * @param {Number} col The column index
5499      * @param {String} header The new header
5500      */
5501     setColumnHeader : function(col, header){
5502         this.config[col].header = header;
5503         this.fireEvent("headerchange", this, col, header);
5504     },
5505
5506     /**
5507      * Returns the tooltip for the specified column.
5508      * @param {Number} col The column index
5509      * @return {String}
5510      */
5511     getColumnTooltip : function(col){
5512             return this.config[col].tooltip;
5513     },
5514     /**
5515      * Sets the tooltip for a column.
5516      * @param {Number} col The column index
5517      * @param {String} tooltip The new tooltip
5518      */
5519     setColumnTooltip : function(col, tooltip){
5520             this.config[col].tooltip = tooltip;
5521     },
5522
5523     /**
5524      * Returns the dataIndex for the specified column.
5525      * @param {Number} col The column index
5526      * @return {Number}
5527      */
5528     getDataIndex : function(col){
5529         return this.config[col].dataIndex;
5530     },
5531
5532     /**
5533      * Sets the dataIndex for a column.
5534      * @param {Number} col The column index
5535      * @param {Number} dataIndex The new dataIndex
5536      */
5537     setDataIndex : function(col, dataIndex){
5538         this.config[col].dataIndex = dataIndex;
5539     },
5540
5541     
5542     
5543     /**
5544      * Returns true if the cell is editable.
5545      * @param {Number} colIndex The column index
5546      * @param {Number} rowIndex The row index - this is nto actually used..?
5547      * @return {Boolean}
5548      */
5549     isCellEditable : function(colIndex, rowIndex){
5550         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5551     },
5552
5553     /**
5554      * Returns the editor defined for the cell/column.
5555      * return false or null to disable editing.
5556      * @param {Number} colIndex The column index
5557      * @param {Number} rowIndex The row index
5558      * @return {Object}
5559      */
5560     getCellEditor : function(colIndex, rowIndex){
5561         return this.config[colIndex].editor;
5562     },
5563
5564     /**
5565      * Sets if a column is editable.
5566      * @param {Number} col The column index
5567      * @param {Boolean} editable True if the column is editable
5568      */
5569     setEditable : function(col, editable){
5570         this.config[col].editable = editable;
5571     },
5572
5573
5574     /**
5575      * Returns true if the column is hidden.
5576      * @param {Number} colIndex The column index
5577      * @return {Boolean}
5578      */
5579     isHidden : function(colIndex){
5580         return this.config[colIndex].hidden;
5581     },
5582
5583
5584     /**
5585      * Returns true if the column width cannot be changed
5586      */
5587     isFixed : function(colIndex){
5588         return this.config[colIndex].fixed;
5589     },
5590
5591     /**
5592      * Returns true if the column can be resized
5593      * @return {Boolean}
5594      */
5595     isResizable : function(colIndex){
5596         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5597     },
5598     /**
5599      * Sets if a column is hidden.
5600      * @param {Number} colIndex The column index
5601      * @param {Boolean} hidden True if the column is hidden
5602      */
5603     setHidden : function(colIndex, hidden){
5604         this.config[colIndex].hidden = hidden;
5605         this.totalWidth = null;
5606         this.fireEvent("hiddenchange", this, colIndex, hidden);
5607     },
5608
5609     /**
5610      * Sets the editor for a column.
5611      * @param {Number} col The column index
5612      * @param {Object} editor The editor object
5613      */
5614     setEditor : function(col, editor){
5615         this.config[col].editor = editor;
5616     }
5617 });
5618
5619 Roo.grid.ColumnModel.defaultRenderer = function(value)
5620 {
5621     if(typeof value == "object") {
5622         return value;
5623     }
5624         if(typeof value == "string" && value.length < 1){
5625             return "&#160;";
5626         }
5627     
5628         return String.format("{0}", value);
5629 };
5630
5631 // Alias for backwards compatibility
5632 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5633 /*
5634  * Based on:
5635  * Ext JS Library 1.1.1
5636  * Copyright(c) 2006-2007, Ext JS, LLC.
5637  *
5638  * Originally Released Under LGPL - original licence link has changed is not relivant.
5639  *
5640  * Fork - LGPL
5641  * <script type="text/javascript">
5642  */
5643  
5644 /**
5645  * @class Roo.LoadMask
5646  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5647  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5648  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5649  * element's UpdateManager load indicator and will be destroyed after the initial load.
5650  * @constructor
5651  * Create a new LoadMask
5652  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5653  * @param {Object} config The config object
5654  */
5655 Roo.LoadMask = function(el, config){
5656     this.el = Roo.get(el);
5657     Roo.apply(this, config);
5658     if(this.store){
5659         this.store.on('beforeload', this.onBeforeLoad, this);
5660         this.store.on('load', this.onLoad, this);
5661         this.store.on('loadexception', this.onLoadException, this);
5662         this.removeMask = false;
5663     }else{
5664         var um = this.el.getUpdateManager();
5665         um.showLoadIndicator = false; // disable the default indicator
5666         um.on('beforeupdate', this.onBeforeLoad, this);
5667         um.on('update', this.onLoad, this);
5668         um.on('failure', this.onLoad, this);
5669         this.removeMask = true;
5670     }
5671 };
5672
5673 Roo.LoadMask.prototype = {
5674     /**
5675      * @cfg {Boolean} removeMask
5676      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5677      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5678      */
5679     /**
5680      * @cfg {String} msg
5681      * The text to display in a centered loading message box (defaults to 'Loading...')
5682      */
5683     msg : 'Loading...',
5684     /**
5685      * @cfg {String} msgCls
5686      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5687      */
5688     msgCls : 'x-mask-loading',
5689
5690     /**
5691      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5692      * @type Boolean
5693      */
5694     disabled: false,
5695
5696     /**
5697      * Disables the mask to prevent it from being displayed
5698      */
5699     disable : function(){
5700        this.disabled = true;
5701     },
5702
5703     /**
5704      * Enables the mask so that it can be displayed
5705      */
5706     enable : function(){
5707         this.disabled = false;
5708     },
5709     
5710     onLoadException : function()
5711     {
5712         Roo.log(arguments);
5713         
5714         if (typeof(arguments[3]) != 'undefined') {
5715             Roo.MessageBox.alert("Error loading",arguments[3]);
5716         } 
5717         /*
5718         try {
5719             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5720                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5721             }   
5722         } catch(e) {
5723             
5724         }
5725         */
5726     
5727         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5728     },
5729     // private
5730     onLoad : function()
5731     {
5732         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5733     },
5734
5735     // private
5736     onBeforeLoad : function(){
5737         if(!this.disabled){
5738             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5739         }
5740     },
5741
5742     // private
5743     destroy : function(){
5744         if(this.store){
5745             this.store.un('beforeload', this.onBeforeLoad, this);
5746             this.store.un('load', this.onLoad, this);
5747             this.store.un('loadexception', this.onLoadException, this);
5748         }else{
5749             var um = this.el.getUpdateManager();
5750             um.un('beforeupdate', this.onBeforeLoad, this);
5751             um.un('update', this.onLoad, this);
5752             um.un('failure', this.onLoad, this);
5753         }
5754     }
5755 };/*
5756  * - LGPL
5757  *
5758  * table
5759  * 
5760  */
5761
5762 /**
5763  * @class Roo.bootstrap.Table
5764  * @extends Roo.bootstrap.Component
5765  * Bootstrap Table class
5766  * @cfg {String} cls table class
5767  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5768  * @cfg {String} bgcolor Specifies the background color for a table
5769  * @cfg {Number} border Specifies whether the table cells should have borders or not
5770  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5771  * @cfg {Number} cellspacing Specifies the space between cells
5772  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5773  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5774  * @cfg {String} sortable Specifies that the table should be sortable
5775  * @cfg {String} summary Specifies a summary of the content of a table
5776  * @cfg {Number} width Specifies the width of a table
5777  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5778  * 
5779  * @cfg {boolean} striped Should the rows be alternative striped
5780  * @cfg {boolean} bordered Add borders to the table
5781  * @cfg {boolean} hover Add hover highlighting
5782  * @cfg {boolean} condensed Format condensed
5783  * @cfg {boolean} responsive Format condensed
5784  * @cfg {Boolean} loadMask (true|false) default false
5785  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5786  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5787  * @cfg {Boolean} rowSelection (true|false) default false
5788  * @cfg {Boolean} cellSelection (true|false) default false
5789  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5790  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5791  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5792  
5793  * 
5794  * @constructor
5795  * Create a new Table
5796  * @param {Object} config The config object
5797  */
5798
5799 Roo.bootstrap.Table = function(config){
5800     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5801     
5802   
5803     
5804     // BC...
5805     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5806     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5807     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5808     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5809     
5810     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5811     if (this.sm) {
5812         this.sm.grid = this;
5813         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5814         this.sm = this.selModel;
5815         this.sm.xmodule = this.xmodule || false;
5816     }
5817     
5818     if (this.cm && typeof(this.cm.config) == 'undefined') {
5819         this.colModel = new Roo.grid.ColumnModel(this.cm);
5820         this.cm = this.colModel;
5821         this.cm.xmodule = this.xmodule || false;
5822     }
5823     if (this.store) {
5824         this.store= Roo.factory(this.store, Roo.data);
5825         this.ds = this.store;
5826         this.ds.xmodule = this.xmodule || false;
5827          
5828     }
5829     if (this.footer && this.store) {
5830         this.footer.dataSource = this.ds;
5831         this.footer = Roo.factory(this.footer);
5832     }
5833     
5834     /** @private */
5835     this.addEvents({
5836         /**
5837          * @event cellclick
5838          * Fires when a cell is clicked
5839          * @param {Roo.bootstrap.Table} this
5840          * @param {Roo.Element} el
5841          * @param {Number} rowIndex
5842          * @param {Number} columnIndex
5843          * @param {Roo.EventObject} e
5844          */
5845         "cellclick" : true,
5846         /**
5847          * @event celldblclick
5848          * Fires when a cell is double clicked
5849          * @param {Roo.bootstrap.Table} this
5850          * @param {Roo.Element} el
5851          * @param {Number} rowIndex
5852          * @param {Number} columnIndex
5853          * @param {Roo.EventObject} e
5854          */
5855         "celldblclick" : true,
5856         /**
5857          * @event rowclick
5858          * Fires when a row is clicked
5859          * @param {Roo.bootstrap.Table} this
5860          * @param {Roo.Element} el
5861          * @param {Number} rowIndex
5862          * @param {Roo.EventObject} e
5863          */
5864         "rowclick" : true,
5865         /**
5866          * @event rowdblclick
5867          * Fires when a row is double clicked
5868          * @param {Roo.bootstrap.Table} this
5869          * @param {Roo.Element} el
5870          * @param {Number} rowIndex
5871          * @param {Roo.EventObject} e
5872          */
5873         "rowdblclick" : true,
5874         /**
5875          * @event mouseover
5876          * Fires when a mouseover occur
5877          * @param {Roo.bootstrap.Table} this
5878          * @param {Roo.Element} el
5879          * @param {Number} rowIndex
5880          * @param {Number} columnIndex
5881          * @param {Roo.EventObject} e
5882          */
5883         "mouseover" : true,
5884         /**
5885          * @event mouseout
5886          * Fires when a mouseout occur
5887          * @param {Roo.bootstrap.Table} this
5888          * @param {Roo.Element} el
5889          * @param {Number} rowIndex
5890          * @param {Number} columnIndex
5891          * @param {Roo.EventObject} e
5892          */
5893         "mouseout" : true,
5894         /**
5895          * @event rowclass
5896          * Fires when a row is rendered, so you can change add a style to it.
5897          * @param {Roo.bootstrap.Table} this
5898          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5899          */
5900         'rowclass' : true,
5901           /**
5902          * @event rowsrendered
5903          * Fires when all the  rows have been rendered
5904          * @param {Roo.bootstrap.Table} this
5905          */
5906         'rowsrendered' : true,
5907         /**
5908          * @event contextmenu
5909          * The raw contextmenu event for the entire grid.
5910          * @param {Roo.EventObject} e
5911          */
5912         "contextmenu" : true,
5913         /**
5914          * @event rowcontextmenu
5915          * Fires when a row is right clicked
5916          * @param {Roo.bootstrap.Table} this
5917          * @param {Number} rowIndex
5918          * @param {Roo.EventObject} e
5919          */
5920         "rowcontextmenu" : true,
5921         /**
5922          * @event cellcontextmenu
5923          * Fires when a cell is right clicked
5924          * @param {Roo.bootstrap.Table} this
5925          * @param {Number} rowIndex
5926          * @param {Number} cellIndex
5927          * @param {Roo.EventObject} e
5928          */
5929          "cellcontextmenu" : true,
5930          /**
5931          * @event headercontextmenu
5932          * Fires when a header is right clicked
5933          * @param {Roo.bootstrap.Table} this
5934          * @param {Number} columnIndex
5935          * @param {Roo.EventObject} e
5936          */
5937         "headercontextmenu" : true
5938     });
5939 };
5940
5941 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5942     
5943     cls: false,
5944     align: false,
5945     bgcolor: false,
5946     border: false,
5947     cellpadding: false,
5948     cellspacing: false,
5949     frame: false,
5950     rules: false,
5951     sortable: false,
5952     summary: false,
5953     width: false,
5954     striped : false,
5955     scrollBody : false,
5956     bordered: false,
5957     hover:  false,
5958     condensed : false,
5959     responsive : false,
5960     sm : false,
5961     cm : false,
5962     store : false,
5963     loadMask : false,
5964     footerShow : true,
5965     headerShow : true,
5966   
5967     rowSelection : false,
5968     cellSelection : false,
5969     layout : false,
5970     
5971     // Roo.Element - the tbody
5972     mainBody: false,
5973     // Roo.Element - thead element
5974     mainHead: false,
5975     
5976     container: false, // used by gridpanel...
5977     
5978     lazyLoad : false,
5979     
5980     getAutoCreate : function()
5981     {
5982         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5983         
5984         cfg = {
5985             tag: 'table',
5986             cls : 'table',
5987             cn : []
5988         };
5989         if (this.scrollBody) {
5990             cfg.cls += ' table-body-fixed';
5991         }    
5992         if (this.striped) {
5993             cfg.cls += ' table-striped';
5994         }
5995         
5996         if (this.hover) {
5997             cfg.cls += ' table-hover';
5998         }
5999         if (this.bordered) {
6000             cfg.cls += ' table-bordered';
6001         }
6002         if (this.condensed) {
6003             cfg.cls += ' table-condensed';
6004         }
6005         if (this.responsive) {
6006             cfg.cls += ' table-responsive';
6007         }
6008         
6009         if (this.cls) {
6010             cfg.cls+=  ' ' +this.cls;
6011         }
6012         
6013         // this lot should be simplifed...
6014         
6015         if (this.align) {
6016             cfg.align=this.align;
6017         }
6018         if (this.bgcolor) {
6019             cfg.bgcolor=this.bgcolor;
6020         }
6021         if (this.border) {
6022             cfg.border=this.border;
6023         }
6024         if (this.cellpadding) {
6025             cfg.cellpadding=this.cellpadding;
6026         }
6027         if (this.cellspacing) {
6028             cfg.cellspacing=this.cellspacing;
6029         }
6030         if (this.frame) {
6031             cfg.frame=this.frame;
6032         }
6033         if (this.rules) {
6034             cfg.rules=this.rules;
6035         }
6036         if (this.sortable) {
6037             cfg.sortable=this.sortable;
6038         }
6039         if (this.summary) {
6040             cfg.summary=this.summary;
6041         }
6042         if (this.width) {
6043             cfg.width=this.width;
6044         }
6045         if (this.layout) {
6046             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6047         }
6048         
6049         if(this.store || this.cm){
6050             if(this.headerShow){
6051                 cfg.cn.push(this.renderHeader());
6052             }
6053             
6054             cfg.cn.push(this.renderBody());
6055             
6056             if(this.footerShow){
6057                 cfg.cn.push(this.renderFooter());
6058             }
6059             // where does this come from?
6060             //cfg.cls+=  ' TableGrid';
6061         }
6062         
6063         return { cn : [ cfg ] };
6064     },
6065     
6066     initEvents : function()
6067     {   
6068         if(!this.store || !this.cm){
6069             return;
6070         }
6071         if (this.selModel) {
6072             this.selModel.initEvents();
6073         }
6074         
6075         
6076         //Roo.log('initEvents with ds!!!!');
6077         
6078         this.mainBody = this.el.select('tbody', true).first();
6079         this.mainHead = this.el.select('thead', true).first();
6080         
6081         
6082         
6083         
6084         var _this = this;
6085         
6086         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6087             e.on('click', _this.sort, _this);
6088         });
6089         
6090         this.mainBody.on("click", this.onClick, this);
6091         this.mainBody.on("dblclick", this.onDblClick, this);
6092         
6093         // why is this done????? = it breaks dialogs??
6094         //this.parent().el.setStyle('position', 'relative');
6095         
6096         
6097         if (this.footer) {
6098             this.footer.parentId = this.id;
6099             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6100             
6101             if(this.lazyLoad){
6102                 this.el.select('tfoot tr td').first().addClass('hide');
6103             }
6104         } 
6105         
6106         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6107         
6108         this.store.on('load', this.onLoad, this);
6109         this.store.on('beforeload', this.onBeforeLoad, this);
6110         this.store.on('update', this.onUpdate, this);
6111         this.store.on('add', this.onAdd, this);
6112         this.store.on("clear", this.clear, this);
6113         
6114         this.el.on("contextmenu", this.onContextMenu, this);
6115         
6116         this.mainBody.on('scroll', this.onBodyScroll, this);
6117         
6118         
6119     },
6120     
6121     onContextMenu : function(e, t)
6122     {
6123         this.processEvent("contextmenu", e);
6124     },
6125     
6126     processEvent : function(name, e)
6127     {
6128         if (name != 'touchstart' ) {
6129             this.fireEvent(name, e);    
6130         }
6131         
6132         var t = e.getTarget();
6133         
6134         var cell = Roo.get(t);
6135         
6136         if(!cell){
6137             return;
6138         }
6139         
6140         if(cell.findParent('tfoot', false, true)){
6141             return;
6142         }
6143         
6144         if(cell.findParent('thead', false, true)){
6145             
6146             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6147                 cell = Roo.get(t).findParent('th', false, true);
6148                 if (!cell) {
6149                     Roo.log("failed to find th in thead?");
6150                     Roo.log(e.getTarget());
6151                     return;
6152                 }
6153             }
6154             
6155             var cellIndex = cell.dom.cellIndex;
6156             
6157             var ename = name == 'touchstart' ? 'click' : name;
6158             this.fireEvent("header" + ename, this, cellIndex, e);
6159             
6160             return;
6161         }
6162         
6163         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6164             cell = Roo.get(t).findParent('td', false, true);
6165             if (!cell) {
6166                 Roo.log("failed to find th in tbody?");
6167                 Roo.log(e.getTarget());
6168                 return;
6169             }
6170         }
6171         
6172         var row = cell.findParent('tr', false, true);
6173         var cellIndex = cell.dom.cellIndex;
6174         var rowIndex = row.dom.rowIndex - 1;
6175         
6176         if(row !== false){
6177             
6178             this.fireEvent("row" + name, this, rowIndex, e);
6179             
6180             if(cell !== false){
6181             
6182                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6183             }
6184         }
6185         
6186     },
6187     
6188     onMouseover : function(e, el)
6189     {
6190         var cell = Roo.get(el);
6191         
6192         if(!cell){
6193             return;
6194         }
6195         
6196         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6197             cell = cell.findParent('td', false, true);
6198         }
6199         
6200         var row = cell.findParent('tr', false, true);
6201         var cellIndex = cell.dom.cellIndex;
6202         var rowIndex = row.dom.rowIndex - 1; // start from 0
6203         
6204         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6205         
6206     },
6207     
6208     onMouseout : function(e, el)
6209     {
6210         var cell = Roo.get(el);
6211         
6212         if(!cell){
6213             return;
6214         }
6215         
6216         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6217             cell = cell.findParent('td', false, true);
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         var cellIndex = cell.dom.cellIndex;
6222         var rowIndex = row.dom.rowIndex - 1; // start from 0
6223         
6224         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6225         
6226     },
6227     
6228     onClick : function(e, el)
6229     {
6230         var cell = Roo.get(el);
6231         
6232         if(!cell || (!this.cellSelection && !this.rowSelection)){
6233             return;
6234         }
6235         
6236         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6237             cell = cell.findParent('td', false, true);
6238         }
6239         
6240         if(!cell || typeof(cell) == 'undefined'){
6241             return;
6242         }
6243         
6244         var row = cell.findParent('tr', false, true);
6245         
6246         if(!row || typeof(row) == 'undefined'){
6247             return;
6248         }
6249         
6250         var cellIndex = cell.dom.cellIndex;
6251         var rowIndex = this.getRowIndex(row);
6252         
6253         // why??? - should these not be based on SelectionModel?
6254         if(this.cellSelection){
6255             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6256         }
6257         
6258         if(this.rowSelection){
6259             this.fireEvent('rowclick', this, row, rowIndex, e);
6260         }
6261         
6262         
6263     },
6264         
6265     onDblClick : function(e,el)
6266     {
6267         var cell = Roo.get(el);
6268         
6269         if(!cell || (!this.cellSelection && !this.rowSelection)){
6270             return;
6271         }
6272         
6273         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6274             cell = cell.findParent('td', false, true);
6275         }
6276         
6277         if(!cell || typeof(cell) == 'undefined'){
6278             return;
6279         }
6280         
6281         var row = cell.findParent('tr', false, true);
6282         
6283         if(!row || typeof(row) == 'undefined'){
6284             return;
6285         }
6286         
6287         var cellIndex = cell.dom.cellIndex;
6288         var rowIndex = this.getRowIndex(row);
6289         
6290         if(this.cellSelection){
6291             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6292         }
6293         
6294         if(this.rowSelection){
6295             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6296         }
6297     },
6298     
6299     sort : function(e,el)
6300     {
6301         var col = Roo.get(el);
6302         
6303         if(!col.hasClass('sortable')){
6304             return;
6305         }
6306         
6307         var sort = col.attr('sort');
6308         var dir = 'ASC';
6309         
6310         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6311             dir = 'DESC';
6312         }
6313         
6314         this.store.sortInfo = {field : sort, direction : dir};
6315         
6316         if (this.footer) {
6317             Roo.log("calling footer first");
6318             this.footer.onClick('first');
6319         } else {
6320         
6321             this.store.load({ params : { start : 0 } });
6322         }
6323     },
6324     
6325     renderHeader : function()
6326     {
6327         var header = {
6328             tag: 'thead',
6329             cn : []
6330         };
6331         
6332         var cm = this.cm;
6333         this.totalWidth = 0;
6334         
6335         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6336             
6337             var config = cm.config[i];
6338             
6339             var c = {
6340                 tag: 'th',
6341                 style : '',
6342                 html: cm.getColumnHeader(i)
6343             };
6344             
6345             var hh = '';
6346             
6347             if(typeof(config.sortable) != 'undefined' && config.sortable){
6348                 c.cls = 'sortable';
6349                 c.html = '<i class="glyphicon"></i>' + c.html;
6350             }
6351             
6352             if(typeof(config.lgHeader) != 'undefined'){
6353                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6354             }
6355             
6356             if(typeof(config.mdHeader) != 'undefined'){
6357                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6358             }
6359             
6360             if(typeof(config.smHeader) != 'undefined'){
6361                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6362             }
6363             
6364             if(typeof(config.xsHeader) != 'undefined'){
6365                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6366             }
6367             
6368             if(hh.length){
6369                 c.html = hh;
6370             }
6371             
6372             if(typeof(config.tooltip) != 'undefined'){
6373                 c.tooltip = config.tooltip;
6374             }
6375             
6376             if(typeof(config.colspan) != 'undefined'){
6377                 c.colspan = config.colspan;
6378             }
6379             
6380             if(typeof(config.hidden) != 'undefined' && config.hidden){
6381                 c.style += ' display:none;';
6382             }
6383             
6384             if(typeof(config.dataIndex) != 'undefined'){
6385                 c.sort = config.dataIndex;
6386             }
6387             
6388            
6389             
6390             if(typeof(config.align) != 'undefined' && config.align.length){
6391                 c.style += ' text-align:' + config.align + ';';
6392             }
6393             
6394             if(typeof(config.width) != 'undefined'){
6395                 c.style += ' width:' + config.width + 'px;';
6396                 this.totalWidth += config.width;
6397             } else {
6398                 this.totalWidth += 100; // assume minimum of 100 per column?
6399             }
6400             
6401             if(typeof(config.cls) != 'undefined'){
6402                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6403             }
6404             
6405             ['xs','sm','md','lg'].map(function(size){
6406                 
6407                 if(typeof(config[size]) == 'undefined'){
6408                     return;
6409                 }
6410                 
6411                 if (!config[size]) { // 0 = hidden
6412                     c.cls += ' hidden-' + size;
6413                     return;
6414                 }
6415                 
6416                 c.cls += ' col-' + size + '-' + config[size];
6417
6418             });
6419             
6420             header.cn.push(c)
6421         }
6422         
6423         return header;
6424     },
6425     
6426     renderBody : function()
6427     {
6428         var body = {
6429             tag: 'tbody',
6430             cn : [
6431                 {
6432                     tag: 'tr',
6433                     cn : [
6434                         {
6435                             tag : 'td',
6436                             colspan :  this.cm.getColumnCount()
6437                         }
6438                     ]
6439                 }
6440             ]
6441         };
6442         
6443         return body;
6444     },
6445     
6446     renderFooter : function()
6447     {
6448         var footer = {
6449             tag: 'tfoot',
6450             cn : [
6451                 {
6452                     tag: 'tr',
6453                     cn : [
6454                         {
6455                             tag : 'td',
6456                             colspan :  this.cm.getColumnCount()
6457                         }
6458                     ]
6459                 }
6460             ]
6461         };
6462         
6463         return footer;
6464     },
6465     
6466     
6467     
6468     onLoad : function()
6469     {
6470 //        Roo.log('ds onload');
6471         this.clear();
6472         
6473         var _this = this;
6474         var cm = this.cm;
6475         var ds = this.store;
6476         
6477         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6478             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6479             if (_this.store.sortInfo) {
6480                     
6481                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6482                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6483                 }
6484                 
6485                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6486                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6487                 }
6488             }
6489         });
6490         
6491         var tbody =  this.mainBody;
6492               
6493         if(ds.getCount() > 0){
6494             ds.data.each(function(d,rowIndex){
6495                 var row =  this.renderRow(cm, ds, rowIndex);
6496                 
6497                 tbody.createChild(row);
6498                 
6499                 var _this = this;
6500                 
6501                 if(row.cellObjects.length){
6502                     Roo.each(row.cellObjects, function(r){
6503                         _this.renderCellObject(r);
6504                     })
6505                 }
6506                 
6507             }, this);
6508         }
6509         
6510         Roo.each(this.el.select('tbody td', true).elements, function(e){
6511             e.on('mouseover', _this.onMouseover, _this);
6512         });
6513         
6514         Roo.each(this.el.select('tbody td', true).elements, function(e){
6515             e.on('mouseout', _this.onMouseout, _this);
6516         });
6517         this.fireEvent('rowsrendered', this);
6518         //if(this.loadMask){
6519         //    this.maskEl.hide();
6520         //}
6521         
6522         this.autoSize();
6523     },
6524     
6525     
6526     onUpdate : function(ds,record)
6527     {
6528         this.refreshRow(record);
6529         this.autoSize();
6530     },
6531     
6532     onRemove : function(ds, record, index, isUpdate){
6533         if(isUpdate !== true){
6534             this.fireEvent("beforerowremoved", this, index, record);
6535         }
6536         var bt = this.mainBody.dom;
6537         
6538         var rows = this.el.select('tbody > tr', true).elements;
6539         
6540         if(typeof(rows[index]) != 'undefined'){
6541             bt.removeChild(rows[index].dom);
6542         }
6543         
6544 //        if(bt.rows[index]){
6545 //            bt.removeChild(bt.rows[index]);
6546 //        }
6547         
6548         if(isUpdate !== true){
6549             //this.stripeRows(index);
6550             //this.syncRowHeights(index, index);
6551             //this.layout();
6552             this.fireEvent("rowremoved", this, index, record);
6553         }
6554     },
6555     
6556     onAdd : function(ds, records, rowIndex)
6557     {
6558         //Roo.log('on Add called');
6559         // - note this does not handle multiple adding very well..
6560         var bt = this.mainBody.dom;
6561         for (var i =0 ; i < records.length;i++) {
6562             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6563             //Roo.log(records[i]);
6564             //Roo.log(this.store.getAt(rowIndex+i));
6565             this.insertRow(this.store, rowIndex + i, false);
6566             return;
6567         }
6568         
6569     },
6570     
6571     
6572     refreshRow : function(record){
6573         var ds = this.store, index;
6574         if(typeof record == 'number'){
6575             index = record;
6576             record = ds.getAt(index);
6577         }else{
6578             index = ds.indexOf(record);
6579         }
6580         this.insertRow(ds, index, true);
6581         this.autoSize();
6582         this.onRemove(ds, record, index+1, true);
6583         this.autoSize();
6584         //this.syncRowHeights(index, index);
6585         //this.layout();
6586         this.fireEvent("rowupdated", this, index, record);
6587     },
6588     
6589     insertRow : function(dm, rowIndex, isUpdate){
6590         
6591         if(!isUpdate){
6592             this.fireEvent("beforerowsinserted", this, rowIndex);
6593         }
6594             //var s = this.getScrollState();
6595         var row = this.renderRow(this.cm, this.store, rowIndex);
6596         // insert before rowIndex..
6597         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6598         
6599         var _this = this;
6600                 
6601         if(row.cellObjects.length){
6602             Roo.each(row.cellObjects, function(r){
6603                 _this.renderCellObject(r);
6604             })
6605         }
6606             
6607         if(!isUpdate){
6608             this.fireEvent("rowsinserted", this, rowIndex);
6609             //this.syncRowHeights(firstRow, lastRow);
6610             //this.stripeRows(firstRow);
6611             //this.layout();
6612         }
6613         
6614     },
6615     
6616     
6617     getRowDom : function(rowIndex)
6618     {
6619         var rows = this.el.select('tbody > tr', true).elements;
6620         
6621         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6622         
6623     },
6624     // returns the object tree for a tr..
6625   
6626     
6627     renderRow : function(cm, ds, rowIndex) 
6628     {
6629         
6630         var d = ds.getAt(rowIndex);
6631         
6632         var row = {
6633             tag : 'tr',
6634             cn : []
6635         };
6636             
6637         var cellObjects = [];
6638         
6639         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6640             var config = cm.config[i];
6641             
6642             var renderer = cm.getRenderer(i);
6643             var value = '';
6644             var id = false;
6645             
6646             if(typeof(renderer) !== 'undefined'){
6647                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6648             }
6649             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6650             // and are rendered into the cells after the row is rendered - using the id for the element.
6651             
6652             if(typeof(value) === 'object'){
6653                 id = Roo.id();
6654                 cellObjects.push({
6655                     container : id,
6656                     cfg : value 
6657                 })
6658             }
6659             
6660             var rowcfg = {
6661                 record: d,
6662                 rowIndex : rowIndex,
6663                 colIndex : i,
6664                 rowClass : ''
6665             };
6666
6667             this.fireEvent('rowclass', this, rowcfg);
6668             
6669             var td = {
6670                 tag: 'td',
6671                 cls : rowcfg.rowClass,
6672                 style: '',
6673                 html: (typeof(value) === 'object') ? '' : value
6674             };
6675             
6676             if (id) {
6677                 td.id = id;
6678             }
6679             
6680             if(typeof(config.colspan) != 'undefined'){
6681                 td.colspan = config.colspan;
6682             }
6683             
6684             if(typeof(config.hidden) != 'undefined' && config.hidden){
6685                 td.style += ' display:none;';
6686             }
6687             
6688             if(typeof(config.align) != 'undefined' && config.align.length){
6689                 td.style += ' text-align:' + config.align + ';';
6690             }
6691             
6692             if(typeof(config.width) != 'undefined'){
6693                 td.style += ' width:' +  config.width + 'px;';
6694             }
6695             
6696             if(typeof(config.cursor) != 'undefined'){
6697                 td.style += ' cursor:' +  config.cursor + ';';
6698             }
6699             
6700             if(typeof(config.cls) != 'undefined'){
6701                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6702             }
6703             
6704             ['xs','sm','md','lg'].map(function(size){
6705                 
6706                 if(typeof(config[size]) == 'undefined'){
6707                     return;
6708                 }
6709                 
6710                 if (!config[size]) { // 0 = hidden
6711                     td.cls += ' hidden-' + size;
6712                     return;
6713                 }
6714                 
6715                 td.cls += ' col-' + size + '-' + config[size];
6716
6717             });
6718              
6719             row.cn.push(td);
6720            
6721         }
6722         
6723         row.cellObjects = cellObjects;
6724         
6725         return row;
6726           
6727     },
6728     
6729     
6730     
6731     onBeforeLoad : function()
6732     {
6733         //Roo.log('ds onBeforeLoad');
6734         
6735         //this.clear();
6736         
6737         //if(this.loadMask){
6738         //    this.maskEl.show();
6739         //}
6740     },
6741      /**
6742      * Remove all rows
6743      */
6744     clear : function()
6745     {
6746         this.el.select('tbody', true).first().dom.innerHTML = '';
6747     },
6748     /**
6749      * Show or hide a row.
6750      * @param {Number} rowIndex to show or hide
6751      * @param {Boolean} state hide
6752      */
6753     setRowVisibility : function(rowIndex, state)
6754     {
6755         var bt = this.mainBody.dom;
6756         
6757         var rows = this.el.select('tbody > tr', true).elements;
6758         
6759         if(typeof(rows[rowIndex]) == 'undefined'){
6760             return;
6761         }
6762         rows[rowIndex].dom.style.display = state ? '' : 'none';
6763     },
6764     
6765     
6766     getSelectionModel : function(){
6767         if(!this.selModel){
6768             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6769         }
6770         return this.selModel;
6771     },
6772     /*
6773      * Render the Roo.bootstrap object from renderder
6774      */
6775     renderCellObject : function(r)
6776     {
6777         var _this = this;
6778         
6779         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6780         
6781         var t = r.cfg.render(r.container);
6782         
6783         if(r.cfg.cn){
6784             Roo.each(r.cfg.cn, function(c){
6785                 var child = {
6786                     container: t.getChildContainer(),
6787                     cfg: c
6788                 };
6789                 _this.renderCellObject(child);
6790             })
6791         }
6792     },
6793     
6794     getRowIndex : function(row)
6795     {
6796         var rowIndex = -1;
6797         
6798         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6799             if(el != row){
6800                 return;
6801             }
6802             
6803             rowIndex = index;
6804         });
6805         
6806         return rowIndex;
6807     },
6808      /**
6809      * Returns the grid's underlying element = used by panel.Grid
6810      * @return {Element} The element
6811      */
6812     getGridEl : function(){
6813         return this.el;
6814     },
6815      /**
6816      * Forces a resize - used by panel.Grid
6817      * @return {Element} The element
6818      */
6819     autoSize : function()
6820     {
6821         //var ctr = Roo.get(this.container.dom.parentElement);
6822         var ctr = Roo.get(this.el.dom);
6823         
6824         var thd = this.getGridEl().select('thead',true).first();
6825         var tbd = this.getGridEl().select('tbody', true).first();
6826         var tfd = this.getGridEl().select('tfoot', true).first();
6827         
6828         var cw = ctr.getWidth();
6829         
6830         if (tbd) {
6831             
6832             tbd.setSize(ctr.getWidth(),
6833                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6834             );
6835             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6836             cw -= barsize;
6837         }
6838         cw = Math.max(cw, this.totalWidth);
6839         this.getGridEl().select('tr',true).setWidth(cw);
6840         // resize 'expandable coloumn?
6841         
6842         return; // we doe not have a view in this design..
6843         
6844     },
6845     onBodyScroll: function()
6846     {
6847         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6848         this.mainHead.setStyle({
6849             'position' : 'relative',
6850             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6851         });
6852         
6853         if(this.lazyLoad){
6854             
6855             var scrollHeight = this.mainBody.dom.scrollHeight;
6856             
6857             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6858             
6859             var height = this.mainBody.getHeight();
6860             
6861             if(scrollHeight - height == scrollTop) {
6862                 
6863                 var total = this.ds.getTotalCount();
6864                 
6865                 if(this.footer.cursor + this.footer.pageSize < total){
6866                     
6867                     this.footer.ds.load({
6868                         params : {
6869                             start : this.footer.cursor + this.footer.pageSize,
6870                             limit : this.footer.pageSize
6871                         },
6872                         add : true
6873                     });
6874                 }
6875             }
6876             
6877         }
6878     }
6879 });
6880
6881  
6882
6883  /*
6884  * - LGPL
6885  *
6886  * table cell
6887  * 
6888  */
6889
6890 /**
6891  * @class Roo.bootstrap.TableCell
6892  * @extends Roo.bootstrap.Component
6893  * Bootstrap TableCell class
6894  * @cfg {String} html cell contain text
6895  * @cfg {String} cls cell class
6896  * @cfg {String} tag cell tag (td|th) default td
6897  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6898  * @cfg {String} align Aligns the content in a cell
6899  * @cfg {String} axis Categorizes cells
6900  * @cfg {String} bgcolor Specifies the background color of a cell
6901  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6902  * @cfg {Number} colspan Specifies the number of columns a cell should span
6903  * @cfg {String} headers Specifies one or more header cells a cell is related to
6904  * @cfg {Number} height Sets the height of a cell
6905  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6906  * @cfg {Number} rowspan Sets the number of rows a cell should span
6907  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6908  * @cfg {String} valign Vertical aligns the content in a cell
6909  * @cfg {Number} width Specifies the width of a cell
6910  * 
6911  * @constructor
6912  * Create a new TableCell
6913  * @param {Object} config The config object
6914  */
6915
6916 Roo.bootstrap.TableCell = function(config){
6917     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6918 };
6919
6920 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6921     
6922     html: false,
6923     cls: false,
6924     tag: false,
6925     abbr: false,
6926     align: false,
6927     axis: false,
6928     bgcolor: false,
6929     charoff: false,
6930     colspan: false,
6931     headers: false,
6932     height: false,
6933     nowrap: false,
6934     rowspan: false,
6935     scope: false,
6936     valign: false,
6937     width: false,
6938     
6939     
6940     getAutoCreate : function(){
6941         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6942         
6943         cfg = {
6944             tag: 'td'
6945         };
6946         
6947         if(this.tag){
6948             cfg.tag = this.tag;
6949         }
6950         
6951         if (this.html) {
6952             cfg.html=this.html
6953         }
6954         if (this.cls) {
6955             cfg.cls=this.cls
6956         }
6957         if (this.abbr) {
6958             cfg.abbr=this.abbr
6959         }
6960         if (this.align) {
6961             cfg.align=this.align
6962         }
6963         if (this.axis) {
6964             cfg.axis=this.axis
6965         }
6966         if (this.bgcolor) {
6967             cfg.bgcolor=this.bgcolor
6968         }
6969         if (this.charoff) {
6970             cfg.charoff=this.charoff
6971         }
6972         if (this.colspan) {
6973             cfg.colspan=this.colspan
6974         }
6975         if (this.headers) {
6976             cfg.headers=this.headers
6977         }
6978         if (this.height) {
6979             cfg.height=this.height
6980         }
6981         if (this.nowrap) {
6982             cfg.nowrap=this.nowrap
6983         }
6984         if (this.rowspan) {
6985             cfg.rowspan=this.rowspan
6986         }
6987         if (this.scope) {
6988             cfg.scope=this.scope
6989         }
6990         if (this.valign) {
6991             cfg.valign=this.valign
6992         }
6993         if (this.width) {
6994             cfg.width=this.width
6995         }
6996         
6997         
6998         return cfg;
6999     }
7000    
7001 });
7002
7003  
7004
7005  /*
7006  * - LGPL
7007  *
7008  * table row
7009  * 
7010  */
7011
7012 /**
7013  * @class Roo.bootstrap.TableRow
7014  * @extends Roo.bootstrap.Component
7015  * Bootstrap TableRow class
7016  * @cfg {String} cls row class
7017  * @cfg {String} align Aligns the content in a table row
7018  * @cfg {String} bgcolor Specifies a background color for a table row
7019  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7020  * @cfg {String} valign Vertical aligns the content in a table row
7021  * 
7022  * @constructor
7023  * Create a new TableRow
7024  * @param {Object} config The config object
7025  */
7026
7027 Roo.bootstrap.TableRow = function(config){
7028     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7029 };
7030
7031 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7032     
7033     cls: false,
7034     align: false,
7035     bgcolor: false,
7036     charoff: false,
7037     valign: false,
7038     
7039     getAutoCreate : function(){
7040         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7041         
7042         cfg = {
7043             tag: 'tr'
7044         };
7045             
7046         if(this.cls){
7047             cfg.cls = this.cls;
7048         }
7049         if(this.align){
7050             cfg.align = this.align;
7051         }
7052         if(this.bgcolor){
7053             cfg.bgcolor = this.bgcolor;
7054         }
7055         if(this.charoff){
7056             cfg.charoff = this.charoff;
7057         }
7058         if(this.valign){
7059             cfg.valign = this.valign;
7060         }
7061         
7062         return cfg;
7063     }
7064    
7065 });
7066
7067  
7068
7069  /*
7070  * - LGPL
7071  *
7072  * table body
7073  * 
7074  */
7075
7076 /**
7077  * @class Roo.bootstrap.TableBody
7078  * @extends Roo.bootstrap.Component
7079  * Bootstrap TableBody class
7080  * @cfg {String} cls element class
7081  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7082  * @cfg {String} align Aligns the content inside the element
7083  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7084  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7085  * 
7086  * @constructor
7087  * Create a new TableBody
7088  * @param {Object} config The config object
7089  */
7090
7091 Roo.bootstrap.TableBody = function(config){
7092     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7093 };
7094
7095 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7096     
7097     cls: false,
7098     tag: false,
7099     align: false,
7100     charoff: false,
7101     valign: false,
7102     
7103     getAutoCreate : function(){
7104         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7105         
7106         cfg = {
7107             tag: 'tbody'
7108         };
7109             
7110         if (this.cls) {
7111             cfg.cls=this.cls
7112         }
7113         if(this.tag){
7114             cfg.tag = this.tag;
7115         }
7116         
7117         if(this.align){
7118             cfg.align = this.align;
7119         }
7120         if(this.charoff){
7121             cfg.charoff = this.charoff;
7122         }
7123         if(this.valign){
7124             cfg.valign = this.valign;
7125         }
7126         
7127         return cfg;
7128     }
7129     
7130     
7131 //    initEvents : function()
7132 //    {
7133 //        
7134 //        if(!this.store){
7135 //            return;
7136 //        }
7137 //        
7138 //        this.store = Roo.factory(this.store, Roo.data);
7139 //        this.store.on('load', this.onLoad, this);
7140 //        
7141 //        this.store.load();
7142 //        
7143 //    },
7144 //    
7145 //    onLoad: function () 
7146 //    {   
7147 //        this.fireEvent('load', this);
7148 //    }
7149 //    
7150 //   
7151 });
7152
7153  
7154
7155  /*
7156  * Based on:
7157  * Ext JS Library 1.1.1
7158  * Copyright(c) 2006-2007, Ext JS, LLC.
7159  *
7160  * Originally Released Under LGPL - original licence link has changed is not relivant.
7161  *
7162  * Fork - LGPL
7163  * <script type="text/javascript">
7164  */
7165
7166 // as we use this in bootstrap.
7167 Roo.namespace('Roo.form');
7168  /**
7169  * @class Roo.form.Action
7170  * Internal Class used to handle form actions
7171  * @constructor
7172  * @param {Roo.form.BasicForm} el The form element or its id
7173  * @param {Object} config Configuration options
7174  */
7175
7176  
7177  
7178 // define the action interface
7179 Roo.form.Action = function(form, options){
7180     this.form = form;
7181     this.options = options || {};
7182 };
7183 /**
7184  * Client Validation Failed
7185  * @const 
7186  */
7187 Roo.form.Action.CLIENT_INVALID = 'client';
7188 /**
7189  * Server Validation Failed
7190  * @const 
7191  */
7192 Roo.form.Action.SERVER_INVALID = 'server';
7193  /**
7194  * Connect to Server Failed
7195  * @const 
7196  */
7197 Roo.form.Action.CONNECT_FAILURE = 'connect';
7198 /**
7199  * Reading Data from Server Failed
7200  * @const 
7201  */
7202 Roo.form.Action.LOAD_FAILURE = 'load';
7203
7204 Roo.form.Action.prototype = {
7205     type : 'default',
7206     failureType : undefined,
7207     response : undefined,
7208     result : undefined,
7209
7210     // interface method
7211     run : function(options){
7212
7213     },
7214
7215     // interface method
7216     success : function(response){
7217
7218     },
7219
7220     // interface method
7221     handleResponse : function(response){
7222
7223     },
7224
7225     // default connection failure
7226     failure : function(response){
7227         
7228         this.response = response;
7229         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7230         this.form.afterAction(this, false);
7231     },
7232
7233     processResponse : function(response){
7234         this.response = response;
7235         if(!response.responseText){
7236             return true;
7237         }
7238         this.result = this.handleResponse(response);
7239         return this.result;
7240     },
7241
7242     // utility functions used internally
7243     getUrl : function(appendParams){
7244         var url = this.options.url || this.form.url || this.form.el.dom.action;
7245         if(appendParams){
7246             var p = this.getParams();
7247             if(p){
7248                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7249             }
7250         }
7251         return url;
7252     },
7253
7254     getMethod : function(){
7255         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7256     },
7257
7258     getParams : function(){
7259         var bp = this.form.baseParams;
7260         var p = this.options.params;
7261         if(p){
7262             if(typeof p == "object"){
7263                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7264             }else if(typeof p == 'string' && bp){
7265                 p += '&' + Roo.urlEncode(bp);
7266             }
7267         }else if(bp){
7268             p = Roo.urlEncode(bp);
7269         }
7270         return p;
7271     },
7272
7273     createCallback : function(){
7274         return {
7275             success: this.success,
7276             failure: this.failure,
7277             scope: this,
7278             timeout: (this.form.timeout*1000),
7279             upload: this.form.fileUpload ? this.success : undefined
7280         };
7281     }
7282 };
7283
7284 Roo.form.Action.Submit = function(form, options){
7285     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7286 };
7287
7288 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7289     type : 'submit',
7290
7291     haveProgress : false,
7292     uploadComplete : false,
7293     
7294     // uploadProgress indicator.
7295     uploadProgress : function()
7296     {
7297         if (!this.form.progressUrl) {
7298             return;
7299         }
7300         
7301         if (!this.haveProgress) {
7302             Roo.MessageBox.progress("Uploading", "Uploading");
7303         }
7304         if (this.uploadComplete) {
7305            Roo.MessageBox.hide();
7306            return;
7307         }
7308         
7309         this.haveProgress = true;
7310    
7311         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7312         
7313         var c = new Roo.data.Connection();
7314         c.request({
7315             url : this.form.progressUrl,
7316             params: {
7317                 id : uid
7318             },
7319             method: 'GET',
7320             success : function(req){
7321                //console.log(data);
7322                 var rdata = false;
7323                 var edata;
7324                 try  {
7325                    rdata = Roo.decode(req.responseText)
7326                 } catch (e) {
7327                     Roo.log("Invalid data from server..");
7328                     Roo.log(edata);
7329                     return;
7330                 }
7331                 if (!rdata || !rdata.success) {
7332                     Roo.log(rdata);
7333                     Roo.MessageBox.alert(Roo.encode(rdata));
7334                     return;
7335                 }
7336                 var data = rdata.data;
7337                 
7338                 if (this.uploadComplete) {
7339                    Roo.MessageBox.hide();
7340                    return;
7341                 }
7342                    
7343                 if (data){
7344                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7345                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7346                     );
7347                 }
7348                 this.uploadProgress.defer(2000,this);
7349             },
7350        
7351             failure: function(data) {
7352                 Roo.log('progress url failed ');
7353                 Roo.log(data);
7354             },
7355             scope : this
7356         });
7357            
7358     },
7359     
7360     
7361     run : function()
7362     {
7363         // run get Values on the form, so it syncs any secondary forms.
7364         this.form.getValues();
7365         
7366         var o = this.options;
7367         var method = this.getMethod();
7368         var isPost = method == 'POST';
7369         if(o.clientValidation === false || this.form.isValid()){
7370             
7371             if (this.form.progressUrl) {
7372                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7373                     (new Date() * 1) + '' + Math.random());
7374                     
7375             } 
7376             
7377             
7378             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7379                 form:this.form.el.dom,
7380                 url:this.getUrl(!isPost),
7381                 method: method,
7382                 params:isPost ? this.getParams() : null,
7383                 isUpload: this.form.fileUpload
7384             }));
7385             
7386             this.uploadProgress();
7387
7388         }else if (o.clientValidation !== false){ // client validation failed
7389             this.failureType = Roo.form.Action.CLIENT_INVALID;
7390             this.form.afterAction(this, false);
7391         }
7392     },
7393
7394     success : function(response)
7395     {
7396         this.uploadComplete= true;
7397         if (this.haveProgress) {
7398             Roo.MessageBox.hide();
7399         }
7400         
7401         
7402         var result = this.processResponse(response);
7403         if(result === true || result.success){
7404             this.form.afterAction(this, true);
7405             return;
7406         }
7407         if(result.errors){
7408             this.form.markInvalid(result.errors);
7409             this.failureType = Roo.form.Action.SERVER_INVALID;
7410         }
7411         this.form.afterAction(this, false);
7412     },
7413     failure : function(response)
7414     {
7415         this.uploadComplete= true;
7416         if (this.haveProgress) {
7417             Roo.MessageBox.hide();
7418         }
7419         
7420         this.response = response;
7421         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7422         this.form.afterAction(this, false);
7423     },
7424     
7425     handleResponse : function(response){
7426         if(this.form.errorReader){
7427             var rs = this.form.errorReader.read(response);
7428             var errors = [];
7429             if(rs.records){
7430                 for(var i = 0, len = rs.records.length; i < len; i++) {
7431                     var r = rs.records[i];
7432                     errors[i] = r.data;
7433                 }
7434             }
7435             if(errors.length < 1){
7436                 errors = null;
7437             }
7438             return {
7439                 success : rs.success,
7440                 errors : errors
7441             };
7442         }
7443         var ret = false;
7444         try {
7445             ret = Roo.decode(response.responseText);
7446         } catch (e) {
7447             ret = {
7448                 success: false,
7449                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7450                 errors : []
7451             };
7452         }
7453         return ret;
7454         
7455     }
7456 });
7457
7458
7459 Roo.form.Action.Load = function(form, options){
7460     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7461     this.reader = this.form.reader;
7462 };
7463
7464 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7465     type : 'load',
7466
7467     run : function(){
7468         
7469         Roo.Ajax.request(Roo.apply(
7470                 this.createCallback(), {
7471                     method:this.getMethod(),
7472                     url:this.getUrl(false),
7473                     params:this.getParams()
7474         }));
7475     },
7476
7477     success : function(response){
7478         
7479         var result = this.processResponse(response);
7480         if(result === true || !result.success || !result.data){
7481             this.failureType = Roo.form.Action.LOAD_FAILURE;
7482             this.form.afterAction(this, false);
7483             return;
7484         }
7485         this.form.clearInvalid();
7486         this.form.setValues(result.data);
7487         this.form.afterAction(this, true);
7488     },
7489
7490     handleResponse : function(response){
7491         if(this.form.reader){
7492             var rs = this.form.reader.read(response);
7493             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7494             return {
7495                 success : rs.success,
7496                 data : data
7497             };
7498         }
7499         return Roo.decode(response.responseText);
7500     }
7501 });
7502
7503 Roo.form.Action.ACTION_TYPES = {
7504     'load' : Roo.form.Action.Load,
7505     'submit' : Roo.form.Action.Submit
7506 };/*
7507  * - LGPL
7508  *
7509  * form
7510  *
7511  */
7512
7513 /**
7514  * @class Roo.bootstrap.Form
7515  * @extends Roo.bootstrap.Component
7516  * Bootstrap Form class
7517  * @cfg {String} method  GET | POST (default POST)
7518  * @cfg {String} labelAlign top | left (default top)
7519  * @cfg {String} align left  | right - for navbars
7520  * @cfg {Boolean} loadMask load mask when submit (default true)
7521
7522  *
7523  * @constructor
7524  * Create a new Form
7525  * @param {Object} config The config object
7526  */
7527
7528
7529 Roo.bootstrap.Form = function(config){
7530     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7531     
7532     Roo.bootstrap.Form.popover.apply();
7533     
7534     this.addEvents({
7535         /**
7536          * @event clientvalidation
7537          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7538          * @param {Form} this
7539          * @param {Boolean} valid true if the form has passed client-side validation
7540          */
7541         clientvalidation: true,
7542         /**
7543          * @event beforeaction
7544          * Fires before any action is performed. Return false to cancel the action.
7545          * @param {Form} this
7546          * @param {Action} action The action to be performed
7547          */
7548         beforeaction: true,
7549         /**
7550          * @event actionfailed
7551          * Fires when an action fails.
7552          * @param {Form} this
7553          * @param {Action} action The action that failed
7554          */
7555         actionfailed : true,
7556         /**
7557          * @event actioncomplete
7558          * Fires when an action is completed.
7559          * @param {Form} this
7560          * @param {Action} action The action that completed
7561          */
7562         actioncomplete : true
7563     });
7564
7565 };
7566
7567 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7568
7569      /**
7570      * @cfg {String} method
7571      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7572      */
7573     method : 'POST',
7574     /**
7575      * @cfg {String} url
7576      * The URL to use for form actions if one isn't supplied in the action options.
7577      */
7578     /**
7579      * @cfg {Boolean} fileUpload
7580      * Set to true if this form is a file upload.
7581      */
7582
7583     /**
7584      * @cfg {Object} baseParams
7585      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7586      */
7587
7588     /**
7589      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7590      */
7591     timeout: 30,
7592     /**
7593      * @cfg {Sting} align (left|right) for navbar forms
7594      */
7595     align : 'left',
7596
7597     // private
7598     activeAction : null,
7599
7600     /**
7601      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7602      * element by passing it or its id or mask the form itself by passing in true.
7603      * @type Mixed
7604      */
7605     waitMsgTarget : false,
7606
7607     loadMask : true,
7608     
7609     /**
7610      * @cfg {Boolean} errorMask (true|false) default false
7611      */
7612     errorMask : false,
7613     
7614     /**
7615      * @cfg {Number} maskOffset Default 100
7616      */
7617     maskOffset : 100,
7618
7619     getAutoCreate : function(){
7620
7621         var cfg = {
7622             tag: 'form',
7623             method : this.method || 'POST',
7624             id : this.id || Roo.id(),
7625             cls : ''
7626         };
7627         if (this.parent().xtype.match(/^Nav/)) {
7628             cfg.cls = 'navbar-form navbar-' + this.align;
7629
7630         }
7631
7632         if (this.labelAlign == 'left' ) {
7633             cfg.cls += ' form-horizontal';
7634         }
7635
7636
7637         return cfg;
7638     },
7639     initEvents : function()
7640     {
7641         this.el.on('submit', this.onSubmit, this);
7642         // this was added as random key presses on the form where triggering form submit.
7643         this.el.on('keypress', function(e) {
7644             if (e.getCharCode() != 13) {
7645                 return true;
7646             }
7647             // we might need to allow it for textareas.. and some other items.
7648             // check e.getTarget().
7649
7650             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7651                 return true;
7652             }
7653
7654             Roo.log("keypress blocked");
7655
7656             e.preventDefault();
7657             return false;
7658         });
7659         
7660     },
7661     // private
7662     onSubmit : function(e){
7663         e.stopEvent();
7664     },
7665
7666      /**
7667      * Returns true if client-side validation on the form is successful.
7668      * @return Boolean
7669      */
7670     isValid : function(){
7671         var items = this.getItems();
7672         var valid = true;
7673         var target = false;
7674         
7675         items.each(function(f){
7676             
7677             if(f.validate()){
7678                 return;
7679             }
7680             valid = false;
7681
7682             if(!target && f.el.isVisible(true)){
7683                 target = f;
7684             }
7685            
7686         });
7687         
7688         if(this.errorMask && !valid){
7689             Roo.bootstrap.Form.popover.mask(this, target);
7690         }
7691         
7692         return valid;
7693     },
7694     
7695     /**
7696      * Returns true if any fields in this form have changed since their original load.
7697      * @return Boolean
7698      */
7699     isDirty : function(){
7700         var dirty = false;
7701         var items = this.getItems();
7702         items.each(function(f){
7703            if(f.isDirty()){
7704                dirty = true;
7705                return false;
7706            }
7707            return true;
7708         });
7709         return dirty;
7710     },
7711      /**
7712      * Performs a predefined action (submit or load) or custom actions you define on this form.
7713      * @param {String} actionName The name of the action type
7714      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7715      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7716      * accept other config options):
7717      * <pre>
7718 Property          Type             Description
7719 ----------------  ---------------  ----------------------------------------------------------------------------------
7720 url               String           The url for the action (defaults to the form's url)
7721 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7722 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7723 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7724                                    validate the form on the client (defaults to false)
7725      * </pre>
7726      * @return {BasicForm} this
7727      */
7728     doAction : function(action, options){
7729         if(typeof action == 'string'){
7730             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7731         }
7732         if(this.fireEvent('beforeaction', this, action) !== false){
7733             this.beforeAction(action);
7734             action.run.defer(100, action);
7735         }
7736         return this;
7737     },
7738
7739     // private
7740     beforeAction : function(action){
7741         var o = action.options;
7742
7743         if(this.loadMask){
7744             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7745         }
7746         // not really supported yet.. ??
7747
7748         //if(this.waitMsgTarget === true){
7749         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7750         //}else if(this.waitMsgTarget){
7751         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7752         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7753         //}else {
7754         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7755        // }
7756
7757     },
7758
7759     // private
7760     afterAction : function(action, success){
7761         this.activeAction = null;
7762         var o = action.options;
7763
7764         //if(this.waitMsgTarget === true){
7765             this.el.unmask();
7766         //}else if(this.waitMsgTarget){
7767         //    this.waitMsgTarget.unmask();
7768         //}else{
7769         //    Roo.MessageBox.updateProgress(1);
7770         //    Roo.MessageBox.hide();
7771        // }
7772         //
7773         if(success){
7774             if(o.reset){
7775                 this.reset();
7776             }
7777             Roo.callback(o.success, o.scope, [this, action]);
7778             this.fireEvent('actioncomplete', this, action);
7779
7780         }else{
7781
7782             // failure condition..
7783             // we have a scenario where updates need confirming.
7784             // eg. if a locking scenario exists..
7785             // we look for { errors : { needs_confirm : true }} in the response.
7786             if (
7787                 (typeof(action.result) != 'undefined')  &&
7788                 (typeof(action.result.errors) != 'undefined')  &&
7789                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7790            ){
7791                 var _t = this;
7792                 Roo.log("not supported yet");
7793                  /*
7794
7795                 Roo.MessageBox.confirm(
7796                     "Change requires confirmation",
7797                     action.result.errorMsg,
7798                     function(r) {
7799                         if (r != 'yes') {
7800                             return;
7801                         }
7802                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7803                     }
7804
7805                 );
7806                 */
7807
7808
7809                 return;
7810             }
7811
7812             Roo.callback(o.failure, o.scope, [this, action]);
7813             // show an error message if no failed handler is set..
7814             if (!this.hasListener('actionfailed')) {
7815                 Roo.log("need to add dialog support");
7816                 /*
7817                 Roo.MessageBox.alert("Error",
7818                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7819                         action.result.errorMsg :
7820                         "Saving Failed, please check your entries or try again"
7821                 );
7822                 */
7823             }
7824
7825             this.fireEvent('actionfailed', this, action);
7826         }
7827
7828     },
7829     /**
7830      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7831      * @param {String} id The value to search for
7832      * @return Field
7833      */
7834     findField : function(id){
7835         var items = this.getItems();
7836         var field = items.get(id);
7837         if(!field){
7838              items.each(function(f){
7839                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7840                     field = f;
7841                     return false;
7842                 }
7843                 return true;
7844             });
7845         }
7846         return field || null;
7847     },
7848      /**
7849      * Mark fields in this form invalid in bulk.
7850      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7851      * @return {BasicForm} this
7852      */
7853     markInvalid : function(errors){
7854         if(errors instanceof Array){
7855             for(var i = 0, len = errors.length; i < len; i++){
7856                 var fieldError = errors[i];
7857                 var f = this.findField(fieldError.id);
7858                 if(f){
7859                     f.markInvalid(fieldError.msg);
7860                 }
7861             }
7862         }else{
7863             var field, id;
7864             for(id in errors){
7865                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7866                     field.markInvalid(errors[id]);
7867                 }
7868             }
7869         }
7870         //Roo.each(this.childForms || [], function (f) {
7871         //    f.markInvalid(errors);
7872         //});
7873
7874         return this;
7875     },
7876
7877     /**
7878      * Set values for fields in this form in bulk.
7879      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7880      * @return {BasicForm} this
7881      */
7882     setValues : function(values){
7883         if(values instanceof Array){ // array of objects
7884             for(var i = 0, len = values.length; i < len; i++){
7885                 var v = values[i];
7886                 var f = this.findField(v.id);
7887                 if(f){
7888                     f.setValue(v.value);
7889                     if(this.trackResetOnLoad){
7890                         f.originalValue = f.getValue();
7891                     }
7892                 }
7893             }
7894         }else{ // object hash
7895             var field, id;
7896             for(id in values){
7897                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7898
7899                     if (field.setFromData &&
7900                         field.valueField &&
7901                         field.displayField &&
7902                         // combos' with local stores can
7903                         // be queried via setValue()
7904                         // to set their value..
7905                         (field.store && !field.store.isLocal)
7906                         ) {
7907                         // it's a combo
7908                         var sd = { };
7909                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7910                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7911                         field.setFromData(sd);
7912
7913                     } else {
7914                         field.setValue(values[id]);
7915                     }
7916
7917
7918                     if(this.trackResetOnLoad){
7919                         field.originalValue = field.getValue();
7920                     }
7921                 }
7922             }
7923         }
7924
7925         //Roo.each(this.childForms || [], function (f) {
7926         //    f.setValues(values);
7927         //});
7928
7929         return this;
7930     },
7931
7932     /**
7933      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7934      * they are returned as an array.
7935      * @param {Boolean} asString
7936      * @return {Object}
7937      */
7938     getValues : function(asString){
7939         //if (this.childForms) {
7940             // copy values from the child forms
7941         //    Roo.each(this.childForms, function (f) {
7942         //        this.setValues(f.getValues());
7943         //    }, this);
7944         //}
7945
7946
7947
7948         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7949         if(asString === true){
7950             return fs;
7951         }
7952         return Roo.urlDecode(fs);
7953     },
7954
7955     /**
7956      * Returns the fields in this form as an object with key/value pairs.
7957      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7958      * @return {Object}
7959      */
7960     getFieldValues : function(with_hidden)
7961     {
7962         var items = this.getItems();
7963         var ret = {};
7964         items.each(function(f){
7965             if (!f.getName()) {
7966                 return;
7967             }
7968             var v = f.getValue();
7969             if (f.inputType =='radio') {
7970                 if (typeof(ret[f.getName()]) == 'undefined') {
7971                     ret[f.getName()] = ''; // empty..
7972                 }
7973
7974                 if (!f.el.dom.checked) {
7975                     return;
7976
7977                 }
7978                 v = f.el.dom.value;
7979
7980             }
7981
7982             // not sure if this supported any more..
7983             if ((typeof(v) == 'object') && f.getRawValue) {
7984                 v = f.getRawValue() ; // dates..
7985             }
7986             // combo boxes where name != hiddenName...
7987             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7988                 ret[f.name] = f.getRawValue();
7989             }
7990             ret[f.getName()] = v;
7991         });
7992
7993         return ret;
7994     },
7995
7996     /**
7997      * Clears all invalid messages in this form.
7998      * @return {BasicForm} this
7999      */
8000     clearInvalid : function(){
8001         var items = this.getItems();
8002
8003         items.each(function(f){
8004            f.clearInvalid();
8005         });
8006
8007
8008
8009         return this;
8010     },
8011
8012     /**
8013      * Resets this form.
8014      * @return {BasicForm} this
8015      */
8016     reset : function(){
8017         var items = this.getItems();
8018         items.each(function(f){
8019             f.reset();
8020         });
8021
8022         Roo.each(this.childForms || [], function (f) {
8023             f.reset();
8024         });
8025
8026
8027         return this;
8028     },
8029     getItems : function()
8030     {
8031         var r=new Roo.util.MixedCollection(false, function(o){
8032             return o.id || (o.id = Roo.id());
8033         });
8034         var iter = function(el) {
8035             if (el.inputEl) {
8036                 r.add(el);
8037             }
8038             if (!el.items) {
8039                 return;
8040             }
8041             Roo.each(el.items,function(e) {
8042                 iter(e);
8043             });
8044
8045
8046         };
8047
8048         iter(this);
8049         return r;
8050
8051
8052
8053
8054     }
8055
8056 });
8057
8058 Roo.apply(Roo.bootstrap.Form, {
8059     
8060     popover : {
8061         
8062         padding : 5,
8063         
8064         isApplied : false,
8065         
8066         isMasked : false,
8067         
8068         form : false,
8069         
8070         target : false,
8071         
8072         toolTip : false,
8073         
8074         intervalID : false,
8075         
8076         maskEl : false,
8077         
8078         apply : function()
8079         {
8080             if(this.isApplied){
8081                 return;
8082             }
8083             
8084             this.maskEl = {
8085                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8086                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8087                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8088                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8089             };
8090             
8091             this.maskEl.top.enableDisplayMode("block");
8092             this.maskEl.left.enableDisplayMode("block");
8093             this.maskEl.bottom.enableDisplayMode("block");
8094             this.maskEl.right.enableDisplayMode("block");
8095             
8096             this.toolTip = new Roo.bootstrap.Tooltip({
8097                 cls : 'roo-form-error-popover',
8098                 alignment : {
8099                     'left' : ['r-l', [-2,0], 'right'],
8100                     'right' : ['l-r', [2,0], 'left'],
8101                     'bottom' : ['tl-bl', [0,2], 'top'],
8102                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8103                 }
8104             });
8105             
8106             this.toolTip.render(Roo.get(document.body));
8107
8108             this.toolTip.el.enableDisplayMode("block");
8109             
8110             Roo.get(document.body).on('click', function(){
8111                 this.unmask();
8112             }, this);
8113             
8114             Roo.get(document.body).on('touchstart', function(){
8115                 this.unmask();
8116             }, this);
8117             
8118             this.isApplied = true
8119         },
8120         
8121         mask : function(form, target)
8122         {
8123             this.form = form;
8124             
8125             this.target = target;
8126             
8127             if(!this.form.errorMask || !target.el){
8128                 return;
8129             }
8130             
8131             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8132             
8133             var ot = this.target.el.calcOffsetsTo(scrollable);
8134             
8135             var scrollTo = ot[1] - this.form.maskOffset;
8136             
8137             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8138             
8139             scrollable.scrollTo('top', scrollTo);
8140             
8141             var box = this.target.el.getBox();
8142             Roo.log(box);
8143             var zIndex = Roo.bootstrap.Modal.zIndex++;
8144
8145             
8146             this.maskEl.top.setStyle('position', 'absolute');
8147             this.maskEl.top.setStyle('z-index', zIndex);
8148             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8149             this.maskEl.top.setLeft(0);
8150             this.maskEl.top.setTop(0);
8151             this.maskEl.top.show();
8152             
8153             this.maskEl.left.setStyle('position', 'absolute');
8154             this.maskEl.left.setStyle('z-index', zIndex);
8155             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8156             this.maskEl.left.setLeft(0);
8157             this.maskEl.left.setTop(box.y - this.padding);
8158             this.maskEl.left.show();
8159
8160             this.maskEl.bottom.setStyle('position', 'absolute');
8161             this.maskEl.bottom.setStyle('z-index', zIndex);
8162             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8163             this.maskEl.bottom.setLeft(0);
8164             this.maskEl.bottom.setTop(box.bottom + this.padding);
8165             this.maskEl.bottom.show();
8166
8167             this.maskEl.right.setStyle('position', 'absolute');
8168             this.maskEl.right.setStyle('z-index', zIndex);
8169             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8170             this.maskEl.right.setLeft(box.right + this.padding);
8171             this.maskEl.right.setTop(box.y - this.padding);
8172             this.maskEl.right.show();
8173
8174             this.toolTip.bindEl = this.target.el;
8175
8176             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8177
8178             var tip = this.target.blankText;
8179
8180             if(this.target.getValue() !== '' ) {
8181                 
8182                 if (this.target.invalidText.length) {
8183                     tip = this.target.invalidText;
8184                 } else if (this.target.regexText.length){
8185                     tip = this.target.regexText;
8186                 }
8187             }
8188
8189             this.toolTip.show(tip);
8190
8191             this.intervalID = window.setInterval(function() {
8192                 Roo.bootstrap.Form.popover.unmask();
8193             }, 10000);
8194
8195             window.onwheel = function(){ return false;};
8196             
8197             (function(){ this.isMasked = true; }).defer(500, this);
8198             
8199         },
8200         
8201         unmask : function()
8202         {
8203             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8204                 return;
8205             }
8206             
8207             this.maskEl.top.setStyle('position', 'absolute');
8208             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8209             this.maskEl.top.hide();
8210
8211             this.maskEl.left.setStyle('position', 'absolute');
8212             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8213             this.maskEl.left.hide();
8214
8215             this.maskEl.bottom.setStyle('position', 'absolute');
8216             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8217             this.maskEl.bottom.hide();
8218
8219             this.maskEl.right.setStyle('position', 'absolute');
8220             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.right.hide();
8222             
8223             this.toolTip.hide();
8224             
8225             this.toolTip.el.hide();
8226             
8227             window.onwheel = function(){ return true;};
8228             
8229             if(this.intervalID){
8230                 window.clearInterval(this.intervalID);
8231                 this.intervalID = false;
8232             }
8233             
8234             this.isMasked = false;
8235             
8236         }
8237         
8238     }
8239     
8240 });
8241
8242 /*
8243  * Based on:
8244  * Ext JS Library 1.1.1
8245  * Copyright(c) 2006-2007, Ext JS, LLC.
8246  *
8247  * Originally Released Under LGPL - original licence link has changed is not relivant.
8248  *
8249  * Fork - LGPL
8250  * <script type="text/javascript">
8251  */
8252 /**
8253  * @class Roo.form.VTypes
8254  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8255  * @singleton
8256  */
8257 Roo.form.VTypes = function(){
8258     // closure these in so they are only created once.
8259     var alpha = /^[a-zA-Z_]+$/;
8260     var alphanum = /^[a-zA-Z0-9_]+$/;
8261     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8262     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8263
8264     // All these messages and functions are configurable
8265     return {
8266         /**
8267          * The function used to validate email addresses
8268          * @param {String} value The email address
8269          */
8270         'email' : function(v){
8271             return email.test(v);
8272         },
8273         /**
8274          * The error text to display when the email validation function returns false
8275          * @type String
8276          */
8277         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8278         /**
8279          * The keystroke filter mask to be applied on email input
8280          * @type RegExp
8281          */
8282         'emailMask' : /[a-z0-9_\.\-@]/i,
8283
8284         /**
8285          * The function used to validate URLs
8286          * @param {String} value The URL
8287          */
8288         'url' : function(v){
8289             return url.test(v);
8290         },
8291         /**
8292          * The error text to display when the url validation function returns false
8293          * @type String
8294          */
8295         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8296         
8297         /**
8298          * The function used to validate alpha values
8299          * @param {String} value The value
8300          */
8301         'alpha' : function(v){
8302             return alpha.test(v);
8303         },
8304         /**
8305          * The error text to display when the alpha validation function returns false
8306          * @type String
8307          */
8308         'alphaText' : 'This field should only contain letters and _',
8309         /**
8310          * The keystroke filter mask to be applied on alpha input
8311          * @type RegExp
8312          */
8313         'alphaMask' : /[a-z_]/i,
8314
8315         /**
8316          * The function used to validate alphanumeric values
8317          * @param {String} value The value
8318          */
8319         'alphanum' : function(v){
8320             return alphanum.test(v);
8321         },
8322         /**
8323          * The error text to display when the alphanumeric validation function returns false
8324          * @type String
8325          */
8326         'alphanumText' : 'This field should only contain letters, numbers and _',
8327         /**
8328          * The keystroke filter mask to be applied on alphanumeric input
8329          * @type RegExp
8330          */
8331         'alphanumMask' : /[a-z0-9_]/i
8332     };
8333 }();/*
8334  * - LGPL
8335  *
8336  * Input
8337  * 
8338  */
8339
8340 /**
8341  * @class Roo.bootstrap.Input
8342  * @extends Roo.bootstrap.Component
8343  * Bootstrap Input class
8344  * @cfg {Boolean} disabled is it disabled
8345  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8346  * @cfg {String} name name of the input
8347  * @cfg {string} fieldLabel - the label associated
8348  * @cfg {string} placeholder - placeholder to put in text.
8349  * @cfg {string}  before - input group add on before
8350  * @cfg {string} after - input group add on after
8351  * @cfg {string} size - (lg|sm) or leave empty..
8352  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8353  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8354  * @cfg {Number} md colspan out of 12 for computer-sized screens
8355  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8356  * @cfg {string} value default value of the input
8357  * @cfg {Number} labelWidth set the width of label 
8358  * @cfg {Number} labellg set the width of label (1-12)
8359  * @cfg {Number} labelmd set the width of label (1-12)
8360  * @cfg {Number} labelsm set the width of label (1-12)
8361  * @cfg {Number} labelxs set the width of label (1-12)
8362  * @cfg {String} labelAlign (top|left)
8363  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8364  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8365  * @cfg {String} indicatorpos (left|right) default left
8366
8367  * @cfg {String} align (left|center|right) Default left
8368  * @cfg {Boolean} forceFeedback (true|false) Default false
8369  * 
8370  * 
8371  * 
8372  * 
8373  * @constructor
8374  * Create a new Input
8375  * @param {Object} config The config object
8376  */
8377
8378 Roo.bootstrap.Input = function(config){
8379     
8380     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8381     
8382     this.addEvents({
8383         /**
8384          * @event focus
8385          * Fires when this field receives input focus.
8386          * @param {Roo.form.Field} this
8387          */
8388         focus : true,
8389         /**
8390          * @event blur
8391          * Fires when this field loses input focus.
8392          * @param {Roo.form.Field} this
8393          */
8394         blur : true,
8395         /**
8396          * @event specialkey
8397          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8398          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8399          * @param {Roo.form.Field} this
8400          * @param {Roo.EventObject} e The event object
8401          */
8402         specialkey : true,
8403         /**
8404          * @event change
8405          * Fires just before the field blurs if the field value has changed.
8406          * @param {Roo.form.Field} this
8407          * @param {Mixed} newValue The new value
8408          * @param {Mixed} oldValue The original value
8409          */
8410         change : true,
8411         /**
8412          * @event invalid
8413          * Fires after the field has been marked as invalid.
8414          * @param {Roo.form.Field} this
8415          * @param {String} msg The validation message
8416          */
8417         invalid : true,
8418         /**
8419          * @event valid
8420          * Fires after the field has been validated with no errors.
8421          * @param {Roo.form.Field} this
8422          */
8423         valid : true,
8424          /**
8425          * @event keyup
8426          * Fires after the key up
8427          * @param {Roo.form.Field} this
8428          * @param {Roo.EventObject}  e The event Object
8429          */
8430         keyup : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8435      /**
8436      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8437       automatic validation (defaults to "keyup").
8438      */
8439     validationEvent : "keyup",
8440      /**
8441      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8442      */
8443     validateOnBlur : true,
8444     /**
8445      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8446      */
8447     validationDelay : 250,
8448      /**
8449      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8450      */
8451     focusClass : "x-form-focus",  // not needed???
8452     
8453        
8454     /**
8455      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8456      */
8457     invalidClass : "has-warning",
8458     
8459     /**
8460      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8461      */
8462     validClass : "has-success",
8463     
8464     /**
8465      * @cfg {Boolean} hasFeedback (true|false) default true
8466      */
8467     hasFeedback : true,
8468     
8469     /**
8470      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8471      */
8472     invalidFeedbackClass : "glyphicon-warning-sign",
8473     
8474     /**
8475      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8476      */
8477     validFeedbackClass : "glyphicon-ok",
8478     
8479     /**
8480      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8481      */
8482     selectOnFocus : false,
8483     
8484      /**
8485      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8486      */
8487     maskRe : null,
8488        /**
8489      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8490      */
8491     vtype : null,
8492     
8493       /**
8494      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8495      */
8496     disableKeyFilter : false,
8497     
8498        /**
8499      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8500      */
8501     disabled : false,
8502      /**
8503      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8504      */
8505     allowBlank : true,
8506     /**
8507      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8508      */
8509     blankText : "Please complete this mandatory field",
8510     
8511      /**
8512      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8513      */
8514     minLength : 0,
8515     /**
8516      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8517      */
8518     maxLength : Number.MAX_VALUE,
8519     /**
8520      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8521      */
8522     minLengthText : "The minimum length for this field is {0}",
8523     /**
8524      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8525      */
8526     maxLengthText : "The maximum length for this field is {0}",
8527   
8528     
8529     /**
8530      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8531      * If available, this function will be called only after the basic validators all return true, and will be passed the
8532      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8533      */
8534     validator : null,
8535     /**
8536      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8537      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8538      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8539      */
8540     regex : null,
8541     /**
8542      * @cfg {String} regexText -- Depricated - use Invalid Text
8543      */
8544     regexText : "",
8545     
8546     /**
8547      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8548      */
8549     invalidText : "",
8550     
8551     
8552     
8553     autocomplete: false,
8554     
8555     
8556     fieldLabel : '',
8557     inputType : 'text',
8558     
8559     name : false,
8560     placeholder: false,
8561     before : false,
8562     after : false,
8563     size : false,
8564     hasFocus : false,
8565     preventMark: false,
8566     isFormField : true,
8567     value : '',
8568     labelWidth : 2,
8569     labelAlign : false,
8570     readOnly : false,
8571     align : false,
8572     formatedValue : false,
8573     forceFeedback : false,
8574     
8575     indicatorpos : 'left',
8576     
8577     labellg : 0,
8578     labelmd : 0,
8579     labelsm : 0,
8580     labelxs : 0,
8581     
8582     parentLabelAlign : function()
8583     {
8584         var parent = this;
8585         while (parent.parent()) {
8586             parent = parent.parent();
8587             if (typeof(parent.labelAlign) !='undefined') {
8588                 return parent.labelAlign;
8589             }
8590         }
8591         return 'left';
8592         
8593     },
8594     
8595     getAutoCreate : function()
8596     {
8597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8598         
8599         var id = Roo.id();
8600         
8601         var cfg = {};
8602         
8603         if(this.inputType != 'hidden'){
8604             cfg.cls = 'form-group' //input-group
8605         }
8606         
8607         var input =  {
8608             tag: 'input',
8609             id : id,
8610             type : this.inputType,
8611             value : this.value,
8612             cls : 'form-control',
8613             placeholder : this.placeholder || '',
8614             autocomplete : this.autocomplete || 'new-password'
8615         };
8616         
8617         if(this.align){
8618             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8619         }
8620         
8621         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8622             input.maxLength = this.maxLength;
8623         }
8624         
8625         if (this.disabled) {
8626             input.disabled=true;
8627         }
8628         
8629         if (this.readOnly) {
8630             input.readonly=true;
8631         }
8632         
8633         if (this.name) {
8634             input.name = this.name;
8635         }
8636         
8637         if (this.size) {
8638             input.cls += ' input-' + this.size;
8639         }
8640         
8641         var settings=this;
8642         ['xs','sm','md','lg'].map(function(size){
8643             if (settings[size]) {
8644                 cfg.cls += ' col-' + size + '-' + settings[size];
8645             }
8646         });
8647         
8648         var inputblock = input;
8649         
8650         var feedback = {
8651             tag: 'span',
8652             cls: 'glyphicon form-control-feedback'
8653         };
8654             
8655         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8656             
8657             inputblock = {
8658                 cls : 'has-feedback',
8659                 cn :  [
8660                     input,
8661                     feedback
8662                 ] 
8663             };  
8664         }
8665         
8666         if (this.before || this.after) {
8667             
8668             inputblock = {
8669                 cls : 'input-group',
8670                 cn :  [] 
8671             };
8672             
8673             if (this.before && typeof(this.before) == 'string') {
8674                 
8675                 inputblock.cn.push({
8676                     tag :'span',
8677                     cls : 'roo-input-before input-group-addon',
8678                     html : this.before
8679                 });
8680             }
8681             if (this.before && typeof(this.before) == 'object') {
8682                 this.before = Roo.factory(this.before);
8683                 
8684                 inputblock.cn.push({
8685                     tag :'span',
8686                     cls : 'roo-input-before input-group-' +
8687                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8688                 });
8689             }
8690             
8691             inputblock.cn.push(input);
8692             
8693             if (this.after && typeof(this.after) == 'string') {
8694                 inputblock.cn.push({
8695                     tag :'span',
8696                     cls : 'roo-input-after input-group-addon',
8697                     html : this.after
8698                 });
8699             }
8700             if (this.after && typeof(this.after) == 'object') {
8701                 this.after = Roo.factory(this.after);
8702                 
8703                 inputblock.cn.push({
8704                     tag :'span',
8705                     cls : 'roo-input-after input-group-' +
8706                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8707                 });
8708             }
8709             
8710             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8711                 inputblock.cls += ' has-feedback';
8712                 inputblock.cn.push(feedback);
8713             }
8714         };
8715         
8716         if (align ==='left' && this.fieldLabel.length) {
8717             
8718             cfg.cls += ' roo-form-group-label-left';
8719             
8720             cfg.cn = [
8721                 {
8722                     tag : 'i',
8723                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8724                     tooltip : 'This field is required'
8725                 },
8726                 {
8727                     tag: 'label',
8728                     'for' :  id,
8729                     cls : 'control-label',
8730                     html : this.fieldLabel
8731
8732                 },
8733                 {
8734                     cls : "", 
8735                     cn: [
8736                         inputblock
8737                     ]
8738                 }
8739             ];
8740             
8741             var labelCfg = cfg.cn[1];
8742             var contentCfg = cfg.cn[2];
8743             
8744             if(this.indicatorpos == 'right'){
8745                 cfg.cn = [
8746                     {
8747                         tag: 'label',
8748                         'for' :  id,
8749                         cls : 'control-label',
8750                         html : this.fieldLabel
8751
8752                     },
8753                     {
8754                         tag : 'i',
8755                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8756                         tooltip : 'This field is required'
8757                     },
8758                     {
8759                         cls : "",
8760                         cn: [
8761                             inputblock
8762                         ]
8763                     }
8764
8765                 ];
8766                 
8767                 labelCfg = cfg.cn[0];
8768                 contentCfg = cfg.cn[2];
8769             
8770             }
8771             
8772             if(this.labelWidth > 12){
8773                 labelCfg.style = "width: " + this.labelWidth + 'px';
8774             }
8775             
8776             if(this.labelWidth < 13 && this.labelmd == 0){
8777                 this.labelmd = this.labelWidth;
8778             }
8779             
8780             if(this.labellg > 0){
8781                 labelCfg.cls += ' col-lg-' + this.labellg;
8782                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8783             }
8784             
8785             if(this.labelmd > 0){
8786                 labelCfg.cls += ' col-md-' + this.labelmd;
8787                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8788             }
8789             
8790             if(this.labelsm > 0){
8791                 labelCfg.cls += ' col-sm-' + this.labelsm;
8792                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8793             }
8794             
8795             if(this.labelxs > 0){
8796                 labelCfg.cls += ' col-xs-' + this.labelxs;
8797                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8798             }
8799             
8800             
8801         } else if ( this.fieldLabel.length) {
8802                 
8803             cfg.cn = [
8804                 {
8805                     tag : 'i',
8806                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8807                     tooltip : 'This field is required'
8808                 },
8809                 {
8810                     tag: 'label',
8811                    //cls : 'input-group-addon',
8812                     html : this.fieldLabel
8813
8814                 },
8815
8816                inputblock
8817
8818            ];
8819            
8820            if(this.indicatorpos == 'right'){
8821                 
8822                 cfg.cn = [
8823                     {
8824                         tag: 'label',
8825                        //cls : 'input-group-addon',
8826                         html : this.fieldLabel
8827
8828                     },
8829                     {
8830                         tag : 'i',
8831                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8832                         tooltip : 'This field is required'
8833                     },
8834
8835                    inputblock
8836
8837                ];
8838
8839             }
8840
8841         } else {
8842             
8843             cfg.cn = [
8844
8845                     inputblock
8846
8847             ];
8848                 
8849                 
8850         };
8851         
8852         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8853            cfg.cls += ' navbar-form';
8854         }
8855         
8856         if (this.parentType === 'NavGroup') {
8857            cfg.cls += ' navbar-form';
8858            cfg.tag = 'li';
8859         }
8860         
8861         return cfg;
8862         
8863     },
8864     /**
8865      * return the real input element.
8866      */
8867     inputEl: function ()
8868     {
8869         return this.el.select('input.form-control',true).first();
8870     },
8871     
8872     tooltipEl : function()
8873     {
8874         return this.inputEl();
8875     },
8876     
8877     indicatorEl : function()
8878     {
8879         var indicator = this.el.select('i.roo-required-indicator',true).first();
8880         
8881         if(!indicator){
8882             return false;
8883         }
8884         
8885         return indicator;
8886         
8887     },
8888     
8889     setDisabled : function(v)
8890     {
8891         var i  = this.inputEl().dom;
8892         if (!v) {
8893             i.removeAttribute('disabled');
8894             return;
8895             
8896         }
8897         i.setAttribute('disabled','true');
8898     },
8899     initEvents : function()
8900     {
8901           
8902         this.inputEl().on("keydown" , this.fireKey,  this);
8903         this.inputEl().on("focus", this.onFocus,  this);
8904         this.inputEl().on("blur", this.onBlur,  this);
8905         
8906         this.inputEl().relayEvent('keyup', this);
8907         
8908         this.indicator = this.indicatorEl();
8909         
8910         if(this.indicator){
8911             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8912             this.indicator.hide();
8913         }
8914  
8915         // reference to original value for reset
8916         this.originalValue = this.getValue();
8917         //Roo.form.TextField.superclass.initEvents.call(this);
8918         if(this.validationEvent == 'keyup'){
8919             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8920             this.inputEl().on('keyup', this.filterValidation, this);
8921         }
8922         else if(this.validationEvent !== false){
8923             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8924         }
8925         
8926         if(this.selectOnFocus){
8927             this.on("focus", this.preFocus, this);
8928             
8929         }
8930         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8931             this.inputEl().on("keypress", this.filterKeys, this);
8932         } else {
8933             this.inputEl().relayEvent('keypress', this);
8934         }
8935        /* if(this.grow){
8936             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8937             this.el.on("click", this.autoSize,  this);
8938         }
8939         */
8940         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8941             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8942         }
8943         
8944         if (typeof(this.before) == 'object') {
8945             this.before.render(this.el.select('.roo-input-before',true).first());
8946         }
8947         if (typeof(this.after) == 'object') {
8948             this.after.render(this.el.select('.roo-input-after',true).first());
8949         }
8950         
8951         
8952     },
8953     filterValidation : function(e){
8954         if(!e.isNavKeyPress()){
8955             this.validationTask.delay(this.validationDelay);
8956         }
8957     },
8958      /**
8959      * Validates the field value
8960      * @return {Boolean} True if the value is valid, else false
8961      */
8962     validate : function(){
8963         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8964         if(this.disabled || this.validateValue(this.getRawValue())){
8965             this.markValid();
8966             return true;
8967         }
8968         
8969         this.markInvalid();
8970         return false;
8971     },
8972     
8973     
8974     /**
8975      * Validates a value according to the field's validation rules and marks the field as invalid
8976      * if the validation fails
8977      * @param {Mixed} value The value to validate
8978      * @return {Boolean} True if the value is valid, else false
8979      */
8980     validateValue : function(value){
8981         if(value.length < 1)  { // if it's blank
8982             if(this.allowBlank){
8983                 return true;
8984             }            
8985             return this.inputEl().hasClass('hide') ? true : false;
8986         }
8987         
8988         if(value.length < this.minLength){
8989             return false;
8990         }
8991         if(value.length > this.maxLength){
8992             return false;
8993         }
8994         if(this.vtype){
8995             var vt = Roo.form.VTypes;
8996             if(!vt[this.vtype](value, this)){
8997                 return false;
8998             }
8999         }
9000         if(typeof this.validator == "function"){
9001             var msg = this.validator(value);
9002             if(msg !== true){
9003                 return false;
9004             }
9005             if (typeof(msg) == 'string') {
9006                 this.invalidText = msg;
9007             }
9008         }
9009         
9010         if(this.regex && !this.regex.test(value)){
9011             return false;
9012         }
9013         
9014         return true;
9015     },
9016
9017     
9018     
9019      // private
9020     fireKey : function(e){
9021         //Roo.log('field ' + e.getKey());
9022         if(e.isNavKeyPress()){
9023             this.fireEvent("specialkey", this, e);
9024         }
9025     },
9026     focus : function (selectText){
9027         if(this.rendered){
9028             this.inputEl().focus();
9029             if(selectText === true){
9030                 this.inputEl().dom.select();
9031             }
9032         }
9033         return this;
9034     } ,
9035     
9036     onFocus : function(){
9037         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9038            // this.el.addClass(this.focusClass);
9039         }
9040         if(!this.hasFocus){
9041             this.hasFocus = true;
9042             this.startValue = this.getValue();
9043             this.fireEvent("focus", this);
9044         }
9045     },
9046     
9047     beforeBlur : Roo.emptyFn,
9048
9049     
9050     // private
9051     onBlur : function(){
9052         this.beforeBlur();
9053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054             //this.el.removeClass(this.focusClass);
9055         }
9056         this.hasFocus = false;
9057         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9058             this.validate();
9059         }
9060         var v = this.getValue();
9061         if(String(v) !== String(this.startValue)){
9062             this.fireEvent('change', this, v, this.startValue);
9063         }
9064         this.fireEvent("blur", this);
9065     },
9066     
9067     /**
9068      * Resets the current field value to the originally loaded value and clears any validation messages
9069      */
9070     reset : function(){
9071         this.setValue(this.originalValue);
9072         this.validate();
9073     },
9074      /**
9075      * Returns the name of the field
9076      * @return {Mixed} name The name field
9077      */
9078     getName: function(){
9079         return this.name;
9080     },
9081      /**
9082      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9083      * @return {Mixed} value The field value
9084      */
9085     getValue : function(){
9086         
9087         var v = this.inputEl().getValue();
9088         
9089         return v;
9090     },
9091     /**
9092      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9093      * @return {Mixed} value The field value
9094      */
9095     getRawValue : function(){
9096         var v = this.inputEl().getValue();
9097         
9098         return v;
9099     },
9100     
9101     /**
9102      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9103      * @param {Mixed} value The value to set
9104      */
9105     setRawValue : function(v){
9106         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9107     },
9108     
9109     selectText : function(start, end){
9110         var v = this.getRawValue();
9111         if(v.length > 0){
9112             start = start === undefined ? 0 : start;
9113             end = end === undefined ? v.length : end;
9114             var d = this.inputEl().dom;
9115             if(d.setSelectionRange){
9116                 d.setSelectionRange(start, end);
9117             }else if(d.createTextRange){
9118                 var range = d.createTextRange();
9119                 range.moveStart("character", start);
9120                 range.moveEnd("character", v.length-end);
9121                 range.select();
9122             }
9123         }
9124     },
9125     
9126     /**
9127      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9128      * @param {Mixed} value The value to set
9129      */
9130     setValue : function(v){
9131         this.value = v;
9132         if(this.rendered){
9133             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9134             this.validate();
9135         }
9136     },
9137     
9138     /*
9139     processValue : function(value){
9140         if(this.stripCharsRe){
9141             var newValue = value.replace(this.stripCharsRe, '');
9142             if(newValue !== value){
9143                 this.setRawValue(newValue);
9144                 return newValue;
9145             }
9146         }
9147         return value;
9148     },
9149   */
9150     preFocus : function(){
9151         
9152         if(this.selectOnFocus){
9153             this.inputEl().dom.select();
9154         }
9155     },
9156     filterKeys : function(e){
9157         var k = e.getKey();
9158         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9159             return;
9160         }
9161         var c = e.getCharCode(), cc = String.fromCharCode(c);
9162         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9163             return;
9164         }
9165         if(!this.maskRe.test(cc)){
9166             e.stopEvent();
9167         }
9168     },
9169      /**
9170      * Clear any invalid styles/messages for this field
9171      */
9172     clearInvalid : function(){
9173         
9174         if(!this.el || this.preventMark){ // not rendered
9175             return;
9176         }
9177         
9178      
9179         this.el.removeClass(this.invalidClass);
9180         
9181         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9182             
9183             var feedback = this.el.select('.form-control-feedback', true).first();
9184             
9185             if(feedback){
9186                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9187             }
9188             
9189         }
9190         
9191         this.fireEvent('valid', this);
9192     },
9193     
9194      /**
9195      * Mark this field as valid
9196      */
9197     markValid : function()
9198     {
9199         if(!this.el  || this.preventMark){ // not rendered...
9200             return;
9201         }
9202         
9203         this.el.removeClass([this.invalidClass, this.validClass]);
9204         
9205         var feedback = this.el.select('.form-control-feedback', true).first();
9206             
9207         if(feedback){
9208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9209         }
9210
9211         if(this.disabled){
9212             return;
9213         }
9214         
9215         if(this.allowBlank && !this.getRawValue().length){
9216             return;
9217         }
9218         
9219         if(this.indicator){
9220             this.indicator.hide();
9221         }
9222         
9223         this.el.addClass(this.validClass);
9224         
9225         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9226             
9227             var feedback = this.el.select('.form-control-feedback', true).first();
9228             
9229             if(feedback){
9230                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9231                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9232             }
9233             
9234         }
9235         
9236         this.fireEvent('valid', this);
9237     },
9238     
9239      /**
9240      * Mark this field as invalid
9241      * @param {String} msg The validation message
9242      */
9243     markInvalid : function(msg)
9244     {
9245         if(!this.el  || this.preventMark){ // not rendered
9246             return;
9247         }
9248         
9249         this.el.removeClass([this.invalidClass, this.validClass]);
9250         
9251         var feedback = this.el.select('.form-control-feedback', true).first();
9252             
9253         if(feedback){
9254             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9255         }
9256
9257         if(this.disabled){
9258             return;
9259         }
9260         
9261         if(this.allowBlank && !this.getRawValue().length){
9262             return;
9263         }
9264         
9265         if(this.indicator){
9266             this.indicator.show();
9267         }
9268         
9269         this.el.addClass(this.invalidClass);
9270         
9271         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9272             
9273             var feedback = this.el.select('.form-control-feedback', true).first();
9274             
9275             if(feedback){
9276                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9277                 
9278                 if(this.getValue().length || this.forceFeedback){
9279                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9280                 }
9281                 
9282             }
9283             
9284         }
9285         
9286         this.fireEvent('invalid', this, msg);
9287     },
9288     // private
9289     SafariOnKeyDown : function(event)
9290     {
9291         // this is a workaround for a password hang bug on chrome/ webkit.
9292         if (this.inputEl().dom.type != 'password') {
9293             return;
9294         }
9295         
9296         var isSelectAll = false;
9297         
9298         if(this.inputEl().dom.selectionEnd > 0){
9299             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9300         }
9301         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9302             event.preventDefault();
9303             this.setValue('');
9304             return;
9305         }
9306         
9307         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9308             
9309             event.preventDefault();
9310             // this is very hacky as keydown always get's upper case.
9311             //
9312             var cc = String.fromCharCode(event.getCharCode());
9313             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9314             
9315         }
9316     },
9317     adjustWidth : function(tag, w){
9318         tag = tag.toLowerCase();
9319         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9320             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9321                 if(tag == 'input'){
9322                     return w + 2;
9323                 }
9324                 if(tag == 'textarea'){
9325                     return w-2;
9326                 }
9327             }else if(Roo.isOpera){
9328                 if(tag == 'input'){
9329                     return w + 2;
9330                 }
9331                 if(tag == 'textarea'){
9332                     return w-2;
9333                 }
9334             }
9335         }
9336         return w;
9337     }
9338     
9339 });
9340
9341  
9342 /*
9343  * - LGPL
9344  *
9345  * Input
9346  * 
9347  */
9348
9349 /**
9350  * @class Roo.bootstrap.TextArea
9351  * @extends Roo.bootstrap.Input
9352  * Bootstrap TextArea class
9353  * @cfg {Number} cols Specifies the visible width of a text area
9354  * @cfg {Number} rows Specifies the visible number of lines in a text area
9355  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9356  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9357  * @cfg {string} html text
9358  * 
9359  * @constructor
9360  * Create a new TextArea
9361  * @param {Object} config The config object
9362  */
9363
9364 Roo.bootstrap.TextArea = function(config){
9365     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9366    
9367 };
9368
9369 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9370      
9371     cols : false,
9372     rows : 5,
9373     readOnly : false,
9374     warp : 'soft',
9375     resize : false,
9376     value: false,
9377     html: false,
9378     
9379     getAutoCreate : function(){
9380         
9381         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9382         
9383         var id = Roo.id();
9384         
9385         var cfg = {};
9386         
9387         var input =  {
9388             tag: 'textarea',
9389             id : id,
9390             warp : this.warp,
9391             rows : this.rows,
9392             value : this.value || '',
9393             html: this.html || '',
9394             cls : 'form-control',
9395             placeholder : this.placeholder || '' 
9396             
9397         };
9398         
9399         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9400             input.maxLength = this.maxLength;
9401         }
9402         
9403         if(this.resize){
9404             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9405         }
9406         
9407         if(this.cols){
9408             input.cols = this.cols;
9409         }
9410         
9411         if (this.readOnly) {
9412             input.readonly = true;
9413         }
9414         
9415         if (this.name) {
9416             input.name = this.name;
9417         }
9418         
9419         if (this.size) {
9420             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9421         }
9422         
9423         var settings=this;
9424         ['xs','sm','md','lg'].map(function(size){
9425             if (settings[size]) {
9426                 cfg.cls += ' col-' + size + '-' + settings[size];
9427             }
9428         });
9429         
9430         var inputblock = input;
9431         
9432         if(this.hasFeedback && !this.allowBlank){
9433             
9434             var feedback = {
9435                 tag: 'span',
9436                 cls: 'glyphicon form-control-feedback'
9437             };
9438
9439             inputblock = {
9440                 cls : 'has-feedback',
9441                 cn :  [
9442                     input,
9443                     feedback
9444                 ] 
9445             };  
9446         }
9447         
9448         
9449         if (this.before || this.after) {
9450             
9451             inputblock = {
9452                 cls : 'input-group',
9453                 cn :  [] 
9454             };
9455             if (this.before) {
9456                 inputblock.cn.push({
9457                     tag :'span',
9458                     cls : 'input-group-addon',
9459                     html : this.before
9460                 });
9461             }
9462             
9463             inputblock.cn.push(input);
9464             
9465             if(this.hasFeedback && !this.allowBlank){
9466                 inputblock.cls += ' has-feedback';
9467                 inputblock.cn.push(feedback);
9468             }
9469             
9470             if (this.after) {
9471                 inputblock.cn.push({
9472                     tag :'span',
9473                     cls : 'input-group-addon',
9474                     html : this.after
9475                 });
9476             }
9477             
9478         }
9479         
9480         if (align ==='left' && this.fieldLabel.length) {
9481             cfg.cn = [
9482                 {
9483                     tag: 'label',
9484                     'for' :  id,
9485                     cls : 'control-label',
9486                     html : this.fieldLabel
9487                 },
9488                 {
9489                     cls : "",
9490                     cn: [
9491                         inputblock
9492                     ]
9493                 }
9494
9495             ];
9496             
9497             if(this.labelWidth > 12){
9498                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9499             }
9500
9501             if(this.labelWidth < 13 && this.labelmd == 0){
9502                 this.labelmd = this.labelWidth;
9503             }
9504
9505             if(this.labellg > 0){
9506                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9507                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9508             }
9509
9510             if(this.labelmd > 0){
9511                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9512                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9513             }
9514
9515             if(this.labelsm > 0){
9516                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9517                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9518             }
9519
9520             if(this.labelxs > 0){
9521                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9522                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9523             }
9524             
9525         } else if ( this.fieldLabel.length) {
9526             cfg.cn = [
9527
9528                {
9529                    tag: 'label',
9530                    //cls : 'input-group-addon',
9531                    html : this.fieldLabel
9532
9533                },
9534
9535                inputblock
9536
9537            ];
9538
9539         } else {
9540
9541             cfg.cn = [
9542
9543                 inputblock
9544
9545             ];
9546                 
9547         }
9548         
9549         if (this.disabled) {
9550             input.disabled=true;
9551         }
9552         
9553         return cfg;
9554         
9555     },
9556     /**
9557      * return the real textarea element.
9558      */
9559     inputEl: function ()
9560     {
9561         return this.el.select('textarea.form-control',true).first();
9562     },
9563     
9564     /**
9565      * Clear any invalid styles/messages for this field
9566      */
9567     clearInvalid : function()
9568     {
9569         
9570         if(!this.el || this.preventMark){ // not rendered
9571             return;
9572         }
9573         
9574         var label = this.el.select('label', true).first();
9575         var icon = this.el.select('i.fa-star', true).first();
9576         
9577         if(label && icon){
9578             icon.remove();
9579         }
9580         
9581         this.el.removeClass(this.invalidClass);
9582         
9583         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9584             
9585             var feedback = this.el.select('.form-control-feedback', true).first();
9586             
9587             if(feedback){
9588                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9589             }
9590             
9591         }
9592         
9593         this.fireEvent('valid', this);
9594     },
9595     
9596      /**
9597      * Mark this field as valid
9598      */
9599     markValid : function()
9600     {
9601         if(!this.el  || this.preventMark){ // not rendered
9602             return;
9603         }
9604         
9605         this.el.removeClass([this.invalidClass, this.validClass]);
9606         
9607         var feedback = this.el.select('.form-control-feedback', true).first();
9608             
9609         if(feedback){
9610             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9611         }
9612
9613         if(this.disabled || this.allowBlank){
9614             return;
9615         }
9616         
9617         var label = this.el.select('label', true).first();
9618         var icon = this.el.select('i.fa-star', true).first();
9619         
9620         if(label && icon){
9621             icon.remove();
9622         }
9623         
9624         this.el.addClass(this.validClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9632                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9633             }
9634             
9635         }
9636         
9637         this.fireEvent('valid', this);
9638     },
9639     
9640      /**
9641      * Mark this field as invalid
9642      * @param {String} msg The validation message
9643      */
9644     markInvalid : function(msg)
9645     {
9646         if(!this.el  || this.preventMark){ // not rendered
9647             return;
9648         }
9649         
9650         this.el.removeClass([this.invalidClass, this.validClass]);
9651         
9652         var feedback = this.el.select('.form-control-feedback', true).first();
9653             
9654         if(feedback){
9655             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9656         }
9657
9658         if(this.disabled || this.allowBlank){
9659             return;
9660         }
9661         
9662         var label = this.el.select('label', true).first();
9663         var icon = this.el.select('i.fa-star', true).first();
9664         
9665         if(!this.getValue().length && label && !icon){
9666             this.el.createChild({
9667                 tag : 'i',
9668                 cls : 'text-danger fa fa-lg fa-star',
9669                 tooltip : 'This field is required',
9670                 style : 'margin-right:5px;'
9671             }, label, true);
9672         }
9673
9674         this.el.addClass(this.invalidClass);
9675         
9676         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9677             
9678             var feedback = this.el.select('.form-control-feedback', true).first();
9679             
9680             if(feedback){
9681                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9682                 
9683                 if(this.getValue().length || this.forceFeedback){
9684                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9685                 }
9686                 
9687             }
9688             
9689         }
9690         
9691         this.fireEvent('invalid', this, msg);
9692     }
9693 });
9694
9695  
9696 /*
9697  * - LGPL
9698  *
9699  * trigger field - base class for combo..
9700  * 
9701  */
9702  
9703 /**
9704  * @class Roo.bootstrap.TriggerField
9705  * @extends Roo.bootstrap.Input
9706  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9707  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9708  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9709  * for which you can provide a custom implementation.  For example:
9710  * <pre><code>
9711 var trigger = new Roo.bootstrap.TriggerField();
9712 trigger.onTriggerClick = myTriggerFn;
9713 trigger.applyTo('my-field');
9714 </code></pre>
9715  *
9716  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9717  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9718  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9719  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9720  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9721
9722  * @constructor
9723  * Create a new TriggerField.
9724  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9725  * to the base TextField)
9726  */
9727 Roo.bootstrap.TriggerField = function(config){
9728     this.mimicing = false;
9729     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9730 };
9731
9732 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9733     /**
9734      * @cfg {String} triggerClass A CSS class to apply to the trigger
9735      */
9736      /**
9737      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9738      */
9739     hideTrigger:false,
9740
9741     /**
9742      * @cfg {Boolean} removable (true|false) special filter default false
9743      */
9744     removable : false,
9745     
9746     /** @cfg {Boolean} grow @hide */
9747     /** @cfg {Number} growMin @hide */
9748     /** @cfg {Number} growMax @hide */
9749
9750     /**
9751      * @hide 
9752      * @method
9753      */
9754     autoSize: Roo.emptyFn,
9755     // private
9756     monitorTab : true,
9757     // private
9758     deferHeight : true,
9759
9760     
9761     actionMode : 'wrap',
9762     
9763     caret : false,
9764     
9765     
9766     getAutoCreate : function(){
9767        
9768         var align = this.labelAlign || this.parentLabelAlign();
9769         
9770         var id = Roo.id();
9771         
9772         var cfg = {
9773             cls: 'form-group' //input-group
9774         };
9775         
9776         
9777         var input =  {
9778             tag: 'input',
9779             id : id,
9780             type : this.inputType,
9781             cls : 'form-control',
9782             autocomplete: 'new-password',
9783             placeholder : this.placeholder || '' 
9784             
9785         };
9786         if (this.name) {
9787             input.name = this.name;
9788         }
9789         if (this.size) {
9790             input.cls += ' input-' + this.size;
9791         }
9792         
9793         if (this.disabled) {
9794             input.disabled=true;
9795         }
9796         
9797         var inputblock = input;
9798         
9799         if(this.hasFeedback && !this.allowBlank){
9800             
9801             var feedback = {
9802                 tag: 'span',
9803                 cls: 'glyphicon form-control-feedback'
9804             };
9805             
9806             if(this.removable && !this.editable && !this.tickable){
9807                 inputblock = {
9808                     cls : 'has-feedback',
9809                     cn :  [
9810                         inputblock,
9811                         {
9812                             tag: 'button',
9813                             html : 'x',
9814                             cls : 'roo-combo-removable-btn close'
9815                         },
9816                         feedback
9817                     ] 
9818                 };
9819             } else {
9820                 inputblock = {
9821                     cls : 'has-feedback',
9822                     cn :  [
9823                         inputblock,
9824                         feedback
9825                     ] 
9826                 };
9827             }
9828
9829         } else {
9830             if(this.removable && !this.editable && !this.tickable){
9831                 inputblock = {
9832                     cls : 'roo-removable',
9833                     cn :  [
9834                         inputblock,
9835                         {
9836                             tag: 'button',
9837                             html : 'x',
9838                             cls : 'roo-combo-removable-btn close'
9839                         }
9840                     ] 
9841                 };
9842             }
9843         }
9844         
9845         if (this.before || this.after) {
9846             
9847             inputblock = {
9848                 cls : 'input-group',
9849                 cn :  [] 
9850             };
9851             if (this.before) {
9852                 inputblock.cn.push({
9853                     tag :'span',
9854                     cls : 'input-group-addon',
9855                     html : this.before
9856                 });
9857             }
9858             
9859             inputblock.cn.push(input);
9860             
9861             if(this.hasFeedback && !this.allowBlank){
9862                 inputblock.cls += ' has-feedback';
9863                 inputblock.cn.push(feedback);
9864             }
9865             
9866             if (this.after) {
9867                 inputblock.cn.push({
9868                     tag :'span',
9869                     cls : 'input-group-addon',
9870                     html : this.after
9871                 });
9872             }
9873             
9874         };
9875         
9876         var box = {
9877             tag: 'div',
9878             cn: [
9879                 {
9880                     tag: 'input',
9881                     type : 'hidden',
9882                     cls: 'form-hidden-field'
9883                 },
9884                 inputblock
9885             ]
9886             
9887         };
9888         
9889         if(this.multiple){
9890             box = {
9891                 tag: 'div',
9892                 cn: [
9893                     {
9894                         tag: 'input',
9895                         type : 'hidden',
9896                         cls: 'form-hidden-field'
9897                     },
9898                     {
9899                         tag: 'ul',
9900                         cls: 'roo-select2-choices',
9901                         cn:[
9902                             {
9903                                 tag: 'li',
9904                                 cls: 'roo-select2-search-field',
9905                                 cn: [
9906
9907                                     inputblock
9908                                 ]
9909                             }
9910                         ]
9911                     }
9912                 ]
9913             }
9914         };
9915         
9916         var combobox = {
9917             cls: 'roo-select2-container input-group',
9918             cn: [
9919                 box
9920 //                {
9921 //                    tag: 'ul',
9922 //                    cls: 'typeahead typeahead-long dropdown-menu',
9923 //                    style: 'display:none'
9924 //                }
9925             ]
9926         };
9927         
9928         if(!this.multiple && this.showToggleBtn){
9929             
9930             var caret = {
9931                         tag: 'span',
9932                         cls: 'caret'
9933              };
9934             if (this.caret != false) {
9935                 caret = {
9936                      tag: 'i',
9937                      cls: 'fa fa-' + this.caret
9938                 };
9939                 
9940             }
9941             
9942             combobox.cn.push({
9943                 tag :'span',
9944                 cls : 'input-group-addon btn dropdown-toggle',
9945                 cn : [
9946                     caret,
9947                     {
9948                         tag: 'span',
9949                         cls: 'combobox-clear',
9950                         cn  : [
9951                             {
9952                                 tag : 'i',
9953                                 cls: 'icon-remove'
9954                             }
9955                         ]
9956                     }
9957                 ]
9958
9959             })
9960         }
9961         
9962         if(this.multiple){
9963             combobox.cls += ' roo-select2-container-multi';
9964         }
9965         
9966         if (align ==='left' && this.fieldLabel.length) {
9967             
9968             cfg.cls += ' roo-form-group-label-left';
9969
9970             cfg.cn = [
9971                 {
9972                     tag : 'i',
9973                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9974                     tooltip : 'This field is required'
9975                 },
9976                 {
9977                     tag: 'label',
9978                     'for' :  id,
9979                     cls : 'control-label',
9980                     html : this.fieldLabel
9981
9982                 },
9983                 {
9984                     cls : "", 
9985                     cn: [
9986                         combobox
9987                     ]
9988                 }
9989
9990             ];
9991             
9992             var labelCfg = cfg.cn[1];
9993             var contentCfg = cfg.cn[2];
9994             
9995             if(this.indicatorpos == 'right'){
9996                 cfg.cn = [
9997                     {
9998                         tag: 'label',
9999                         'for' :  id,
10000                         cls : 'control-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                         cls : "", 
10015                         cn: [
10016                             combobox
10017                         ]
10018                     }
10019
10020                 ];
10021                 
10022                 labelCfg = cfg.cn[0];
10023                 contentCfg = cfg.cn[1];
10024             }
10025             
10026             if(this.labelWidth > 12){
10027                 labelCfg.style = "width: " + this.labelWidth + 'px';
10028             }
10029             
10030             if(this.labelWidth < 13 && this.labelmd == 0){
10031                 this.labelmd = this.labelWidth;
10032             }
10033             
10034             if(this.labellg > 0){
10035                 labelCfg.cls += ' col-lg-' + this.labellg;
10036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10037             }
10038             
10039             if(this.labelmd > 0){
10040                 labelCfg.cls += ' col-md-' + this.labelmd;
10041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10042             }
10043             
10044             if(this.labelsm > 0){
10045                 labelCfg.cls += ' col-sm-' + this.labelsm;
10046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10047             }
10048             
10049             if(this.labelxs > 0){
10050                 labelCfg.cls += ' col-xs-' + this.labelxs;
10051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10052             }
10053             
10054         } else if ( this.fieldLabel.length) {
10055 //                Roo.log(" label");
10056             cfg.cn = [
10057                 {
10058                    tag : 'i',
10059                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10060                    tooltip : 'This field is required'
10061                },
10062                {
10063                    tag: 'label',
10064                    //cls : 'input-group-addon',
10065                    html : this.fieldLabel
10066
10067                },
10068
10069                combobox
10070
10071             ];
10072             
10073             if(this.indicatorpos == 'right'){
10074                 
10075                 cfg.cn = [
10076                     {
10077                        tag: 'label',
10078                        cn : [
10079                            {
10080                                tag : 'span',
10081                                html : this.fieldLabel
10082                            },
10083                            {
10084                               tag : 'i',
10085                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10086                               tooltip : 'This field is required'
10087                            }
10088                        ]
10089
10090                     },
10091                     combobox
10092
10093                 ];
10094
10095             }
10096
10097         } else {
10098             
10099 //                Roo.log(" no label && no align");
10100                 cfg = combobox
10101                      
10102                 
10103         }
10104         
10105         var settings=this;
10106         ['xs','sm','md','lg'].map(function(size){
10107             if (settings[size]) {
10108                 cfg.cls += ' col-' + size + '-' + settings[size];
10109             }
10110         });
10111         
10112         return cfg;
10113         
10114     },
10115     
10116     
10117     
10118     // private
10119     onResize : function(w, h){
10120 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10121 //        if(typeof w == 'number'){
10122 //            var x = w - this.trigger.getWidth();
10123 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10124 //            this.trigger.setStyle('left', x+'px');
10125 //        }
10126     },
10127
10128     // private
10129     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10130
10131     // private
10132     getResizeEl : function(){
10133         return this.inputEl();
10134     },
10135
10136     // private
10137     getPositionEl : function(){
10138         return this.inputEl();
10139     },
10140
10141     // private
10142     alignErrorIcon : function(){
10143         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10144     },
10145
10146     // private
10147     initEvents : function(){
10148         
10149         this.createList();
10150         
10151         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10152         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10153         if(!this.multiple && this.showToggleBtn){
10154             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10155             if(this.hideTrigger){
10156                 this.trigger.setDisplayed(false);
10157             }
10158             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10159         }
10160         
10161         if(this.multiple){
10162             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10163         }
10164         
10165         if(this.removable && !this.editable && !this.tickable){
10166             var close = this.closeTriggerEl();
10167             
10168             if(close){
10169                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10170                 close.on('click', this.removeBtnClick, this, close);
10171             }
10172         }
10173         
10174         //this.trigger.addClassOnOver('x-form-trigger-over');
10175         //this.trigger.addClassOnClick('x-form-trigger-click');
10176         
10177         //if(!this.width){
10178         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10179         //}
10180     },
10181     
10182     closeTriggerEl : function()
10183     {
10184         var close = this.el.select('.roo-combo-removable-btn', true).first();
10185         return close ? close : false;
10186     },
10187     
10188     removeBtnClick : function(e, h, el)
10189     {
10190         e.preventDefault();
10191         
10192         if(this.fireEvent("remove", this) !== false){
10193             this.reset();
10194             this.fireEvent("afterremove", this)
10195         }
10196     },
10197     
10198     createList : function()
10199     {
10200         this.list = Roo.get(document.body).createChild({
10201             tag: 'ul',
10202             cls: 'typeahead typeahead-long dropdown-menu',
10203             style: 'display:none'
10204         });
10205         
10206         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10207         
10208     },
10209
10210     // private
10211     initTrigger : function(){
10212        
10213     },
10214
10215     // private
10216     onDestroy : function(){
10217         if(this.trigger){
10218             this.trigger.removeAllListeners();
10219           //  this.trigger.remove();
10220         }
10221         //if(this.wrap){
10222         //    this.wrap.remove();
10223         //}
10224         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10225     },
10226
10227     // private
10228     onFocus : function(){
10229         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10230         /*
10231         if(!this.mimicing){
10232             this.wrap.addClass('x-trigger-wrap-focus');
10233             this.mimicing = true;
10234             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10235             if(this.monitorTab){
10236                 this.el.on("keydown", this.checkTab, this);
10237             }
10238         }
10239         */
10240     },
10241
10242     // private
10243     checkTab : function(e){
10244         if(e.getKey() == e.TAB){
10245             this.triggerBlur();
10246         }
10247     },
10248
10249     // private
10250     onBlur : function(){
10251         // do nothing
10252     },
10253
10254     // private
10255     mimicBlur : function(e, t){
10256         /*
10257         if(!this.wrap.contains(t) && this.validateBlur()){
10258             this.triggerBlur();
10259         }
10260         */
10261     },
10262
10263     // private
10264     triggerBlur : function(){
10265         this.mimicing = false;
10266         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10267         if(this.monitorTab){
10268             this.el.un("keydown", this.checkTab, this);
10269         }
10270         //this.wrap.removeClass('x-trigger-wrap-focus');
10271         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10272     },
10273
10274     // private
10275     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10276     validateBlur : function(e, t){
10277         return true;
10278     },
10279
10280     // private
10281     onDisable : function(){
10282         this.inputEl().dom.disabled = true;
10283         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10284         //if(this.wrap){
10285         //    this.wrap.addClass('x-item-disabled');
10286         //}
10287     },
10288
10289     // private
10290     onEnable : function(){
10291         this.inputEl().dom.disabled = false;
10292         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10293         //if(this.wrap){
10294         //    this.el.removeClass('x-item-disabled');
10295         //}
10296     },
10297
10298     // private
10299     onShow : function(){
10300         var ae = this.getActionEl();
10301         
10302         if(ae){
10303             ae.dom.style.display = '';
10304             ae.dom.style.visibility = 'visible';
10305         }
10306     },
10307
10308     // private
10309     
10310     onHide : function(){
10311         var ae = this.getActionEl();
10312         ae.dom.style.display = 'none';
10313     },
10314
10315     /**
10316      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10317      * by an implementing function.
10318      * @method
10319      * @param {EventObject} e
10320      */
10321     onTriggerClick : Roo.emptyFn
10322 });
10323  /*
10324  * Based on:
10325  * Ext JS Library 1.1.1
10326  * Copyright(c) 2006-2007, Ext JS, LLC.
10327  *
10328  * Originally Released Under LGPL - original licence link has changed is not relivant.
10329  *
10330  * Fork - LGPL
10331  * <script type="text/javascript">
10332  */
10333
10334
10335 /**
10336  * @class Roo.data.SortTypes
10337  * @singleton
10338  * Defines the default sorting (casting?) comparison functions used when sorting data.
10339  */
10340 Roo.data.SortTypes = {
10341     /**
10342      * Default sort that does nothing
10343      * @param {Mixed} s The value being converted
10344      * @return {Mixed} The comparison value
10345      */
10346     none : function(s){
10347         return s;
10348     },
10349     
10350     /**
10351      * The regular expression used to strip tags
10352      * @type {RegExp}
10353      * @property
10354      */
10355     stripTagsRE : /<\/?[^>]+>/gi,
10356     
10357     /**
10358      * Strips all HTML tags to sort on text only
10359      * @param {Mixed} s The value being converted
10360      * @return {String} The comparison value
10361      */
10362     asText : function(s){
10363         return String(s).replace(this.stripTagsRE, "");
10364     },
10365     
10366     /**
10367      * Strips all HTML tags to sort on text only - Case insensitive
10368      * @param {Mixed} s The value being converted
10369      * @return {String} The comparison value
10370      */
10371     asUCText : function(s){
10372         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10373     },
10374     
10375     /**
10376      * Case insensitive string
10377      * @param {Mixed} s The value being converted
10378      * @return {String} The comparison value
10379      */
10380     asUCString : function(s) {
10381         return String(s).toUpperCase();
10382     },
10383     
10384     /**
10385      * Date sorting
10386      * @param {Mixed} s The value being converted
10387      * @return {Number} The comparison value
10388      */
10389     asDate : function(s) {
10390         if(!s){
10391             return 0;
10392         }
10393         if(s instanceof Date){
10394             return s.getTime();
10395         }
10396         return Date.parse(String(s));
10397     },
10398     
10399     /**
10400      * Float sorting
10401      * @param {Mixed} s The value being converted
10402      * @return {Float} The comparison value
10403      */
10404     asFloat : function(s) {
10405         var val = parseFloat(String(s).replace(/,/g, ""));
10406         if(isNaN(val)) {
10407             val = 0;
10408         }
10409         return val;
10410     },
10411     
10412     /**
10413      * Integer sorting
10414      * @param {Mixed} s The value being converted
10415      * @return {Number} The comparison value
10416      */
10417     asInt : function(s) {
10418         var val = parseInt(String(s).replace(/,/g, ""));
10419         if(isNaN(val)) {
10420             val = 0;
10421         }
10422         return val;
10423     }
10424 };/*
10425  * Based on:
10426  * Ext JS Library 1.1.1
10427  * Copyright(c) 2006-2007, Ext JS, LLC.
10428  *
10429  * Originally Released Under LGPL - original licence link has changed is not relivant.
10430  *
10431  * Fork - LGPL
10432  * <script type="text/javascript">
10433  */
10434
10435 /**
10436 * @class Roo.data.Record
10437  * Instances of this class encapsulate both record <em>definition</em> information, and record
10438  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10439  * to access Records cached in an {@link Roo.data.Store} object.<br>
10440  * <p>
10441  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10442  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10443  * objects.<br>
10444  * <p>
10445  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10446  * @constructor
10447  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10448  * {@link #create}. The parameters are the same.
10449  * @param {Array} data An associative Array of data values keyed by the field name.
10450  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10451  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10452  * not specified an integer id is generated.
10453  */
10454 Roo.data.Record = function(data, id){
10455     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10456     this.data = data;
10457 };
10458
10459 /**
10460  * Generate a constructor for a specific record layout.
10461  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10462  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10463  * Each field definition object may contain the following properties: <ul>
10464  * <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,
10465  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10466  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10467  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10468  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10469  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10470  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10471  * this may be omitted.</p></li>
10472  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10473  * <ul><li>auto (Default, implies no conversion)</li>
10474  * <li>string</li>
10475  * <li>int</li>
10476  * <li>float</li>
10477  * <li>boolean</li>
10478  * <li>date</li></ul></p></li>
10479  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10480  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10481  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10482  * by the Reader into an object that will be stored in the Record. It is passed the
10483  * following parameters:<ul>
10484  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10485  * </ul></p></li>
10486  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10487  * </ul>
10488  * <br>usage:<br><pre><code>
10489 var TopicRecord = Roo.data.Record.create(
10490     {name: 'title', mapping: 'topic_title'},
10491     {name: 'author', mapping: 'username'},
10492     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10493     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10494     {name: 'lastPoster', mapping: 'user2'},
10495     {name: 'excerpt', mapping: 'post_text'}
10496 );
10497
10498 var myNewRecord = new TopicRecord({
10499     title: 'Do my job please',
10500     author: 'noobie',
10501     totalPosts: 1,
10502     lastPost: new Date(),
10503     lastPoster: 'Animal',
10504     excerpt: 'No way dude!'
10505 });
10506 myStore.add(myNewRecord);
10507 </code></pre>
10508  * @method create
10509  * @static
10510  */
10511 Roo.data.Record.create = function(o){
10512     var f = function(){
10513         f.superclass.constructor.apply(this, arguments);
10514     };
10515     Roo.extend(f, Roo.data.Record);
10516     var p = f.prototype;
10517     p.fields = new Roo.util.MixedCollection(false, function(field){
10518         return field.name;
10519     });
10520     for(var i = 0, len = o.length; i < len; i++){
10521         p.fields.add(new Roo.data.Field(o[i]));
10522     }
10523     f.getField = function(name){
10524         return p.fields.get(name);  
10525     };
10526     return f;
10527 };
10528
10529 Roo.data.Record.AUTO_ID = 1000;
10530 Roo.data.Record.EDIT = 'edit';
10531 Roo.data.Record.REJECT = 'reject';
10532 Roo.data.Record.COMMIT = 'commit';
10533
10534 Roo.data.Record.prototype = {
10535     /**
10536      * Readonly flag - true if this record has been modified.
10537      * @type Boolean
10538      */
10539     dirty : false,
10540     editing : false,
10541     error: null,
10542     modified: null,
10543
10544     // private
10545     join : function(store){
10546         this.store = store;
10547     },
10548
10549     /**
10550      * Set the named field to the specified value.
10551      * @param {String} name The name of the field to set.
10552      * @param {Object} value The value to set the field to.
10553      */
10554     set : function(name, value){
10555         if(this.data[name] == value){
10556             return;
10557         }
10558         this.dirty = true;
10559         if(!this.modified){
10560             this.modified = {};
10561         }
10562         if(typeof this.modified[name] == 'undefined'){
10563             this.modified[name] = this.data[name];
10564         }
10565         this.data[name] = value;
10566         if(!this.editing && this.store){
10567             this.store.afterEdit(this);
10568         }       
10569     },
10570
10571     /**
10572      * Get the value of the named field.
10573      * @param {String} name The name of the field to get the value of.
10574      * @return {Object} The value of the field.
10575      */
10576     get : function(name){
10577         return this.data[name]; 
10578     },
10579
10580     // private
10581     beginEdit : function(){
10582         this.editing = true;
10583         this.modified = {}; 
10584     },
10585
10586     // private
10587     cancelEdit : function(){
10588         this.editing = false;
10589         delete this.modified;
10590     },
10591
10592     // private
10593     endEdit : function(){
10594         this.editing = false;
10595         if(this.dirty && this.store){
10596             this.store.afterEdit(this);
10597         }
10598     },
10599
10600     /**
10601      * Usually called by the {@link Roo.data.Store} which owns the Record.
10602      * Rejects all changes made to the Record since either creation, or the last commit operation.
10603      * Modified fields are reverted to their original values.
10604      * <p>
10605      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10606      * of reject operations.
10607      */
10608     reject : function(){
10609         var m = this.modified;
10610         for(var n in m){
10611             if(typeof m[n] != "function"){
10612                 this.data[n] = m[n];
10613             }
10614         }
10615         this.dirty = false;
10616         delete this.modified;
10617         this.editing = false;
10618         if(this.store){
10619             this.store.afterReject(this);
10620         }
10621     },
10622
10623     /**
10624      * Usually called by the {@link Roo.data.Store} which owns the Record.
10625      * Commits all changes made to the Record since either creation, or the last commit operation.
10626      * <p>
10627      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10628      * of commit operations.
10629      */
10630     commit : function(){
10631         this.dirty = false;
10632         delete this.modified;
10633         this.editing = false;
10634         if(this.store){
10635             this.store.afterCommit(this);
10636         }
10637     },
10638
10639     // private
10640     hasError : function(){
10641         return this.error != null;
10642     },
10643
10644     // private
10645     clearError : function(){
10646         this.error = null;
10647     },
10648
10649     /**
10650      * Creates a copy of this record.
10651      * @param {String} id (optional) A new record id if you don't want to use this record's id
10652      * @return {Record}
10653      */
10654     copy : function(newId) {
10655         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10656     }
10657 };/*
10658  * Based on:
10659  * Ext JS Library 1.1.1
10660  * Copyright(c) 2006-2007, Ext JS, LLC.
10661  *
10662  * Originally Released Under LGPL - original licence link has changed is not relivant.
10663  *
10664  * Fork - LGPL
10665  * <script type="text/javascript">
10666  */
10667
10668
10669
10670 /**
10671  * @class Roo.data.Store
10672  * @extends Roo.util.Observable
10673  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10674  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10675  * <p>
10676  * 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
10677  * has no knowledge of the format of the data returned by the Proxy.<br>
10678  * <p>
10679  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10680  * instances from the data object. These records are cached and made available through accessor functions.
10681  * @constructor
10682  * Creates a new Store.
10683  * @param {Object} config A config object containing the objects needed for the Store to access data,
10684  * and read the data into Records.
10685  */
10686 Roo.data.Store = function(config){
10687     this.data = new Roo.util.MixedCollection(false);
10688     this.data.getKey = function(o){
10689         return o.id;
10690     };
10691     this.baseParams = {};
10692     // private
10693     this.paramNames = {
10694         "start" : "start",
10695         "limit" : "limit",
10696         "sort" : "sort",
10697         "dir" : "dir",
10698         "multisort" : "_multisort"
10699     };
10700
10701     if(config && config.data){
10702         this.inlineData = config.data;
10703         delete config.data;
10704     }
10705
10706     Roo.apply(this, config);
10707     
10708     if(this.reader){ // reader passed
10709         this.reader = Roo.factory(this.reader, Roo.data);
10710         this.reader.xmodule = this.xmodule || false;
10711         if(!this.recordType){
10712             this.recordType = this.reader.recordType;
10713         }
10714         if(this.reader.onMetaChange){
10715             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10716         }
10717     }
10718
10719     if(this.recordType){
10720         this.fields = this.recordType.prototype.fields;
10721     }
10722     this.modified = [];
10723
10724     this.addEvents({
10725         /**
10726          * @event datachanged
10727          * Fires when the data cache has changed, and a widget which is using this Store
10728          * as a Record cache should refresh its view.
10729          * @param {Store} this
10730          */
10731         datachanged : true,
10732         /**
10733          * @event metachange
10734          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10735          * @param {Store} this
10736          * @param {Object} meta The JSON metadata
10737          */
10738         metachange : true,
10739         /**
10740          * @event add
10741          * Fires when Records have been added to the Store
10742          * @param {Store} this
10743          * @param {Roo.data.Record[]} records The array of Records added
10744          * @param {Number} index The index at which the record(s) were added
10745          */
10746         add : true,
10747         /**
10748          * @event remove
10749          * Fires when a Record has been removed from the Store
10750          * @param {Store} this
10751          * @param {Roo.data.Record} record The Record that was removed
10752          * @param {Number} index The index at which the record was removed
10753          */
10754         remove : true,
10755         /**
10756          * @event update
10757          * Fires when a Record has been updated
10758          * @param {Store} this
10759          * @param {Roo.data.Record} record The Record that was updated
10760          * @param {String} operation The update operation being performed.  Value may be one of:
10761          * <pre><code>
10762  Roo.data.Record.EDIT
10763  Roo.data.Record.REJECT
10764  Roo.data.Record.COMMIT
10765          * </code></pre>
10766          */
10767         update : true,
10768         /**
10769          * @event clear
10770          * Fires when the data cache has been cleared.
10771          * @param {Store} this
10772          */
10773         clear : true,
10774         /**
10775          * @event beforeload
10776          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10777          * the load action will be canceled.
10778          * @param {Store} this
10779          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10780          */
10781         beforeload : true,
10782         /**
10783          * @event beforeloadadd
10784          * Fires after a new set of Records has been loaded.
10785          * @param {Store} this
10786          * @param {Roo.data.Record[]} records The Records that were loaded
10787          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10788          */
10789         beforeloadadd : true,
10790         /**
10791          * @event load
10792          * Fires after a new set of Records has been loaded, before they are added to the store.
10793          * @param {Store} this
10794          * @param {Roo.data.Record[]} records The Records that were loaded
10795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10796          * @params {Object} return from reader
10797          */
10798         load : true,
10799         /**
10800          * @event loadexception
10801          * Fires if an exception occurs in the Proxy during loading.
10802          * Called with the signature of the Proxy's "loadexception" event.
10803          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10804          * 
10805          * @param {Proxy} 
10806          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10807          * @param {Object} load options 
10808          * @param {Object} jsonData from your request (normally this contains the Exception)
10809          */
10810         loadexception : true
10811     });
10812     
10813     if(this.proxy){
10814         this.proxy = Roo.factory(this.proxy, Roo.data);
10815         this.proxy.xmodule = this.xmodule || false;
10816         this.relayEvents(this.proxy,  ["loadexception"]);
10817     }
10818     this.sortToggle = {};
10819     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10820
10821     Roo.data.Store.superclass.constructor.call(this);
10822
10823     if(this.inlineData){
10824         this.loadData(this.inlineData);
10825         delete this.inlineData;
10826     }
10827 };
10828
10829 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10830      /**
10831     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10832     * without a remote query - used by combo/forms at present.
10833     */
10834     
10835     /**
10836     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10837     */
10838     /**
10839     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10840     */
10841     /**
10842     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10843     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10844     */
10845     /**
10846     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10847     * on any HTTP request
10848     */
10849     /**
10850     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10851     */
10852     /**
10853     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10854     */
10855     multiSort: false,
10856     /**
10857     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10858     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10859     */
10860     remoteSort : false,
10861
10862     /**
10863     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10864      * loaded or when a record is removed. (defaults to false).
10865     */
10866     pruneModifiedRecords : false,
10867
10868     // private
10869     lastOptions : null,
10870
10871     /**
10872      * Add Records to the Store and fires the add event.
10873      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10874      */
10875     add : function(records){
10876         records = [].concat(records);
10877         for(var i = 0, len = records.length; i < len; i++){
10878             records[i].join(this);
10879         }
10880         var index = this.data.length;
10881         this.data.addAll(records);
10882         this.fireEvent("add", this, records, index);
10883     },
10884
10885     /**
10886      * Remove a Record from the Store and fires the remove event.
10887      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10888      */
10889     remove : function(record){
10890         var index = this.data.indexOf(record);
10891         this.data.removeAt(index);
10892         if(this.pruneModifiedRecords){
10893             this.modified.remove(record);
10894         }
10895         this.fireEvent("remove", this, record, index);
10896     },
10897
10898     /**
10899      * Remove all Records from the Store and fires the clear event.
10900      */
10901     removeAll : function(){
10902         this.data.clear();
10903         if(this.pruneModifiedRecords){
10904             this.modified = [];
10905         }
10906         this.fireEvent("clear", this);
10907     },
10908
10909     /**
10910      * Inserts Records to the Store at the given index and fires the add event.
10911      * @param {Number} index The start index at which to insert the passed Records.
10912      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10913      */
10914     insert : function(index, records){
10915         records = [].concat(records);
10916         for(var i = 0, len = records.length; i < len; i++){
10917             this.data.insert(index, records[i]);
10918             records[i].join(this);
10919         }
10920         this.fireEvent("add", this, records, index);
10921     },
10922
10923     /**
10924      * Get the index within the cache of the passed Record.
10925      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10926      * @return {Number} The index of the passed Record. Returns -1 if not found.
10927      */
10928     indexOf : function(record){
10929         return this.data.indexOf(record);
10930     },
10931
10932     /**
10933      * Get the index within the cache of the Record with the passed id.
10934      * @param {String} id The id of the Record to find.
10935      * @return {Number} The index of the Record. Returns -1 if not found.
10936      */
10937     indexOfId : function(id){
10938         return this.data.indexOfKey(id);
10939     },
10940
10941     /**
10942      * Get the Record with the specified id.
10943      * @param {String} id The id of the Record to find.
10944      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10945      */
10946     getById : function(id){
10947         return this.data.key(id);
10948     },
10949
10950     /**
10951      * Get the Record at the specified index.
10952      * @param {Number} index The index of the Record to find.
10953      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10954      */
10955     getAt : function(index){
10956         return this.data.itemAt(index);
10957     },
10958
10959     /**
10960      * Returns a range of Records between specified indices.
10961      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10962      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10963      * @return {Roo.data.Record[]} An array of Records
10964      */
10965     getRange : function(start, end){
10966         return this.data.getRange(start, end);
10967     },
10968
10969     // private
10970     storeOptions : function(o){
10971         o = Roo.apply({}, o);
10972         delete o.callback;
10973         delete o.scope;
10974         this.lastOptions = o;
10975     },
10976
10977     /**
10978      * Loads the Record cache from the configured Proxy using the configured Reader.
10979      * <p>
10980      * If using remote paging, then the first load call must specify the <em>start</em>
10981      * and <em>limit</em> properties in the options.params property to establish the initial
10982      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10983      * <p>
10984      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10985      * and this call will return before the new data has been loaded. Perform any post-processing
10986      * in a callback function, or in a "load" event handler.</strong>
10987      * <p>
10988      * @param {Object} options An object containing properties which control loading options:<ul>
10989      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10990      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10991      * passed the following arguments:<ul>
10992      * <li>r : Roo.data.Record[]</li>
10993      * <li>options: Options object from the load call</li>
10994      * <li>success: Boolean success indicator</li></ul></li>
10995      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10996      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10997      * </ul>
10998      */
10999     load : function(options){
11000         options = options || {};
11001         if(this.fireEvent("beforeload", this, options) !== false){
11002             this.storeOptions(options);
11003             var p = Roo.apply(options.params || {}, this.baseParams);
11004             // if meta was not loaded from remote source.. try requesting it.
11005             if (!this.reader.metaFromRemote) {
11006                 p._requestMeta = 1;
11007             }
11008             if(this.sortInfo && this.remoteSort){
11009                 var pn = this.paramNames;
11010                 p[pn["sort"]] = this.sortInfo.field;
11011                 p[pn["dir"]] = this.sortInfo.direction;
11012             }
11013             if (this.multiSort) {
11014                 var pn = this.paramNames;
11015                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11016             }
11017             
11018             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11019         }
11020     },
11021
11022     /**
11023      * Reloads the Record cache from the configured Proxy using the configured Reader and
11024      * the options from the last load operation performed.
11025      * @param {Object} options (optional) An object containing properties which may override the options
11026      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11027      * the most recently used options are reused).
11028      */
11029     reload : function(options){
11030         this.load(Roo.applyIf(options||{}, this.lastOptions));
11031     },
11032
11033     // private
11034     // Called as a callback by the Reader during a load operation.
11035     loadRecords : function(o, options, success){
11036         if(!o || success === false){
11037             if(success !== false){
11038                 this.fireEvent("load", this, [], options, o);
11039             }
11040             if(options.callback){
11041                 options.callback.call(options.scope || this, [], options, false);
11042             }
11043             return;
11044         }
11045         // if data returned failure - throw an exception.
11046         if (o.success === false) {
11047             // show a message if no listener is registered.
11048             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11049                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11050             }
11051             // loadmask wil be hooked into this..
11052             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11053             return;
11054         }
11055         var r = o.records, t = o.totalRecords || r.length;
11056         
11057         this.fireEvent("beforeloadadd", this, r, options, o);
11058         
11059         if(!options || options.add !== true){
11060             if(this.pruneModifiedRecords){
11061                 this.modified = [];
11062             }
11063             for(var i = 0, len = r.length; i < len; i++){
11064                 r[i].join(this);
11065             }
11066             if(this.snapshot){
11067                 this.data = this.snapshot;
11068                 delete this.snapshot;
11069             }
11070             this.data.clear();
11071             this.data.addAll(r);
11072             this.totalLength = t;
11073             this.applySort();
11074             this.fireEvent("datachanged", this);
11075         }else{
11076             this.totalLength = Math.max(t, this.data.length+r.length);
11077             this.add(r);
11078         }
11079         
11080         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11081                 
11082             var e = new Roo.data.Record({});
11083
11084             e.set(this.parent.displayField, this.parent.emptyTitle);
11085             e.set(this.parent.valueField, '');
11086
11087             this.insert(0, e);
11088         }
11089             
11090         this.fireEvent("load", this, r, options, o);
11091         if(options.callback){
11092             options.callback.call(options.scope || this, r, options, true);
11093         }
11094     },
11095
11096
11097     /**
11098      * Loads data from a passed data block. A Reader which understands the format of the data
11099      * must have been configured in the constructor.
11100      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11101      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11102      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11103      */
11104     loadData : function(o, append){
11105         var r = this.reader.readRecords(o);
11106         this.loadRecords(r, {add: append}, true);
11107     },
11108
11109     /**
11110      * Gets the number of cached records.
11111      * <p>
11112      * <em>If using paging, this may not be the total size of the dataset. If the data object
11113      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11114      * the data set size</em>
11115      */
11116     getCount : function(){
11117         return this.data.length || 0;
11118     },
11119
11120     /**
11121      * Gets the total number of records in the dataset as returned by the server.
11122      * <p>
11123      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11124      * the dataset size</em>
11125      */
11126     getTotalCount : function(){
11127         return this.totalLength || 0;
11128     },
11129
11130     /**
11131      * Returns the sort state of the Store as an object with two properties:
11132      * <pre><code>
11133  field {String} The name of the field by which the Records are sorted
11134  direction {String} The sort order, "ASC" or "DESC"
11135      * </code></pre>
11136      */
11137     getSortState : function(){
11138         return this.sortInfo;
11139     },
11140
11141     // private
11142     applySort : function(){
11143         if(this.sortInfo && !this.remoteSort){
11144             var s = this.sortInfo, f = s.field;
11145             var st = this.fields.get(f).sortType;
11146             var fn = function(r1, r2){
11147                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11148                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11149             };
11150             this.data.sort(s.direction, fn);
11151             if(this.snapshot && this.snapshot != this.data){
11152                 this.snapshot.sort(s.direction, fn);
11153             }
11154         }
11155     },
11156
11157     /**
11158      * Sets the default sort column and order to be used by the next load operation.
11159      * @param {String} fieldName The name of the field to sort by.
11160      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11161      */
11162     setDefaultSort : function(field, dir){
11163         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11164     },
11165
11166     /**
11167      * Sort the Records.
11168      * If remote sorting is used, the sort is performed on the server, and the cache is
11169      * reloaded. If local sorting is used, the cache is sorted internally.
11170      * @param {String} fieldName The name of the field to sort by.
11171      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11172      */
11173     sort : function(fieldName, dir){
11174         var f = this.fields.get(fieldName);
11175         if(!dir){
11176             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11177             
11178             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11179                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11180             }else{
11181                 dir = f.sortDir;
11182             }
11183         }
11184         this.sortToggle[f.name] = dir;
11185         this.sortInfo = {field: f.name, direction: dir};
11186         if(!this.remoteSort){
11187             this.applySort();
11188             this.fireEvent("datachanged", this);
11189         }else{
11190             this.load(this.lastOptions);
11191         }
11192     },
11193
11194     /**
11195      * Calls the specified function for each of the Records in the cache.
11196      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11197      * Returning <em>false</em> aborts and exits the iteration.
11198      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11199      */
11200     each : function(fn, scope){
11201         this.data.each(fn, scope);
11202     },
11203
11204     /**
11205      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11206      * (e.g., during paging).
11207      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11208      */
11209     getModifiedRecords : function(){
11210         return this.modified;
11211     },
11212
11213     // private
11214     createFilterFn : function(property, value, anyMatch){
11215         if(!value.exec){ // not a regex
11216             value = String(value);
11217             if(value.length == 0){
11218                 return false;
11219             }
11220             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11221         }
11222         return function(r){
11223             return value.test(r.data[property]);
11224         };
11225     },
11226
11227     /**
11228      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11229      * @param {String} property A field on your records
11230      * @param {Number} start The record index to start at (defaults to 0)
11231      * @param {Number} end The last record index to include (defaults to length - 1)
11232      * @return {Number} The sum
11233      */
11234     sum : function(property, start, end){
11235         var rs = this.data.items, v = 0;
11236         start = start || 0;
11237         end = (end || end === 0) ? end : rs.length-1;
11238
11239         for(var i = start; i <= end; i++){
11240             v += (rs[i].data[property] || 0);
11241         }
11242         return v;
11243     },
11244
11245     /**
11246      * Filter the records by a specified property.
11247      * @param {String} field A field on your records
11248      * @param {String/RegExp} value Either a string that the field
11249      * should start with or a RegExp to test against the field
11250      * @param {Boolean} anyMatch True to match any part not just the beginning
11251      */
11252     filter : function(property, value, anyMatch){
11253         var fn = this.createFilterFn(property, value, anyMatch);
11254         return fn ? this.filterBy(fn) : this.clearFilter();
11255     },
11256
11257     /**
11258      * Filter by a function. The specified function will be called with each
11259      * record in this data source. If the function returns true the record is included,
11260      * otherwise it is filtered.
11261      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11262      * @param {Object} scope (optional) The scope of the function (defaults to this)
11263      */
11264     filterBy : function(fn, scope){
11265         this.snapshot = this.snapshot || this.data;
11266         this.data = this.queryBy(fn, scope||this);
11267         this.fireEvent("datachanged", this);
11268     },
11269
11270     /**
11271      * Query the records by a specified property.
11272      * @param {String} field A field on your records
11273      * @param {String/RegExp} value Either a string that the field
11274      * should start with or a RegExp to test against the field
11275      * @param {Boolean} anyMatch True to match any part not just the beginning
11276      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11277      */
11278     query : function(property, value, anyMatch){
11279         var fn = this.createFilterFn(property, value, anyMatch);
11280         return fn ? this.queryBy(fn) : this.data.clone();
11281     },
11282
11283     /**
11284      * Query by a function. The specified function will be called with each
11285      * record in this data source. If the function returns true the record is included
11286      * in the results.
11287      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11288      * @param {Object} scope (optional) The scope of the function (defaults to this)
11289       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11290      **/
11291     queryBy : function(fn, scope){
11292         var data = this.snapshot || this.data;
11293         return data.filterBy(fn, scope||this);
11294     },
11295
11296     /**
11297      * Collects unique values for a particular dataIndex from this store.
11298      * @param {String} dataIndex The property to collect
11299      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11300      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11301      * @return {Array} An array of the unique values
11302      **/
11303     collect : function(dataIndex, allowNull, bypassFilter){
11304         var d = (bypassFilter === true && this.snapshot) ?
11305                 this.snapshot.items : this.data.items;
11306         var v, sv, r = [], l = {};
11307         for(var i = 0, len = d.length; i < len; i++){
11308             v = d[i].data[dataIndex];
11309             sv = String(v);
11310             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11311                 l[sv] = true;
11312                 r[r.length] = v;
11313             }
11314         }
11315         return r;
11316     },
11317
11318     /**
11319      * Revert to a view of the Record cache with no filtering applied.
11320      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11321      */
11322     clearFilter : function(suppressEvent){
11323         if(this.snapshot && this.snapshot != this.data){
11324             this.data = this.snapshot;
11325             delete this.snapshot;
11326             if(suppressEvent !== true){
11327                 this.fireEvent("datachanged", this);
11328             }
11329         }
11330     },
11331
11332     // private
11333     afterEdit : function(record){
11334         if(this.modified.indexOf(record) == -1){
11335             this.modified.push(record);
11336         }
11337         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11338     },
11339     
11340     // private
11341     afterReject : function(record){
11342         this.modified.remove(record);
11343         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11344     },
11345
11346     // private
11347     afterCommit : function(record){
11348         this.modified.remove(record);
11349         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11350     },
11351
11352     /**
11353      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11354      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11355      */
11356     commitChanges : function(){
11357         var m = this.modified.slice(0);
11358         this.modified = [];
11359         for(var i = 0, len = m.length; i < len; i++){
11360             m[i].commit();
11361         }
11362     },
11363
11364     /**
11365      * Cancel outstanding changes on all changed records.
11366      */
11367     rejectChanges : function(){
11368         var m = this.modified.slice(0);
11369         this.modified = [];
11370         for(var i = 0, len = m.length; i < len; i++){
11371             m[i].reject();
11372         }
11373     },
11374
11375     onMetaChange : function(meta, rtype, o){
11376         this.recordType = rtype;
11377         this.fields = rtype.prototype.fields;
11378         delete this.snapshot;
11379         this.sortInfo = meta.sortInfo || this.sortInfo;
11380         this.modified = [];
11381         this.fireEvent('metachange', this, this.reader.meta);
11382     },
11383     
11384     moveIndex : function(data, type)
11385     {
11386         var index = this.indexOf(data);
11387         
11388         var newIndex = index + type;
11389         
11390         this.remove(data);
11391         
11392         this.insert(newIndex, data);
11393         
11394     }
11395 });/*
11396  * Based on:
11397  * Ext JS Library 1.1.1
11398  * Copyright(c) 2006-2007, Ext JS, LLC.
11399  *
11400  * Originally Released Under LGPL - original licence link has changed is not relivant.
11401  *
11402  * Fork - LGPL
11403  * <script type="text/javascript">
11404  */
11405
11406 /**
11407  * @class Roo.data.SimpleStore
11408  * @extends Roo.data.Store
11409  * Small helper class to make creating Stores from Array data easier.
11410  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11411  * @cfg {Array} fields An array of field definition objects, or field name strings.
11412  * @cfg {Array} data The multi-dimensional array of data
11413  * @constructor
11414  * @param {Object} config
11415  */
11416 Roo.data.SimpleStore = function(config){
11417     Roo.data.SimpleStore.superclass.constructor.call(this, {
11418         isLocal : true,
11419         reader: new Roo.data.ArrayReader({
11420                 id: config.id
11421             },
11422             Roo.data.Record.create(config.fields)
11423         ),
11424         proxy : new Roo.data.MemoryProxy(config.data)
11425     });
11426     this.load();
11427 };
11428 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11429  * Based on:
11430  * Ext JS Library 1.1.1
11431  * Copyright(c) 2006-2007, Ext JS, LLC.
11432  *
11433  * Originally Released Under LGPL - original licence link has changed is not relivant.
11434  *
11435  * Fork - LGPL
11436  * <script type="text/javascript">
11437  */
11438
11439 /**
11440 /**
11441  * @extends Roo.data.Store
11442  * @class Roo.data.JsonStore
11443  * Small helper class to make creating Stores for JSON data easier. <br/>
11444 <pre><code>
11445 var store = new Roo.data.JsonStore({
11446     url: 'get-images.php',
11447     root: 'images',
11448     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11449 });
11450 </code></pre>
11451  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11452  * JsonReader and HttpProxy (unless inline data is provided).</b>
11453  * @cfg {Array} fields An array of field definition objects, or field name strings.
11454  * @constructor
11455  * @param {Object} config
11456  */
11457 Roo.data.JsonStore = function(c){
11458     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11459         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11460         reader: new Roo.data.JsonReader(c, c.fields)
11461     }));
11462 };
11463 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11464  * Based on:
11465  * Ext JS Library 1.1.1
11466  * Copyright(c) 2006-2007, Ext JS, LLC.
11467  *
11468  * Originally Released Under LGPL - original licence link has changed is not relivant.
11469  *
11470  * Fork - LGPL
11471  * <script type="text/javascript">
11472  */
11473
11474  
11475 Roo.data.Field = function(config){
11476     if(typeof config == "string"){
11477         config = {name: config};
11478     }
11479     Roo.apply(this, config);
11480     
11481     if(!this.type){
11482         this.type = "auto";
11483     }
11484     
11485     var st = Roo.data.SortTypes;
11486     // named sortTypes are supported, here we look them up
11487     if(typeof this.sortType == "string"){
11488         this.sortType = st[this.sortType];
11489     }
11490     
11491     // set default sortType for strings and dates
11492     if(!this.sortType){
11493         switch(this.type){
11494             case "string":
11495                 this.sortType = st.asUCString;
11496                 break;
11497             case "date":
11498                 this.sortType = st.asDate;
11499                 break;
11500             default:
11501                 this.sortType = st.none;
11502         }
11503     }
11504
11505     // define once
11506     var stripRe = /[\$,%]/g;
11507
11508     // prebuilt conversion function for this field, instead of
11509     // switching every time we're reading a value
11510     if(!this.convert){
11511         var cv, dateFormat = this.dateFormat;
11512         switch(this.type){
11513             case "":
11514             case "auto":
11515             case undefined:
11516                 cv = function(v){ return v; };
11517                 break;
11518             case "string":
11519                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11520                 break;
11521             case "int":
11522                 cv = function(v){
11523                     return v !== undefined && v !== null && v !== '' ?
11524                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11525                     };
11526                 break;
11527             case "float":
11528                 cv = function(v){
11529                     return v !== undefined && v !== null && v !== '' ?
11530                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11531                     };
11532                 break;
11533             case "bool":
11534             case "boolean":
11535                 cv = function(v){ return v === true || v === "true" || v == 1; };
11536                 break;
11537             case "date":
11538                 cv = function(v){
11539                     if(!v){
11540                         return '';
11541                     }
11542                     if(v instanceof Date){
11543                         return v;
11544                     }
11545                     if(dateFormat){
11546                         if(dateFormat == "timestamp"){
11547                             return new Date(v*1000);
11548                         }
11549                         return Date.parseDate(v, dateFormat);
11550                     }
11551                     var parsed = Date.parse(v);
11552                     return parsed ? new Date(parsed) : null;
11553                 };
11554              break;
11555             
11556         }
11557         this.convert = cv;
11558     }
11559 };
11560
11561 Roo.data.Field.prototype = {
11562     dateFormat: null,
11563     defaultValue: "",
11564     mapping: null,
11565     sortType : null,
11566     sortDir : "ASC"
11567 };/*
11568  * Based on:
11569  * Ext JS Library 1.1.1
11570  * Copyright(c) 2006-2007, Ext JS, LLC.
11571  *
11572  * Originally Released Under LGPL - original licence link has changed is not relivant.
11573  *
11574  * Fork - LGPL
11575  * <script type="text/javascript">
11576  */
11577  
11578 // Base class for reading structured data from a data source.  This class is intended to be
11579 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11580
11581 /**
11582  * @class Roo.data.DataReader
11583  * Base class for reading structured data from a data source.  This class is intended to be
11584  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11585  */
11586
11587 Roo.data.DataReader = function(meta, recordType){
11588     
11589     this.meta = meta;
11590     
11591     this.recordType = recordType instanceof Array ? 
11592         Roo.data.Record.create(recordType) : recordType;
11593 };
11594
11595 Roo.data.DataReader.prototype = {
11596      /**
11597      * Create an empty record
11598      * @param {Object} data (optional) - overlay some values
11599      * @return {Roo.data.Record} record created.
11600      */
11601     newRow :  function(d) {
11602         var da =  {};
11603         this.recordType.prototype.fields.each(function(c) {
11604             switch( c.type) {
11605                 case 'int' : da[c.name] = 0; break;
11606                 case 'date' : da[c.name] = new Date(); break;
11607                 case 'float' : da[c.name] = 0.0; break;
11608                 case 'boolean' : da[c.name] = false; break;
11609                 default : da[c.name] = ""; break;
11610             }
11611             
11612         });
11613         return new this.recordType(Roo.apply(da, d));
11614     }
11615     
11616 };/*
11617  * Based on:
11618  * Ext JS Library 1.1.1
11619  * Copyright(c) 2006-2007, Ext JS, LLC.
11620  *
11621  * Originally Released Under LGPL - original licence link has changed is not relivant.
11622  *
11623  * Fork - LGPL
11624  * <script type="text/javascript">
11625  */
11626
11627 /**
11628  * @class Roo.data.DataProxy
11629  * @extends Roo.data.Observable
11630  * This class is an abstract base class for implementations which provide retrieval of
11631  * unformatted data objects.<br>
11632  * <p>
11633  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11634  * (of the appropriate type which knows how to parse the data object) to provide a block of
11635  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11636  * <p>
11637  * Custom implementations must implement the load method as described in
11638  * {@link Roo.data.HttpProxy#load}.
11639  */
11640 Roo.data.DataProxy = function(){
11641     this.addEvents({
11642         /**
11643          * @event beforeload
11644          * Fires before a network request is made to retrieve a data object.
11645          * @param {Object} This DataProxy object.
11646          * @param {Object} params The params parameter to the load function.
11647          */
11648         beforeload : true,
11649         /**
11650          * @event load
11651          * Fires before the load method's callback is called.
11652          * @param {Object} This DataProxy object.
11653          * @param {Object} o The data object.
11654          * @param {Object} arg The callback argument object passed to the load function.
11655          */
11656         load : true,
11657         /**
11658          * @event loadexception
11659          * Fires if an Exception occurs during data retrieval.
11660          * @param {Object} This DataProxy object.
11661          * @param {Object} o The data object.
11662          * @param {Object} arg The callback argument object passed to the load function.
11663          * @param {Object} e The Exception.
11664          */
11665         loadexception : true
11666     });
11667     Roo.data.DataProxy.superclass.constructor.call(this);
11668 };
11669
11670 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11671
11672     /**
11673      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11674      */
11675 /*
11676  * Based on:
11677  * Ext JS Library 1.1.1
11678  * Copyright(c) 2006-2007, Ext JS, LLC.
11679  *
11680  * Originally Released Under LGPL - original licence link has changed is not relivant.
11681  *
11682  * Fork - LGPL
11683  * <script type="text/javascript">
11684  */
11685 /**
11686  * @class Roo.data.MemoryProxy
11687  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11688  * to the Reader when its load method is called.
11689  * @constructor
11690  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11691  */
11692 Roo.data.MemoryProxy = function(data){
11693     if (data.data) {
11694         data = data.data;
11695     }
11696     Roo.data.MemoryProxy.superclass.constructor.call(this);
11697     this.data = data;
11698 };
11699
11700 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11701     
11702     /**
11703      * Load data from the requested source (in this case an in-memory
11704      * data object passed to the constructor), read the data object into
11705      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11706      * process that block using the passed callback.
11707      * @param {Object} params This parameter is not used by the MemoryProxy class.
11708      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11709      * object into a block of Roo.data.Records.
11710      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11711      * The function must be passed <ul>
11712      * <li>The Record block object</li>
11713      * <li>The "arg" argument from the load function</li>
11714      * <li>A boolean success indicator</li>
11715      * </ul>
11716      * @param {Object} scope The scope in which to call the callback
11717      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11718      */
11719     load : function(params, reader, callback, scope, arg){
11720         params = params || {};
11721         var result;
11722         try {
11723             result = reader.readRecords(this.data);
11724         }catch(e){
11725             this.fireEvent("loadexception", this, arg, null, e);
11726             callback.call(scope, null, arg, false);
11727             return;
11728         }
11729         callback.call(scope, result, arg, true);
11730     },
11731     
11732     // private
11733     update : function(params, records){
11734         
11735     }
11736 });/*
11737  * Based on:
11738  * Ext JS Library 1.1.1
11739  * Copyright(c) 2006-2007, Ext JS, LLC.
11740  *
11741  * Originally Released Under LGPL - original licence link has changed is not relivant.
11742  *
11743  * Fork - LGPL
11744  * <script type="text/javascript">
11745  */
11746 /**
11747  * @class Roo.data.HttpProxy
11748  * @extends Roo.data.DataProxy
11749  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11750  * configured to reference a certain URL.<br><br>
11751  * <p>
11752  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11753  * from which the running page was served.<br><br>
11754  * <p>
11755  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11756  * <p>
11757  * Be aware that to enable the browser to parse an XML document, the server must set
11758  * the Content-Type header in the HTTP response to "text/xml".
11759  * @constructor
11760  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11761  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11762  * will be used to make the request.
11763  */
11764 Roo.data.HttpProxy = function(conn){
11765     Roo.data.HttpProxy.superclass.constructor.call(this);
11766     // is conn a conn config or a real conn?
11767     this.conn = conn;
11768     this.useAjax = !conn || !conn.events;
11769   
11770 };
11771
11772 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11773     // thse are take from connection...
11774     
11775     /**
11776      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11777      */
11778     /**
11779      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11780      * extra parameters to each request made by this object. (defaults to undefined)
11781      */
11782     /**
11783      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11784      *  to each request made by this object. (defaults to undefined)
11785      */
11786     /**
11787      * @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)
11788      */
11789     /**
11790      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11791      */
11792      /**
11793      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11794      * @type Boolean
11795      */
11796   
11797
11798     /**
11799      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11800      * @type Boolean
11801      */
11802     /**
11803      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11804      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11805      * a finer-grained basis than the DataProxy events.
11806      */
11807     getConnection : function(){
11808         return this.useAjax ? Roo.Ajax : this.conn;
11809     },
11810
11811     /**
11812      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11813      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11814      * process that block using the passed callback.
11815      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11816      * for the request to the remote server.
11817      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11818      * object into a block of Roo.data.Records.
11819      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11820      * The function must be passed <ul>
11821      * <li>The Record block object</li>
11822      * <li>The "arg" argument from the load function</li>
11823      * <li>A boolean success indicator</li>
11824      * </ul>
11825      * @param {Object} scope The scope in which to call the callback
11826      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11827      */
11828     load : function(params, reader, callback, scope, arg){
11829         if(this.fireEvent("beforeload", this, params) !== false){
11830             var  o = {
11831                 params : params || {},
11832                 request: {
11833                     callback : callback,
11834                     scope : scope,
11835                     arg : arg
11836                 },
11837                 reader: reader,
11838                 callback : this.loadResponse,
11839                 scope: this
11840             };
11841             if(this.useAjax){
11842                 Roo.applyIf(o, this.conn);
11843                 if(this.activeRequest){
11844                     Roo.Ajax.abort(this.activeRequest);
11845                 }
11846                 this.activeRequest = Roo.Ajax.request(o);
11847             }else{
11848                 this.conn.request(o);
11849             }
11850         }else{
11851             callback.call(scope||this, null, arg, false);
11852         }
11853     },
11854
11855     // private
11856     loadResponse : function(o, success, response){
11857         delete this.activeRequest;
11858         if(!success){
11859             this.fireEvent("loadexception", this, o, response);
11860             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11861             return;
11862         }
11863         var result;
11864         try {
11865             result = o.reader.read(response);
11866         }catch(e){
11867             this.fireEvent("loadexception", this, o, response, e);
11868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11869             return;
11870         }
11871         
11872         this.fireEvent("load", this, o, o.request.arg);
11873         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11874     },
11875
11876     // private
11877     update : function(dataSet){
11878
11879     },
11880
11881     // private
11882     updateResponse : function(dataSet){
11883
11884     }
11885 });/*
11886  * Based on:
11887  * Ext JS Library 1.1.1
11888  * Copyright(c) 2006-2007, Ext JS, LLC.
11889  *
11890  * Originally Released Under LGPL - original licence link has changed is not relivant.
11891  *
11892  * Fork - LGPL
11893  * <script type="text/javascript">
11894  */
11895
11896 /**
11897  * @class Roo.data.ScriptTagProxy
11898  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11899  * other than the originating domain of the running page.<br><br>
11900  * <p>
11901  * <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
11902  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11903  * <p>
11904  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11905  * source code that is used as the source inside a &lt;script> tag.<br><br>
11906  * <p>
11907  * In order for the browser to process the returned data, the server must wrap the data object
11908  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11909  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11910  * depending on whether the callback name was passed:
11911  * <p>
11912  * <pre><code>
11913 boolean scriptTag = false;
11914 String cb = request.getParameter("callback");
11915 if (cb != null) {
11916     scriptTag = true;
11917     response.setContentType("text/javascript");
11918 } else {
11919     response.setContentType("application/x-json");
11920 }
11921 Writer out = response.getWriter();
11922 if (scriptTag) {
11923     out.write(cb + "(");
11924 }
11925 out.print(dataBlock.toJsonString());
11926 if (scriptTag) {
11927     out.write(");");
11928 }
11929 </pre></code>
11930  *
11931  * @constructor
11932  * @param {Object} config A configuration object.
11933  */
11934 Roo.data.ScriptTagProxy = function(config){
11935     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11936     Roo.apply(this, config);
11937     this.head = document.getElementsByTagName("head")[0];
11938 };
11939
11940 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11941
11942 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11943     /**
11944      * @cfg {String} url The URL from which to request the data object.
11945      */
11946     /**
11947      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11948      */
11949     timeout : 30000,
11950     /**
11951      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11952      * the server the name of the callback function set up by the load call to process the returned data object.
11953      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11954      * javascript output which calls this named function passing the data object as its only parameter.
11955      */
11956     callbackParam : "callback",
11957     /**
11958      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11959      * name to the request.
11960      */
11961     nocache : true,
11962
11963     /**
11964      * Load data from the configured URL, read the data object into
11965      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11966      * process that block using the passed callback.
11967      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11968      * for the request to the remote server.
11969      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11970      * object into a block of Roo.data.Records.
11971      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11972      * The function must be passed <ul>
11973      * <li>The Record block object</li>
11974      * <li>The "arg" argument from the load function</li>
11975      * <li>A boolean success indicator</li>
11976      * </ul>
11977      * @param {Object} scope The scope in which to call the callback
11978      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11979      */
11980     load : function(params, reader, callback, scope, arg){
11981         if(this.fireEvent("beforeload", this, params) !== false){
11982
11983             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11984
11985             var url = this.url;
11986             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11987             if(this.nocache){
11988                 url += "&_dc=" + (new Date().getTime());
11989             }
11990             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11991             var trans = {
11992                 id : transId,
11993                 cb : "stcCallback"+transId,
11994                 scriptId : "stcScript"+transId,
11995                 params : params,
11996                 arg : arg,
11997                 url : url,
11998                 callback : callback,
11999                 scope : scope,
12000                 reader : reader
12001             };
12002             var conn = this;
12003
12004             window[trans.cb] = function(o){
12005                 conn.handleResponse(o, trans);
12006             };
12007
12008             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12009
12010             if(this.autoAbort !== false){
12011                 this.abort();
12012             }
12013
12014             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12015
12016             var script = document.createElement("script");
12017             script.setAttribute("src", url);
12018             script.setAttribute("type", "text/javascript");
12019             script.setAttribute("id", trans.scriptId);
12020             this.head.appendChild(script);
12021
12022             this.trans = trans;
12023         }else{
12024             callback.call(scope||this, null, arg, false);
12025         }
12026     },
12027
12028     // private
12029     isLoading : function(){
12030         return this.trans ? true : false;
12031     },
12032
12033     /**
12034      * Abort the current server request.
12035      */
12036     abort : function(){
12037         if(this.isLoading()){
12038             this.destroyTrans(this.trans);
12039         }
12040     },
12041
12042     // private
12043     destroyTrans : function(trans, isLoaded){
12044         this.head.removeChild(document.getElementById(trans.scriptId));
12045         clearTimeout(trans.timeoutId);
12046         if(isLoaded){
12047             window[trans.cb] = undefined;
12048             try{
12049                 delete window[trans.cb];
12050             }catch(e){}
12051         }else{
12052             // if hasn't been loaded, wait for load to remove it to prevent script error
12053             window[trans.cb] = function(){
12054                 window[trans.cb] = undefined;
12055                 try{
12056                     delete window[trans.cb];
12057                 }catch(e){}
12058             };
12059         }
12060     },
12061
12062     // private
12063     handleResponse : function(o, trans){
12064         this.trans = false;
12065         this.destroyTrans(trans, true);
12066         var result;
12067         try {
12068             result = trans.reader.readRecords(o);
12069         }catch(e){
12070             this.fireEvent("loadexception", this, o, trans.arg, e);
12071             trans.callback.call(trans.scope||window, null, trans.arg, false);
12072             return;
12073         }
12074         this.fireEvent("load", this, o, trans.arg);
12075         trans.callback.call(trans.scope||window, result, trans.arg, true);
12076     },
12077
12078     // private
12079     handleFailure : function(trans){
12080         this.trans = false;
12081         this.destroyTrans(trans, false);
12082         this.fireEvent("loadexception", this, null, trans.arg);
12083         trans.callback.call(trans.scope||window, null, trans.arg, false);
12084     }
12085 });/*
12086  * Based on:
12087  * Ext JS Library 1.1.1
12088  * Copyright(c) 2006-2007, Ext JS, LLC.
12089  *
12090  * Originally Released Under LGPL - original licence link has changed is not relivant.
12091  *
12092  * Fork - LGPL
12093  * <script type="text/javascript">
12094  */
12095
12096 /**
12097  * @class Roo.data.JsonReader
12098  * @extends Roo.data.DataReader
12099  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12100  * based on mappings in a provided Roo.data.Record constructor.
12101  * 
12102  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12103  * in the reply previously. 
12104  * 
12105  * <p>
12106  * Example code:
12107  * <pre><code>
12108 var RecordDef = Roo.data.Record.create([
12109     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12110     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12111 ]);
12112 var myReader = new Roo.data.JsonReader({
12113     totalProperty: "results",    // The property which contains the total dataset size (optional)
12114     root: "rows",                // The property which contains an Array of row objects
12115     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12116 }, RecordDef);
12117 </code></pre>
12118  * <p>
12119  * This would consume a JSON file like this:
12120  * <pre><code>
12121 { 'results': 2, 'rows': [
12122     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12123     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12124 }
12125 </code></pre>
12126  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12127  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12128  * paged from the remote server.
12129  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12130  * @cfg {String} root name of the property which contains the Array of row objects.
12131  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12132  * @cfg {Array} fields Array of field definition objects
12133  * @constructor
12134  * Create a new JsonReader
12135  * @param {Object} meta Metadata configuration options
12136  * @param {Object} recordType Either an Array of field definition objects,
12137  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12138  */
12139 Roo.data.JsonReader = function(meta, recordType){
12140     
12141     meta = meta || {};
12142     // set some defaults:
12143     Roo.applyIf(meta, {
12144         totalProperty: 'total',
12145         successProperty : 'success',
12146         root : 'data',
12147         id : 'id'
12148     });
12149     
12150     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12151 };
12152 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12153     
12154     /**
12155      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12156      * Used by Store query builder to append _requestMeta to params.
12157      * 
12158      */
12159     metaFromRemote : false,
12160     /**
12161      * This method is only used by a DataProxy which has retrieved data from a remote server.
12162      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12163      * @return {Object} data A data block which is used by an Roo.data.Store object as
12164      * a cache of Roo.data.Records.
12165      */
12166     read : function(response){
12167         var json = response.responseText;
12168        
12169         var o = /* eval:var:o */ eval("("+json+")");
12170         if(!o) {
12171             throw {message: "JsonReader.read: Json object not found"};
12172         }
12173         
12174         if(o.metaData){
12175             
12176             delete this.ef;
12177             this.metaFromRemote = true;
12178             this.meta = o.metaData;
12179             this.recordType = Roo.data.Record.create(o.metaData.fields);
12180             this.onMetaChange(this.meta, this.recordType, o);
12181         }
12182         return this.readRecords(o);
12183     },
12184
12185     // private function a store will implement
12186     onMetaChange : function(meta, recordType, o){
12187
12188     },
12189
12190     /**
12191          * @ignore
12192          */
12193     simpleAccess: function(obj, subsc) {
12194         return obj[subsc];
12195     },
12196
12197         /**
12198          * @ignore
12199          */
12200     getJsonAccessor: function(){
12201         var re = /[\[\.]/;
12202         return function(expr) {
12203             try {
12204                 return(re.test(expr))
12205                     ? new Function("obj", "return obj." + expr)
12206                     : function(obj){
12207                         return obj[expr];
12208                     };
12209             } catch(e){}
12210             return Roo.emptyFn;
12211         };
12212     }(),
12213
12214     /**
12215      * Create a data block containing Roo.data.Records from an XML document.
12216      * @param {Object} o An object which contains an Array of row objects in the property specified
12217      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12218      * which contains the total size of the dataset.
12219      * @return {Object} data A data block which is used by an Roo.data.Store object as
12220      * a cache of Roo.data.Records.
12221      */
12222     readRecords : function(o){
12223         /**
12224          * After any data loads, the raw JSON data is available for further custom processing.
12225          * @type Object
12226          */
12227         this.o = o;
12228         var s = this.meta, Record = this.recordType,
12229             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12230
12231 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12232         if (!this.ef) {
12233             if(s.totalProperty) {
12234                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12235                 }
12236                 if(s.successProperty) {
12237                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12238                 }
12239                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12240                 if (s.id) {
12241                         var g = this.getJsonAccessor(s.id);
12242                         this.getId = function(rec) {
12243                                 var r = g(rec);  
12244                                 return (r === undefined || r === "") ? null : r;
12245                         };
12246                 } else {
12247                         this.getId = function(){return null;};
12248                 }
12249             this.ef = [];
12250             for(var jj = 0; jj < fl; jj++){
12251                 f = fi[jj];
12252                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12253                 this.ef[jj] = this.getJsonAccessor(map);
12254             }
12255         }
12256
12257         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12258         if(s.totalProperty){
12259             var vt = parseInt(this.getTotal(o), 10);
12260             if(!isNaN(vt)){
12261                 totalRecords = vt;
12262             }
12263         }
12264         if(s.successProperty){
12265             var vs = this.getSuccess(o);
12266             if(vs === false || vs === 'false'){
12267                 success = false;
12268             }
12269         }
12270         var records = [];
12271         for(var i = 0; i < c; i++){
12272                 var n = root[i];
12273             var values = {};
12274             var id = this.getId(n);
12275             for(var j = 0; j < fl; j++){
12276                 f = fi[j];
12277             var v = this.ef[j](n);
12278             if (!f.convert) {
12279                 Roo.log('missing convert for ' + f.name);
12280                 Roo.log(f);
12281                 continue;
12282             }
12283             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12284             }
12285             var record = new Record(values, id);
12286             record.json = n;
12287             records[i] = record;
12288         }
12289         return {
12290             raw : o,
12291             success : success,
12292             records : records,
12293             totalRecords : totalRecords
12294         };
12295     }
12296 });/*
12297  * Based on:
12298  * Ext JS Library 1.1.1
12299  * Copyright(c) 2006-2007, Ext JS, LLC.
12300  *
12301  * Originally Released Under LGPL - original licence link has changed is not relivant.
12302  *
12303  * Fork - LGPL
12304  * <script type="text/javascript">
12305  */
12306
12307 /**
12308  * @class Roo.data.ArrayReader
12309  * @extends Roo.data.DataReader
12310  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12311  * Each element of that Array represents a row of data fields. The
12312  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12313  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12314  * <p>
12315  * Example code:.
12316  * <pre><code>
12317 var RecordDef = Roo.data.Record.create([
12318     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12319     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12320 ]);
12321 var myReader = new Roo.data.ArrayReader({
12322     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12323 }, RecordDef);
12324 </code></pre>
12325  * <p>
12326  * This would consume an Array like this:
12327  * <pre><code>
12328 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12329   </code></pre>
12330  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12331  * @constructor
12332  * Create a new JsonReader
12333  * @param {Object} meta Metadata configuration options.
12334  * @param {Object} recordType Either an Array of field definition objects
12335  * as specified to {@link Roo.data.Record#create},
12336  * or an {@link Roo.data.Record} object
12337  * created using {@link Roo.data.Record#create}.
12338  */
12339 Roo.data.ArrayReader = function(meta, recordType){
12340     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12341 };
12342
12343 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12344     /**
12345      * Create a data block containing Roo.data.Records from an XML document.
12346      * @param {Object} o An Array of row objects which represents the dataset.
12347      * @return {Object} data A data block which is used by an Roo.data.Store object as
12348      * a cache of Roo.data.Records.
12349      */
12350     readRecords : function(o){
12351         var sid = this.meta ? this.meta.id : null;
12352         var recordType = this.recordType, fields = recordType.prototype.fields;
12353         var records = [];
12354         var root = o;
12355             for(var i = 0; i < root.length; i++){
12356                     var n = root[i];
12357                 var values = {};
12358                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12359                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12360                 var f = fields.items[j];
12361                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12362                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12363                 v = f.convert(v);
12364                 values[f.name] = v;
12365             }
12366                 var record = new recordType(values, id);
12367                 record.json = n;
12368                 records[records.length] = record;
12369             }
12370             return {
12371                 records : records,
12372                 totalRecords : records.length
12373             };
12374     }
12375 });/*
12376  * - LGPL
12377  * * 
12378  */
12379
12380 /**
12381  * @class Roo.bootstrap.ComboBox
12382  * @extends Roo.bootstrap.TriggerField
12383  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12384  * @cfg {Boolean} append (true|false) default false
12385  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12386  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12387  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12388  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12389  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12390  * @cfg {Boolean} animate default true
12391  * @cfg {Boolean} emptyResultText only for touch device
12392  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12393  * @cfg {String} emptyTitle default ''
12394  * @constructor
12395  * Create a new ComboBox.
12396  * @param {Object} config Configuration options
12397  */
12398 Roo.bootstrap.ComboBox = function(config){
12399     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12400     this.addEvents({
12401         /**
12402          * @event expand
12403          * Fires when the dropdown list is expanded
12404         * @param {Roo.bootstrap.ComboBox} combo This combo box
12405         */
12406         'expand' : true,
12407         /**
12408          * @event collapse
12409          * Fires when the dropdown list is collapsed
12410         * @param {Roo.bootstrap.ComboBox} combo This combo box
12411         */
12412         'collapse' : true,
12413         /**
12414          * @event beforeselect
12415          * Fires before a list item is selected. Return false to cancel the selection.
12416         * @param {Roo.bootstrap.ComboBox} combo This combo box
12417         * @param {Roo.data.Record} record The data record returned from the underlying store
12418         * @param {Number} index The index of the selected item in the dropdown list
12419         */
12420         'beforeselect' : true,
12421         /**
12422          * @event select
12423          * Fires when a list item is selected
12424         * @param {Roo.bootstrap.ComboBox} combo This combo box
12425         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12426         * @param {Number} index The index of the selected item in the dropdown list
12427         */
12428         'select' : true,
12429         /**
12430          * @event beforequery
12431          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12432          * The event object passed has these properties:
12433         * @param {Roo.bootstrap.ComboBox} combo This combo box
12434         * @param {String} query The query
12435         * @param {Boolean} forceAll true to force "all" query
12436         * @param {Boolean} cancel true to cancel the query
12437         * @param {Object} e The query event object
12438         */
12439         'beforequery': true,
12440          /**
12441          * @event add
12442          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12443         * @param {Roo.bootstrap.ComboBox} combo This combo box
12444         */
12445         'add' : true,
12446         /**
12447          * @event edit
12448          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12449         * @param {Roo.bootstrap.ComboBox} combo This combo box
12450         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12451         */
12452         'edit' : true,
12453         /**
12454          * @event remove
12455          * Fires when the remove value from the combobox array
12456         * @param {Roo.bootstrap.ComboBox} combo This combo box
12457         */
12458         'remove' : true,
12459         /**
12460          * @event afterremove
12461          * Fires when the remove value from the combobox array
12462         * @param {Roo.bootstrap.ComboBox} combo This combo box
12463         */
12464         'afterremove' : true,
12465         /**
12466          * @event specialfilter
12467          * Fires when specialfilter
12468             * @param {Roo.bootstrap.ComboBox} combo This combo box
12469             */
12470         'specialfilter' : true,
12471         /**
12472          * @event tick
12473          * Fires when tick the element
12474             * @param {Roo.bootstrap.ComboBox} combo This combo box
12475             */
12476         'tick' : true,
12477         /**
12478          * @event touchviewdisplay
12479          * Fires when touch view require special display (default is using displayField)
12480             * @param {Roo.bootstrap.ComboBox} combo This combo box
12481             * @param {Object} cfg set html .
12482             */
12483         'touchviewdisplay' : true
12484         
12485     });
12486     
12487     this.item = [];
12488     this.tickItems = [];
12489     
12490     this.selectedIndex = -1;
12491     if(this.mode == 'local'){
12492         if(config.queryDelay === undefined){
12493             this.queryDelay = 10;
12494         }
12495         if(config.minChars === undefined){
12496             this.minChars = 0;
12497         }
12498     }
12499 };
12500
12501 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12502      
12503     /**
12504      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12505      * rendering into an Roo.Editor, defaults to false)
12506      */
12507     /**
12508      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12509      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12510      */
12511     /**
12512      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12513      */
12514     /**
12515      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12516      * the dropdown list (defaults to undefined, with no header element)
12517      */
12518
12519      /**
12520      * @cfg {String/Roo.Template} tpl The template to use to render the output
12521      */
12522      
12523      /**
12524      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12525      */
12526     listWidth: undefined,
12527     /**
12528      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12529      * mode = 'remote' or 'text' if mode = 'local')
12530      */
12531     displayField: undefined,
12532     
12533     /**
12534      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12535      * mode = 'remote' or 'value' if mode = 'local'). 
12536      * Note: use of a valueField requires the user make a selection
12537      * in order for a value to be mapped.
12538      */
12539     valueField: undefined,
12540     /**
12541      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12542      */
12543     modalTitle : '',
12544     
12545     /**
12546      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12547      * field's data value (defaults to the underlying DOM element's name)
12548      */
12549     hiddenName: undefined,
12550     /**
12551      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12552      */
12553     listClass: '',
12554     /**
12555      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12556      */
12557     selectedClass: 'active',
12558     
12559     /**
12560      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12561      */
12562     shadow:'sides',
12563     /**
12564      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12565      * anchor positions (defaults to 'tl-bl')
12566      */
12567     listAlign: 'tl-bl?',
12568     /**
12569      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12570      */
12571     maxHeight: 300,
12572     /**
12573      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12574      * query specified by the allQuery config option (defaults to 'query')
12575      */
12576     triggerAction: 'query',
12577     /**
12578      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12579      * (defaults to 4, does not apply if editable = false)
12580      */
12581     minChars : 4,
12582     /**
12583      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12584      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12585      */
12586     typeAhead: false,
12587     /**
12588      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12589      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12590      */
12591     queryDelay: 500,
12592     /**
12593      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12594      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12595      */
12596     pageSize: 0,
12597     /**
12598      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12599      * when editable = true (defaults to false)
12600      */
12601     selectOnFocus:false,
12602     /**
12603      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12604      */
12605     queryParam: 'query',
12606     /**
12607      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12608      * when mode = 'remote' (defaults to 'Loading...')
12609      */
12610     loadingText: 'Loading...',
12611     /**
12612      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12613      */
12614     resizable: false,
12615     /**
12616      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12617      */
12618     handleHeight : 8,
12619     /**
12620      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12621      * traditional select (defaults to true)
12622      */
12623     editable: true,
12624     /**
12625      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12626      */
12627     allQuery: '',
12628     /**
12629      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12630      */
12631     mode: 'remote',
12632     /**
12633      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12634      * listWidth has a higher value)
12635      */
12636     minListWidth : 70,
12637     /**
12638      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12639      * allow the user to set arbitrary text into the field (defaults to false)
12640      */
12641     forceSelection:false,
12642     /**
12643      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12644      * if typeAhead = true (defaults to 250)
12645      */
12646     typeAheadDelay : 250,
12647     /**
12648      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12649      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12650      */
12651     valueNotFoundText : undefined,
12652     /**
12653      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12654      */
12655     blockFocus : false,
12656     
12657     /**
12658      * @cfg {Boolean} disableClear Disable showing of clear button.
12659      */
12660     disableClear : false,
12661     /**
12662      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12663      */
12664     alwaysQuery : false,
12665     
12666     /**
12667      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12668      */
12669     multiple : false,
12670     
12671     /**
12672      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12673      */
12674     invalidClass : "has-warning",
12675     
12676     /**
12677      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12678      */
12679     validClass : "has-success",
12680     
12681     /**
12682      * @cfg {Boolean} specialFilter (true|false) special filter default false
12683      */
12684     specialFilter : false,
12685     
12686     /**
12687      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12688      */
12689     mobileTouchView : true,
12690     
12691     /**
12692      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12693      */
12694     useNativeIOS : false,
12695     
12696     ios_options : false,
12697     
12698     //private
12699     addicon : false,
12700     editicon: false,
12701     
12702     page: 0,
12703     hasQuery: false,
12704     append: false,
12705     loadNext: false,
12706     autoFocus : true,
12707     tickable : false,
12708     btnPosition : 'right',
12709     triggerList : true,
12710     showToggleBtn : true,
12711     animate : true,
12712     emptyResultText: 'Empty',
12713     triggerText : 'Select',
12714     emptyTitle : '',
12715     
12716     // element that contains real text value.. (when hidden is used..)
12717     
12718     getAutoCreate : function()
12719     {   
12720         var cfg = false;
12721         //render
12722         /*
12723          * Render classic select for iso
12724          */
12725         
12726         if(Roo.isIOS && this.useNativeIOS){
12727             cfg = this.getAutoCreateNativeIOS();
12728             return cfg;
12729         }
12730         
12731         /*
12732          * Touch Devices
12733          */
12734         
12735         if(Roo.isTouch && this.mobileTouchView){
12736             cfg = this.getAutoCreateTouchView();
12737             return cfg;;
12738         }
12739         
12740         /*
12741          *  Normal ComboBox
12742          */
12743         if(!this.tickable){
12744             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12745             return cfg;
12746         }
12747         
12748         /*
12749          *  ComboBox with tickable selections
12750          */
12751              
12752         var align = this.labelAlign || this.parentLabelAlign();
12753         
12754         cfg = {
12755             cls : 'form-group roo-combobox-tickable' //input-group
12756         };
12757         
12758         var btn_text_select = '';
12759         var btn_text_done = '';
12760         var btn_text_cancel = '';
12761         
12762         if (this.btn_text_show) {
12763             btn_text_select = 'Select';
12764             btn_text_done = 'Done';
12765             btn_text_cancel = 'Cancel'; 
12766         }
12767         
12768         var buttons = {
12769             tag : 'div',
12770             cls : 'tickable-buttons',
12771             cn : [
12772                 {
12773                     tag : 'button',
12774                     type : 'button',
12775                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12776                     //html : this.triggerText
12777                     html: btn_text_select
12778                 },
12779                 {
12780                     tag : 'button',
12781                     type : 'button',
12782                     name : 'ok',
12783                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12784                     //html : 'Done'
12785                     html: btn_text_done
12786                 },
12787                 {
12788                     tag : 'button',
12789                     type : 'button',
12790                     name : 'cancel',
12791                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12792                     //html : 'Cancel'
12793                     html: btn_text_cancel
12794                 }
12795             ]
12796         };
12797         
12798         if(this.editable){
12799             buttons.cn.unshift({
12800                 tag: 'input',
12801                 cls: 'roo-select2-search-field-input'
12802             });
12803         }
12804         
12805         var _this = this;
12806         
12807         Roo.each(buttons.cn, function(c){
12808             if (_this.size) {
12809                 c.cls += ' btn-' + _this.size;
12810             }
12811
12812             if (_this.disabled) {
12813                 c.disabled = true;
12814             }
12815         });
12816         
12817         var box = {
12818             tag: 'div',
12819             cn: [
12820                 {
12821                     tag: 'input',
12822                     type : 'hidden',
12823                     cls: 'form-hidden-field'
12824                 },
12825                 {
12826                     tag: 'ul',
12827                     cls: 'roo-select2-choices',
12828                     cn:[
12829                         {
12830                             tag: 'li',
12831                             cls: 'roo-select2-search-field',
12832                             cn: [
12833                                 buttons
12834                             ]
12835                         }
12836                     ]
12837                 }
12838             ]
12839         };
12840         
12841         var combobox = {
12842             cls: 'roo-select2-container input-group roo-select2-container-multi',
12843             cn: [
12844                 box
12845 //                {
12846 //                    tag: 'ul',
12847 //                    cls: 'typeahead typeahead-long dropdown-menu',
12848 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12849 //                }
12850             ]
12851         };
12852         
12853         if(this.hasFeedback && !this.allowBlank){
12854             
12855             var feedback = {
12856                 tag: 'span',
12857                 cls: 'glyphicon form-control-feedback'
12858             };
12859
12860             combobox.cn.push(feedback);
12861         }
12862         
12863         
12864         if (align ==='left' && this.fieldLabel.length) {
12865             
12866             cfg.cls += ' roo-form-group-label-left';
12867             
12868             cfg.cn = [
12869                 {
12870                     tag : 'i',
12871                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12872                     tooltip : 'This field is required'
12873                 },
12874                 {
12875                     tag: 'label',
12876                     'for' :  id,
12877                     cls : 'control-label',
12878                     html : this.fieldLabel
12879
12880                 },
12881                 {
12882                     cls : "", 
12883                     cn: [
12884                         combobox
12885                     ]
12886                 }
12887
12888             ];
12889             
12890             var labelCfg = cfg.cn[1];
12891             var contentCfg = cfg.cn[2];
12892             
12893
12894             if(this.indicatorpos == 'right'){
12895                 
12896                 cfg.cn = [
12897                     {
12898                         tag: 'label',
12899                         'for' :  id,
12900                         cls : 'control-label',
12901                         cn : [
12902                             {
12903                                 tag : 'span',
12904                                 html : this.fieldLabel
12905                             },
12906                             {
12907                                 tag : 'i',
12908                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12909                                 tooltip : 'This field is required'
12910                             }
12911                         ]
12912                     },
12913                     {
12914                         cls : "",
12915                         cn: [
12916                             combobox
12917                         ]
12918                     }
12919
12920                 ];
12921                 
12922                 
12923                 
12924                 labelCfg = cfg.cn[0];
12925                 contentCfg = cfg.cn[1];
12926             
12927             }
12928             
12929             if(this.labelWidth > 12){
12930                 labelCfg.style = "width: " + this.labelWidth + 'px';
12931             }
12932             
12933             if(this.labelWidth < 13 && this.labelmd == 0){
12934                 this.labelmd = this.labelWidth;
12935             }
12936             
12937             if(this.labellg > 0){
12938                 labelCfg.cls += ' col-lg-' + this.labellg;
12939                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12940             }
12941             
12942             if(this.labelmd > 0){
12943                 labelCfg.cls += ' col-md-' + this.labelmd;
12944                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12945             }
12946             
12947             if(this.labelsm > 0){
12948                 labelCfg.cls += ' col-sm-' + this.labelsm;
12949                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12950             }
12951             
12952             if(this.labelxs > 0){
12953                 labelCfg.cls += ' col-xs-' + this.labelxs;
12954                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12955             }
12956                 
12957                 
12958         } else if ( this.fieldLabel.length) {
12959 //                Roo.log(" label");
12960                  cfg.cn = [
12961                     {
12962                         tag : 'i',
12963                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12964                         tooltip : 'This field is required'
12965                     },
12966                     {
12967                         tag: 'label',
12968                         //cls : 'input-group-addon',
12969                         html : this.fieldLabel
12970                     },
12971                     combobox
12972                 ];
12973                 
12974                 if(this.indicatorpos == 'right'){
12975                     cfg.cn = [
12976                         {
12977                             tag: 'label',
12978                             //cls : 'input-group-addon',
12979                             html : this.fieldLabel
12980                         },
12981                         {
12982                             tag : 'i',
12983                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12984                             tooltip : 'This field is required'
12985                         },
12986                         combobox
12987                     ];
12988                     
12989                 }
12990
12991         } else {
12992             
12993 //                Roo.log(" no label && no align");
12994                 cfg = combobox
12995                      
12996                 
12997         }
12998          
12999         var settings=this;
13000         ['xs','sm','md','lg'].map(function(size){
13001             if (settings[size]) {
13002                 cfg.cls += ' col-' + size + '-' + settings[size];
13003             }
13004         });
13005         
13006         return cfg;
13007         
13008     },
13009     
13010     _initEventsCalled : false,
13011     
13012     // private
13013     initEvents: function()
13014     {   
13015         if (this._initEventsCalled) { // as we call render... prevent looping...
13016             return;
13017         }
13018         this._initEventsCalled = true;
13019         
13020         if (!this.store) {
13021             throw "can not find store for combo";
13022         }
13023         
13024         this.indicator = this.indicatorEl();
13025         
13026         this.store = Roo.factory(this.store, Roo.data);
13027         this.store.parent = this;
13028         
13029         // if we are building from html. then this element is so complex, that we can not really
13030         // use the rendered HTML.
13031         // so we have to trash and replace the previous code.
13032         if (Roo.XComponent.build_from_html) {
13033             // remove this element....
13034             var e = this.el.dom, k=0;
13035             while (e ) { e = e.previousSibling;  ++k;}
13036
13037             this.el.remove();
13038             
13039             this.el=false;
13040             this.rendered = false;
13041             
13042             this.render(this.parent().getChildContainer(true), k);
13043         }
13044         
13045         if(Roo.isIOS && this.useNativeIOS){
13046             this.initIOSView();
13047             return;
13048         }
13049         
13050         /*
13051          * Touch Devices
13052          */
13053         
13054         if(Roo.isTouch && this.mobileTouchView){
13055             this.initTouchView();
13056             return;
13057         }
13058         
13059         if(this.tickable){
13060             this.initTickableEvents();
13061             return;
13062         }
13063         
13064         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13065         
13066         if(this.hiddenName){
13067             
13068             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13069             
13070             this.hiddenField.dom.value =
13071                 this.hiddenValue !== undefined ? this.hiddenValue :
13072                 this.value !== undefined ? this.value : '';
13073
13074             // prevent input submission
13075             this.el.dom.removeAttribute('name');
13076             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13077              
13078              
13079         }
13080         //if(Roo.isGecko){
13081         //    this.el.dom.setAttribute('autocomplete', 'off');
13082         //}
13083         
13084         var cls = 'x-combo-list';
13085         
13086         //this.list = new Roo.Layer({
13087         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13088         //});
13089         
13090         var _this = this;
13091         
13092         (function(){
13093             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13094             _this.list.setWidth(lw);
13095         }).defer(100);
13096         
13097         this.list.on('mouseover', this.onViewOver, this);
13098         this.list.on('mousemove', this.onViewMove, this);
13099         this.list.on('scroll', this.onViewScroll, this);
13100         
13101         /*
13102         this.list.swallowEvent('mousewheel');
13103         this.assetHeight = 0;
13104
13105         if(this.title){
13106             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13107             this.assetHeight += this.header.getHeight();
13108         }
13109
13110         this.innerList = this.list.createChild({cls:cls+'-inner'});
13111         this.innerList.on('mouseover', this.onViewOver, this);
13112         this.innerList.on('mousemove', this.onViewMove, this);
13113         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13114         
13115         if(this.allowBlank && !this.pageSize && !this.disableClear){
13116             this.footer = this.list.createChild({cls:cls+'-ft'});
13117             this.pageTb = new Roo.Toolbar(this.footer);
13118            
13119         }
13120         if(this.pageSize){
13121             this.footer = this.list.createChild({cls:cls+'-ft'});
13122             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13123                     {pageSize: this.pageSize});
13124             
13125         }
13126         
13127         if (this.pageTb && this.allowBlank && !this.disableClear) {
13128             var _this = this;
13129             this.pageTb.add(new Roo.Toolbar.Fill(), {
13130                 cls: 'x-btn-icon x-btn-clear',
13131                 text: '&#160;',
13132                 handler: function()
13133                 {
13134                     _this.collapse();
13135                     _this.clearValue();
13136                     _this.onSelect(false, -1);
13137                 }
13138             });
13139         }
13140         if (this.footer) {
13141             this.assetHeight += this.footer.getHeight();
13142         }
13143         */
13144             
13145         if(!this.tpl){
13146             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13147         }
13148
13149         this.view = new Roo.View(this.list, this.tpl, {
13150             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13151         });
13152         //this.view.wrapEl.setDisplayed(false);
13153         this.view.on('click', this.onViewClick, this);
13154         
13155         
13156         this.store.on('beforeload', this.onBeforeLoad, this);
13157         this.store.on('load', this.onLoad, this);
13158         this.store.on('loadexception', this.onLoadException, this);
13159         /*
13160         if(this.resizable){
13161             this.resizer = new Roo.Resizable(this.list,  {
13162                pinned:true, handles:'se'
13163             });
13164             this.resizer.on('resize', function(r, w, h){
13165                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13166                 this.listWidth = w;
13167                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13168                 this.restrictHeight();
13169             }, this);
13170             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13171         }
13172         */
13173         if(!this.editable){
13174             this.editable = true;
13175             this.setEditable(false);
13176         }
13177         
13178         /*
13179         
13180         if (typeof(this.events.add.listeners) != 'undefined') {
13181             
13182             this.addicon = this.wrap.createChild(
13183                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13184        
13185             this.addicon.on('click', function(e) {
13186                 this.fireEvent('add', this);
13187             }, this);
13188         }
13189         if (typeof(this.events.edit.listeners) != 'undefined') {
13190             
13191             this.editicon = this.wrap.createChild(
13192                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13193             if (this.addicon) {
13194                 this.editicon.setStyle('margin-left', '40px');
13195             }
13196             this.editicon.on('click', function(e) {
13197                 
13198                 // we fire even  if inothing is selected..
13199                 this.fireEvent('edit', this, this.lastData );
13200                 
13201             }, this);
13202         }
13203         */
13204         
13205         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13206             "up" : function(e){
13207                 this.inKeyMode = true;
13208                 this.selectPrev();
13209             },
13210
13211             "down" : function(e){
13212                 if(!this.isExpanded()){
13213                     this.onTriggerClick();
13214                 }else{
13215                     this.inKeyMode = true;
13216                     this.selectNext();
13217                 }
13218             },
13219
13220             "enter" : function(e){
13221 //                this.onViewClick();
13222                 //return true;
13223                 this.collapse();
13224                 
13225                 if(this.fireEvent("specialkey", this, e)){
13226                     this.onViewClick(false);
13227                 }
13228                 
13229                 return true;
13230             },
13231
13232             "esc" : function(e){
13233                 this.collapse();
13234             },
13235
13236             "tab" : function(e){
13237                 this.collapse();
13238                 
13239                 if(this.fireEvent("specialkey", this, e)){
13240                     this.onViewClick(false);
13241                 }
13242                 
13243                 return true;
13244             },
13245
13246             scope : this,
13247
13248             doRelay : function(foo, bar, hname){
13249                 if(hname == 'down' || this.scope.isExpanded()){
13250                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13251                 }
13252                 return true;
13253             },
13254
13255             forceKeyDown: true
13256         });
13257         
13258         
13259         this.queryDelay = Math.max(this.queryDelay || 10,
13260                 this.mode == 'local' ? 10 : 250);
13261         
13262         
13263         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13264         
13265         if(this.typeAhead){
13266             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13267         }
13268         if(this.editable !== false){
13269             this.inputEl().on("keyup", this.onKeyUp, this);
13270         }
13271         if(this.forceSelection){
13272             this.inputEl().on('blur', this.doForce, this);
13273         }
13274         
13275         if(this.multiple){
13276             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13277             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13278         }
13279     },
13280     
13281     initTickableEvents: function()
13282     {   
13283         this.createList();
13284         
13285         if(this.hiddenName){
13286             
13287             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13288             
13289             this.hiddenField.dom.value =
13290                 this.hiddenValue !== undefined ? this.hiddenValue :
13291                 this.value !== undefined ? this.value : '';
13292
13293             // prevent input submission
13294             this.el.dom.removeAttribute('name');
13295             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13296              
13297              
13298         }
13299         
13300 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13301         
13302         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13303         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13304         if(this.triggerList){
13305             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13306         }
13307          
13308         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13309         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13310         
13311         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13312         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13313         
13314         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13315         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13316         
13317         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13318         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13319         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13320         
13321         this.okBtn.hide();
13322         this.cancelBtn.hide();
13323         
13324         var _this = this;
13325         
13326         (function(){
13327             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13328             _this.list.setWidth(lw);
13329         }).defer(100);
13330         
13331         this.list.on('mouseover', this.onViewOver, this);
13332         this.list.on('mousemove', this.onViewMove, this);
13333         
13334         this.list.on('scroll', this.onViewScroll, this);
13335         
13336         if(!this.tpl){
13337             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>';
13338         }
13339
13340         this.view = new Roo.View(this.list, this.tpl, {
13341             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13342         });
13343         
13344         //this.view.wrapEl.setDisplayed(false);
13345         this.view.on('click', this.onViewClick, this);
13346         
13347         
13348         
13349         this.store.on('beforeload', this.onBeforeLoad, this);
13350         this.store.on('load', this.onLoad, this);
13351         this.store.on('loadexception', this.onLoadException, this);
13352         
13353         if(this.editable){
13354             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13355                 "up" : function(e){
13356                     this.inKeyMode = true;
13357                     this.selectPrev();
13358                 },
13359
13360                 "down" : function(e){
13361                     this.inKeyMode = true;
13362                     this.selectNext();
13363                 },
13364
13365                 "enter" : function(e){
13366                     if(this.fireEvent("specialkey", this, e)){
13367                         this.onViewClick(false);
13368                     }
13369                     
13370                     return true;
13371                 },
13372
13373                 "esc" : function(e){
13374                     this.onTickableFooterButtonClick(e, false, false);
13375                 },
13376
13377                 "tab" : function(e){
13378                     this.fireEvent("specialkey", this, e);
13379                     
13380                     this.onTickableFooterButtonClick(e, false, false);
13381                     
13382                     return true;
13383                 },
13384
13385                 scope : this,
13386
13387                 doRelay : function(e, fn, key){
13388                     if(this.scope.isExpanded()){
13389                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13390                     }
13391                     return true;
13392                 },
13393
13394                 forceKeyDown: true
13395             });
13396         }
13397         
13398         this.queryDelay = Math.max(this.queryDelay || 10,
13399                 this.mode == 'local' ? 10 : 250);
13400         
13401         
13402         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13403         
13404         if(this.typeAhead){
13405             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13406         }
13407         
13408         if(this.editable !== false){
13409             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13410         }
13411         
13412         this.indicator = this.indicatorEl();
13413         
13414         if(this.indicator){
13415             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13416             this.indicator.hide();
13417         }
13418         
13419     },
13420
13421     onDestroy : function(){
13422         if(this.view){
13423             this.view.setStore(null);
13424             this.view.el.removeAllListeners();
13425             this.view.el.remove();
13426             this.view.purgeListeners();
13427         }
13428         if(this.list){
13429             this.list.dom.innerHTML  = '';
13430         }
13431         
13432         if(this.store){
13433             this.store.un('beforeload', this.onBeforeLoad, this);
13434             this.store.un('load', this.onLoad, this);
13435             this.store.un('loadexception', this.onLoadException, this);
13436         }
13437         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13438     },
13439
13440     // private
13441     fireKey : function(e){
13442         if(e.isNavKeyPress() && !this.list.isVisible()){
13443             this.fireEvent("specialkey", this, e);
13444         }
13445     },
13446
13447     // private
13448     onResize: function(w, h){
13449 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13450 //        
13451 //        if(typeof w != 'number'){
13452 //            // we do not handle it!?!?
13453 //            return;
13454 //        }
13455 //        var tw = this.trigger.getWidth();
13456 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13457 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13458 //        var x = w - tw;
13459 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13460 //            
13461 //        //this.trigger.setStyle('left', x+'px');
13462 //        
13463 //        if(this.list && this.listWidth === undefined){
13464 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13465 //            this.list.setWidth(lw);
13466 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13467 //        }
13468         
13469     
13470         
13471     },
13472
13473     /**
13474      * Allow or prevent the user from directly editing the field text.  If false is passed,
13475      * the user will only be able to select from the items defined in the dropdown list.  This method
13476      * is the runtime equivalent of setting the 'editable' config option at config time.
13477      * @param {Boolean} value True to allow the user to directly edit the field text
13478      */
13479     setEditable : function(value){
13480         if(value == this.editable){
13481             return;
13482         }
13483         this.editable = value;
13484         if(!value){
13485             this.inputEl().dom.setAttribute('readOnly', true);
13486             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13487             this.inputEl().addClass('x-combo-noedit');
13488         }else{
13489             this.inputEl().dom.setAttribute('readOnly', false);
13490             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13491             this.inputEl().removeClass('x-combo-noedit');
13492         }
13493     },
13494
13495     // private
13496     
13497     onBeforeLoad : function(combo,opts){
13498         if(!this.hasFocus){
13499             return;
13500         }
13501          if (!opts.add) {
13502             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13503          }
13504         this.restrictHeight();
13505         this.selectedIndex = -1;
13506     },
13507
13508     // private
13509     onLoad : function(){
13510         
13511         this.hasQuery = false;
13512         
13513         if(!this.hasFocus){
13514             return;
13515         }
13516         
13517         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13518             this.loading.hide();
13519         }
13520         
13521         if(this.store.getCount() > 0){
13522             
13523             this.expand();
13524             this.restrictHeight();
13525             if(this.lastQuery == this.allQuery){
13526                 if(this.editable && !this.tickable){
13527                     this.inputEl().dom.select();
13528                 }
13529                 
13530                 if(
13531                     !this.selectByValue(this.value, true) &&
13532                     this.autoFocus && 
13533                     (
13534                         !this.store.lastOptions ||
13535                         typeof(this.store.lastOptions.add) == 'undefined' || 
13536                         this.store.lastOptions.add != true
13537                     )
13538                 ){
13539                     this.select(0, true);
13540                 }
13541             }else{
13542                 if(this.autoFocus){
13543                     this.selectNext();
13544                 }
13545                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13546                     this.taTask.delay(this.typeAheadDelay);
13547                 }
13548             }
13549         }else{
13550             this.onEmptyResults();
13551         }
13552         
13553         //this.el.focus();
13554     },
13555     // private
13556     onLoadException : function()
13557     {
13558         this.hasQuery = false;
13559         
13560         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13561             this.loading.hide();
13562         }
13563         
13564         if(this.tickable && this.editable){
13565             return;
13566         }
13567         
13568         this.collapse();
13569         // only causes errors at present
13570         //Roo.log(this.store.reader.jsonData);
13571         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13572             // fixme
13573             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13574         //}
13575         
13576         
13577     },
13578     // private
13579     onTypeAhead : function(){
13580         if(this.store.getCount() > 0){
13581             var r = this.store.getAt(0);
13582             var newValue = r.data[this.displayField];
13583             var len = newValue.length;
13584             var selStart = this.getRawValue().length;
13585             
13586             if(selStart != len){
13587                 this.setRawValue(newValue);
13588                 this.selectText(selStart, newValue.length);
13589             }
13590         }
13591     },
13592
13593     // private
13594     onSelect : function(record, index){
13595         
13596         if(this.fireEvent('beforeselect', this, record, index) !== false){
13597         
13598             this.setFromData(index > -1 ? record.data : false);
13599             
13600             this.collapse();
13601             this.fireEvent('select', this, record, index);
13602         }
13603     },
13604
13605     /**
13606      * Returns the currently selected field value or empty string if no value is set.
13607      * @return {String} value The selected value
13608      */
13609     getValue : function()
13610     {
13611         if(Roo.isIOS && this.useNativeIOS){
13612             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13613         }
13614         
13615         if(this.multiple){
13616             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13617         }
13618         
13619         if(this.valueField){
13620             return typeof this.value != 'undefined' ? this.value : '';
13621         }else{
13622             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13623         }
13624     },
13625     
13626     getRawValue : function()
13627     {
13628         if(Roo.isIOS && this.useNativeIOS){
13629             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13630         }
13631         
13632         var v = this.inputEl().getValue();
13633         
13634         return v;
13635     },
13636
13637     /**
13638      * Clears any text/value currently set in the field
13639      */
13640     clearValue : function(){
13641         
13642         if(this.hiddenField){
13643             this.hiddenField.dom.value = '';
13644         }
13645         this.value = '';
13646         this.setRawValue('');
13647         this.lastSelectionText = '';
13648         this.lastData = false;
13649         
13650         var close = this.closeTriggerEl();
13651         
13652         if(close){
13653             close.hide();
13654         }
13655         
13656         this.validate();
13657         
13658     },
13659
13660     /**
13661      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13662      * will be displayed in the field.  If the value does not match the data value of an existing item,
13663      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13664      * Otherwise the field will be blank (although the value will still be set).
13665      * @param {String} value The value to match
13666      */
13667     setValue : function(v)
13668     {
13669         if(Roo.isIOS && this.useNativeIOS){
13670             this.setIOSValue(v);
13671             return;
13672         }
13673         
13674         if(this.multiple){
13675             this.syncValue();
13676             return;
13677         }
13678         
13679         var text = v;
13680         if(this.valueField){
13681             var r = this.findRecord(this.valueField, v);
13682             if(r){
13683                 text = r.data[this.displayField];
13684             }else if(this.valueNotFoundText !== undefined){
13685                 text = this.valueNotFoundText;
13686             }
13687         }
13688         this.lastSelectionText = text;
13689         if(this.hiddenField){
13690             this.hiddenField.dom.value = v;
13691         }
13692         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13693         this.value = v;
13694         
13695         var close = this.closeTriggerEl();
13696         
13697         if(close){
13698             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13699         }
13700         
13701         this.validate();
13702     },
13703     /**
13704      * @property {Object} the last set data for the element
13705      */
13706     
13707     lastData : false,
13708     /**
13709      * Sets the value of the field based on a object which is related to the record format for the store.
13710      * @param {Object} value the value to set as. or false on reset?
13711      */
13712     setFromData : function(o){
13713         
13714         if(this.multiple){
13715             this.addItem(o);
13716             return;
13717         }
13718             
13719         var dv = ''; // display value
13720         var vv = ''; // value value..
13721         this.lastData = o;
13722         if (this.displayField) {
13723             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13724         } else {
13725             // this is an error condition!!!
13726             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13727         }
13728         
13729         if(this.valueField){
13730             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13731         }
13732         
13733         var close = this.closeTriggerEl();
13734         
13735         if(close){
13736             if(dv.length || vv * 1 > 0){
13737                 close.show() ;
13738                 this.blockFocus=true;
13739             } else {
13740                 close.hide();
13741             }             
13742         }
13743         
13744         if(this.hiddenField){
13745             this.hiddenField.dom.value = vv;
13746             
13747             this.lastSelectionText = dv;
13748             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13749             this.value = vv;
13750             return;
13751         }
13752         // no hidden field.. - we store the value in 'value', but still display
13753         // display field!!!!
13754         this.lastSelectionText = dv;
13755         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13756         this.value = vv;
13757         
13758         
13759         
13760     },
13761     // private
13762     reset : function(){
13763         // overridden so that last data is reset..
13764         
13765         if(this.multiple){
13766             this.clearItem();
13767             return;
13768         }
13769         
13770         this.setValue(this.originalValue);
13771         //this.clearInvalid();
13772         this.lastData = false;
13773         if (this.view) {
13774             this.view.clearSelections();
13775         }
13776         
13777         this.validate();
13778     },
13779     // private
13780     findRecord : function(prop, value){
13781         var record;
13782         if(this.store.getCount() > 0){
13783             this.store.each(function(r){
13784                 if(r.data[prop] == value){
13785                     record = r;
13786                     return false;
13787                 }
13788                 return true;
13789             });
13790         }
13791         return record;
13792     },
13793     
13794     getName: function()
13795     {
13796         // returns hidden if it's set..
13797         if (!this.rendered) {return ''};
13798         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13799         
13800     },
13801     // private
13802     onViewMove : function(e, t){
13803         this.inKeyMode = false;
13804     },
13805
13806     // private
13807     onViewOver : function(e, t){
13808         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13809             return;
13810         }
13811         var item = this.view.findItemFromChild(t);
13812         
13813         if(item){
13814             var index = this.view.indexOf(item);
13815             this.select(index, false);
13816         }
13817     },
13818
13819     // private
13820     onViewClick : function(view, doFocus, el, e)
13821     {
13822         var index = this.view.getSelectedIndexes()[0];
13823         
13824         var r = this.store.getAt(index);
13825         
13826         if(this.tickable){
13827             
13828             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13829                 return;
13830             }
13831             
13832             var rm = false;
13833             var _this = this;
13834             
13835             Roo.each(this.tickItems, function(v,k){
13836                 
13837                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13838                     Roo.log(v);
13839                     _this.tickItems.splice(k, 1);
13840                     
13841                     if(typeof(e) == 'undefined' && view == false){
13842                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13843                     }
13844                     
13845                     rm = true;
13846                     return;
13847                 }
13848             });
13849             
13850             if(rm){
13851                 return;
13852             }
13853             
13854             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13855                 this.tickItems.push(r.data);
13856             }
13857             
13858             if(typeof(e) == 'undefined' && view == false){
13859                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13860             }
13861                     
13862             return;
13863         }
13864         
13865         if(r){
13866             this.onSelect(r, index);
13867         }
13868         if(doFocus !== false && !this.blockFocus){
13869             this.inputEl().focus();
13870         }
13871     },
13872
13873     // private
13874     restrictHeight : function(){
13875         //this.innerList.dom.style.height = '';
13876         //var inner = this.innerList.dom;
13877         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13878         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13879         //this.list.beginUpdate();
13880         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13881         this.list.alignTo(this.inputEl(), this.listAlign);
13882         this.list.alignTo(this.inputEl(), this.listAlign);
13883         //this.list.endUpdate();
13884     },
13885
13886     // private
13887     onEmptyResults : function(){
13888         
13889         if(this.tickable && this.editable){
13890             this.restrictHeight();
13891             return;
13892         }
13893         
13894         this.collapse();
13895     },
13896
13897     /**
13898      * Returns true if the dropdown list is expanded, else false.
13899      */
13900     isExpanded : function(){
13901         return this.list.isVisible();
13902     },
13903
13904     /**
13905      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13906      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13907      * @param {String} value The data value of the item to select
13908      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13909      * selected item if it is not currently in view (defaults to true)
13910      * @return {Boolean} True if the value matched an item in the list, else false
13911      */
13912     selectByValue : function(v, scrollIntoView){
13913         if(v !== undefined && v !== null){
13914             var r = this.findRecord(this.valueField || this.displayField, v);
13915             if(r){
13916                 this.select(this.store.indexOf(r), scrollIntoView);
13917                 return true;
13918             }
13919         }
13920         return false;
13921     },
13922
13923     /**
13924      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13925      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13926      * @param {Number} index The zero-based index of the list item to select
13927      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13928      * selected item if it is not currently in view (defaults to true)
13929      */
13930     select : function(index, scrollIntoView){
13931         this.selectedIndex = index;
13932         this.view.select(index);
13933         if(scrollIntoView !== false){
13934             var el = this.view.getNode(index);
13935             /*
13936              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13937              */
13938             if(el){
13939                 this.list.scrollChildIntoView(el, false);
13940             }
13941         }
13942     },
13943
13944     // private
13945     selectNext : function(){
13946         var ct = this.store.getCount();
13947         if(ct > 0){
13948             if(this.selectedIndex == -1){
13949                 this.select(0);
13950             }else if(this.selectedIndex < ct-1){
13951                 this.select(this.selectedIndex+1);
13952             }
13953         }
13954     },
13955
13956     // private
13957     selectPrev : function(){
13958         var ct = this.store.getCount();
13959         if(ct > 0){
13960             if(this.selectedIndex == -1){
13961                 this.select(0);
13962             }else if(this.selectedIndex != 0){
13963                 this.select(this.selectedIndex-1);
13964             }
13965         }
13966     },
13967
13968     // private
13969     onKeyUp : function(e){
13970         if(this.editable !== false && !e.isSpecialKey()){
13971             this.lastKey = e.getKey();
13972             this.dqTask.delay(this.queryDelay);
13973         }
13974     },
13975
13976     // private
13977     validateBlur : function(){
13978         return !this.list || !this.list.isVisible();   
13979     },
13980
13981     // private
13982     initQuery : function(){
13983         
13984         var v = this.getRawValue();
13985         
13986         if(this.tickable && this.editable){
13987             v = this.tickableInputEl().getValue();
13988         }
13989         
13990         this.doQuery(v);
13991     },
13992
13993     // private
13994     doForce : function(){
13995         if(this.inputEl().dom.value.length > 0){
13996             this.inputEl().dom.value =
13997                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13998              
13999         }
14000     },
14001
14002     /**
14003      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14004      * query allowing the query action to be canceled if needed.
14005      * @param {String} query The SQL query to execute
14006      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14007      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14008      * saved in the current store (defaults to false)
14009      */
14010     doQuery : function(q, forceAll){
14011         
14012         if(q === undefined || q === null){
14013             q = '';
14014         }
14015         var qe = {
14016             query: q,
14017             forceAll: forceAll,
14018             combo: this,
14019             cancel:false
14020         };
14021         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14022             return false;
14023         }
14024         q = qe.query;
14025         
14026         forceAll = qe.forceAll;
14027         if(forceAll === true || (q.length >= this.minChars)){
14028             
14029             this.hasQuery = true;
14030             
14031             if(this.lastQuery != q || this.alwaysQuery){
14032                 this.lastQuery = q;
14033                 if(this.mode == 'local'){
14034                     this.selectedIndex = -1;
14035                     if(forceAll){
14036                         this.store.clearFilter();
14037                     }else{
14038                         
14039                         if(this.specialFilter){
14040                             this.fireEvent('specialfilter', this);
14041                             this.onLoad();
14042                             return;
14043                         }
14044                         
14045                         this.store.filter(this.displayField, q);
14046                     }
14047                     
14048                     this.store.fireEvent("datachanged", this.store);
14049                     
14050                     this.onLoad();
14051                     
14052                     
14053                 }else{
14054                     
14055                     this.store.baseParams[this.queryParam] = q;
14056                     
14057                     var options = {params : this.getParams(q)};
14058                     
14059                     if(this.loadNext){
14060                         options.add = true;
14061                         options.params.start = this.page * this.pageSize;
14062                     }
14063                     
14064                     this.store.load(options);
14065                     
14066                     /*
14067                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14068                      *  we should expand the list on onLoad
14069                      *  so command out it
14070                      */
14071 //                    this.expand();
14072                 }
14073             }else{
14074                 this.selectedIndex = -1;
14075                 this.onLoad();   
14076             }
14077         }
14078         
14079         this.loadNext = false;
14080     },
14081     
14082     // private
14083     getParams : function(q){
14084         var p = {};
14085         //p[this.queryParam] = q;
14086         
14087         if(this.pageSize){
14088             p.start = 0;
14089             p.limit = this.pageSize;
14090         }
14091         return p;
14092     },
14093
14094     /**
14095      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14096      */
14097     collapse : function(){
14098         if(!this.isExpanded()){
14099             return;
14100         }
14101         
14102         this.list.hide();
14103         
14104         this.hasFocus = false;
14105         
14106         if(this.tickable){
14107             this.okBtn.hide();
14108             this.cancelBtn.hide();
14109             this.trigger.show();
14110             
14111             if(this.editable){
14112                 this.tickableInputEl().dom.value = '';
14113                 this.tickableInputEl().blur();
14114             }
14115             
14116         }
14117         
14118         Roo.get(document).un('mousedown', this.collapseIf, this);
14119         Roo.get(document).un('mousewheel', this.collapseIf, this);
14120         if (!this.editable) {
14121             Roo.get(document).un('keydown', this.listKeyPress, this);
14122         }
14123         this.fireEvent('collapse', this);
14124         
14125         this.validate();
14126     },
14127
14128     // private
14129     collapseIf : function(e){
14130         var in_combo  = e.within(this.el);
14131         var in_list =  e.within(this.list);
14132         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14133         
14134         if (in_combo || in_list || is_list) {
14135             //e.stopPropagation();
14136             return;
14137         }
14138         
14139         if(this.tickable){
14140             this.onTickableFooterButtonClick(e, false, false);
14141         }
14142
14143         this.collapse();
14144         
14145     },
14146
14147     /**
14148      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14149      */
14150     expand : function(){
14151        
14152         if(this.isExpanded() || !this.hasFocus){
14153             return;
14154         }
14155         
14156         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14157         this.list.setWidth(lw);
14158         
14159         Roo.log('expand');
14160         
14161         this.list.show();
14162         
14163         this.restrictHeight();
14164         
14165         if(this.tickable){
14166             
14167             this.tickItems = Roo.apply([], this.item);
14168             
14169             this.okBtn.show();
14170             this.cancelBtn.show();
14171             this.trigger.hide();
14172             
14173             if(this.editable){
14174                 this.tickableInputEl().focus();
14175             }
14176             
14177         }
14178         
14179         Roo.get(document).on('mousedown', this.collapseIf, this);
14180         Roo.get(document).on('mousewheel', this.collapseIf, this);
14181         if (!this.editable) {
14182             Roo.get(document).on('keydown', this.listKeyPress, this);
14183         }
14184         
14185         this.fireEvent('expand', this);
14186     },
14187
14188     // private
14189     // Implements the default empty TriggerField.onTriggerClick function
14190     onTriggerClick : function(e)
14191     {
14192         Roo.log('trigger click');
14193         
14194         if(this.disabled || !this.triggerList){
14195             return;
14196         }
14197         
14198         this.page = 0;
14199         this.loadNext = false;
14200         
14201         if(this.isExpanded()){
14202             this.collapse();
14203             if (!this.blockFocus) {
14204                 this.inputEl().focus();
14205             }
14206             
14207         }else {
14208             this.hasFocus = true;
14209             if(this.triggerAction == 'all') {
14210                 this.doQuery(this.allQuery, true);
14211             } else {
14212                 this.doQuery(this.getRawValue());
14213             }
14214             if (!this.blockFocus) {
14215                 this.inputEl().focus();
14216             }
14217         }
14218     },
14219     
14220     onTickableTriggerClick : function(e)
14221     {
14222         if(this.disabled){
14223             return;
14224         }
14225         
14226         this.page = 0;
14227         this.loadNext = false;
14228         this.hasFocus = true;
14229         
14230         if(this.triggerAction == 'all') {
14231             this.doQuery(this.allQuery, true);
14232         } else {
14233             this.doQuery(this.getRawValue());
14234         }
14235     },
14236     
14237     onSearchFieldClick : function(e)
14238     {
14239         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14240             this.onTickableFooterButtonClick(e, false, false);
14241             return;
14242         }
14243         
14244         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14245             return;
14246         }
14247         
14248         this.page = 0;
14249         this.loadNext = false;
14250         this.hasFocus = true;
14251         
14252         if(this.triggerAction == 'all') {
14253             this.doQuery(this.allQuery, true);
14254         } else {
14255             this.doQuery(this.getRawValue());
14256         }
14257     },
14258     
14259     listKeyPress : function(e)
14260     {
14261         //Roo.log('listkeypress');
14262         // scroll to first matching element based on key pres..
14263         if (e.isSpecialKey()) {
14264             return false;
14265         }
14266         var k = String.fromCharCode(e.getKey()).toUpperCase();
14267         //Roo.log(k);
14268         var match  = false;
14269         var csel = this.view.getSelectedNodes();
14270         var cselitem = false;
14271         if (csel.length) {
14272             var ix = this.view.indexOf(csel[0]);
14273             cselitem  = this.store.getAt(ix);
14274             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14275                 cselitem = false;
14276             }
14277             
14278         }
14279         
14280         this.store.each(function(v) { 
14281             if (cselitem) {
14282                 // start at existing selection.
14283                 if (cselitem.id == v.id) {
14284                     cselitem = false;
14285                 }
14286                 return true;
14287             }
14288                 
14289             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14290                 match = this.store.indexOf(v);
14291                 return false;
14292             }
14293             return true;
14294         }, this);
14295         
14296         if (match === false) {
14297             return true; // no more action?
14298         }
14299         // scroll to?
14300         this.view.select(match);
14301         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14302         sn.scrollIntoView(sn.dom.parentNode, false);
14303     },
14304     
14305     onViewScroll : function(e, t){
14306         
14307         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){
14308             return;
14309         }
14310         
14311         this.hasQuery = true;
14312         
14313         this.loading = this.list.select('.loading', true).first();
14314         
14315         if(this.loading === null){
14316             this.list.createChild({
14317                 tag: 'div',
14318                 cls: 'loading roo-select2-more-results roo-select2-active',
14319                 html: 'Loading more results...'
14320             });
14321             
14322             this.loading = this.list.select('.loading', true).first();
14323             
14324             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14325             
14326             this.loading.hide();
14327         }
14328         
14329         this.loading.show();
14330         
14331         var _combo = this;
14332         
14333         this.page++;
14334         this.loadNext = true;
14335         
14336         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14337         
14338         return;
14339     },
14340     
14341     addItem : function(o)
14342     {   
14343         var dv = ''; // display value
14344         
14345         if (this.displayField) {
14346             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14347         } else {
14348             // this is an error condition!!!
14349             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14350         }
14351         
14352         if(!dv.length){
14353             return;
14354         }
14355         
14356         var choice = this.choices.createChild({
14357             tag: 'li',
14358             cls: 'roo-select2-search-choice',
14359             cn: [
14360                 {
14361                     tag: 'div',
14362                     html: dv
14363                 },
14364                 {
14365                     tag: 'a',
14366                     href: '#',
14367                     cls: 'roo-select2-search-choice-close fa fa-times',
14368                     tabindex: '-1'
14369                 }
14370             ]
14371             
14372         }, this.searchField);
14373         
14374         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14375         
14376         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14377         
14378         this.item.push(o);
14379         
14380         this.lastData = o;
14381         
14382         this.syncValue();
14383         
14384         this.inputEl().dom.value = '';
14385         
14386         this.validate();
14387     },
14388     
14389     onRemoveItem : function(e, _self, o)
14390     {
14391         e.preventDefault();
14392         
14393         this.lastItem = Roo.apply([], this.item);
14394         
14395         var index = this.item.indexOf(o.data) * 1;
14396         
14397         if( index < 0){
14398             Roo.log('not this item?!');
14399             return;
14400         }
14401         
14402         this.item.splice(index, 1);
14403         o.item.remove();
14404         
14405         this.syncValue();
14406         
14407         this.fireEvent('remove', this, e);
14408         
14409         this.validate();
14410         
14411     },
14412     
14413     syncValue : function()
14414     {
14415         if(!this.item.length){
14416             this.clearValue();
14417             return;
14418         }
14419             
14420         var value = [];
14421         var _this = this;
14422         Roo.each(this.item, function(i){
14423             if(_this.valueField){
14424                 value.push(i[_this.valueField]);
14425                 return;
14426             }
14427
14428             value.push(i);
14429         });
14430
14431         this.value = value.join(',');
14432
14433         if(this.hiddenField){
14434             this.hiddenField.dom.value = this.value;
14435         }
14436         
14437         this.store.fireEvent("datachanged", this.store);
14438         
14439         this.validate();
14440     },
14441     
14442     clearItem : function()
14443     {
14444         if(!this.multiple){
14445             return;
14446         }
14447         
14448         this.item = [];
14449         
14450         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14451            c.remove();
14452         });
14453         
14454         this.syncValue();
14455         
14456         this.validate();
14457         
14458         if(this.tickable && !Roo.isTouch){
14459             this.view.refresh();
14460         }
14461     },
14462     
14463     inputEl: function ()
14464     {
14465         if(Roo.isIOS && this.useNativeIOS){
14466             return this.el.select('select.roo-ios-select', true).first();
14467         }
14468         
14469         if(Roo.isTouch && this.mobileTouchView){
14470             return this.el.select('input.form-control',true).first();
14471         }
14472         
14473         if(this.tickable){
14474             return this.searchField;
14475         }
14476         
14477         return this.el.select('input.form-control',true).first();
14478     },
14479     
14480     onTickableFooterButtonClick : function(e, btn, el)
14481     {
14482         e.preventDefault();
14483         
14484         this.lastItem = Roo.apply([], this.item);
14485         
14486         if(btn && btn.name == 'cancel'){
14487             this.tickItems = Roo.apply([], this.item);
14488             this.collapse();
14489             return;
14490         }
14491         
14492         this.clearItem();
14493         
14494         var _this = this;
14495         
14496         Roo.each(this.tickItems, function(o){
14497             _this.addItem(o);
14498         });
14499         
14500         this.collapse();
14501         
14502     },
14503     
14504     validate : function()
14505     {
14506         var v = this.getRawValue();
14507         
14508         if(this.multiple){
14509             v = this.getValue();
14510         }
14511         
14512         if(this.disabled || this.allowBlank || v.length){
14513             this.markValid();
14514             return true;
14515         }
14516         
14517         this.markInvalid();
14518         return false;
14519     },
14520     
14521     tickableInputEl : function()
14522     {
14523         if(!this.tickable || !this.editable){
14524             return this.inputEl();
14525         }
14526         
14527         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14528     },
14529     
14530     
14531     getAutoCreateTouchView : function()
14532     {
14533         var id = Roo.id();
14534         
14535         var cfg = {
14536             cls: 'form-group' //input-group
14537         };
14538         
14539         var input =  {
14540             tag: 'input',
14541             id : id,
14542             type : this.inputType,
14543             cls : 'form-control x-combo-noedit',
14544             autocomplete: 'new-password',
14545             placeholder : this.placeholder || '',
14546             readonly : true
14547         };
14548         
14549         if (this.name) {
14550             input.name = this.name;
14551         }
14552         
14553         if (this.size) {
14554             input.cls += ' input-' + this.size;
14555         }
14556         
14557         if (this.disabled) {
14558             input.disabled = true;
14559         }
14560         
14561         var inputblock = {
14562             cls : '',
14563             cn : [
14564                 input
14565             ]
14566         };
14567         
14568         if(this.before){
14569             inputblock.cls += ' input-group';
14570             
14571             inputblock.cn.unshift({
14572                 tag :'span',
14573                 cls : 'input-group-addon',
14574                 html : this.before
14575             });
14576         }
14577         
14578         if(this.removable && !this.multiple){
14579             inputblock.cls += ' roo-removable';
14580             
14581             inputblock.cn.push({
14582                 tag: 'button',
14583                 html : 'x',
14584                 cls : 'roo-combo-removable-btn close'
14585             });
14586         }
14587
14588         if(this.hasFeedback && !this.allowBlank){
14589             
14590             inputblock.cls += ' has-feedback';
14591             
14592             inputblock.cn.push({
14593                 tag: 'span',
14594                 cls: 'glyphicon form-control-feedback'
14595             });
14596             
14597         }
14598         
14599         if (this.after) {
14600             
14601             inputblock.cls += (this.before) ? '' : ' input-group';
14602             
14603             inputblock.cn.push({
14604                 tag :'span',
14605                 cls : 'input-group-addon',
14606                 html : this.after
14607             });
14608         }
14609
14610         var box = {
14611             tag: 'div',
14612             cn: [
14613                 {
14614                     tag: 'input',
14615                     type : 'hidden',
14616                     cls: 'form-hidden-field'
14617                 },
14618                 inputblock
14619             ]
14620             
14621         };
14622         
14623         if(this.multiple){
14624             box = {
14625                 tag: 'div',
14626                 cn: [
14627                     {
14628                         tag: 'input',
14629                         type : 'hidden',
14630                         cls: 'form-hidden-field'
14631                     },
14632                     {
14633                         tag: 'ul',
14634                         cls: 'roo-select2-choices',
14635                         cn:[
14636                             {
14637                                 tag: 'li',
14638                                 cls: 'roo-select2-search-field',
14639                                 cn: [
14640
14641                                     inputblock
14642                                 ]
14643                             }
14644                         ]
14645                     }
14646                 ]
14647             }
14648         };
14649         
14650         var combobox = {
14651             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14652             cn: [
14653                 box
14654             ]
14655         };
14656         
14657         if(!this.multiple && this.showToggleBtn){
14658             
14659             var caret = {
14660                         tag: 'span',
14661                         cls: 'caret'
14662             };
14663             
14664             if (this.caret != false) {
14665                 caret = {
14666                      tag: 'i',
14667                      cls: 'fa fa-' + this.caret
14668                 };
14669                 
14670             }
14671             
14672             combobox.cn.push({
14673                 tag :'span',
14674                 cls : 'input-group-addon btn dropdown-toggle',
14675                 cn : [
14676                     caret,
14677                     {
14678                         tag: 'span',
14679                         cls: 'combobox-clear',
14680                         cn  : [
14681                             {
14682                                 tag : 'i',
14683                                 cls: 'icon-remove'
14684                             }
14685                         ]
14686                     }
14687                 ]
14688
14689             })
14690         }
14691         
14692         if(this.multiple){
14693             combobox.cls += ' roo-select2-container-multi';
14694         }
14695         
14696         var align = this.labelAlign || this.parentLabelAlign();
14697         
14698         if (align ==='left' && this.fieldLabel.length) {
14699
14700             cfg.cn = [
14701                 {
14702                    tag : 'i',
14703                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14704                    tooltip : 'This field is required'
14705                 },
14706                 {
14707                     tag: 'label',
14708                     cls : 'control-label',
14709                     html : this.fieldLabel
14710
14711                 },
14712                 {
14713                     cls : '', 
14714                     cn: [
14715                         combobox
14716                     ]
14717                 }
14718             ];
14719             
14720             var labelCfg = cfg.cn[1];
14721             var contentCfg = cfg.cn[2];
14722             
14723
14724             if(this.indicatorpos == 'right'){
14725                 cfg.cn = [
14726                     {
14727                         tag: 'label',
14728                         'for' :  id,
14729                         cls : 'control-label',
14730                         cn : [
14731                             {
14732                                 tag : 'span',
14733                                 html : this.fieldLabel
14734                             },
14735                             {
14736                                 tag : 'i',
14737                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14738                                 tooltip : 'This field is required'
14739                             }
14740                         ]
14741                     },
14742                     {
14743                         cls : "",
14744                         cn: [
14745                             combobox
14746                         ]
14747                     }
14748
14749                 ];
14750                 
14751                 labelCfg = cfg.cn[0];
14752                 contentCfg = cfg.cn[1];
14753             }
14754             
14755            
14756             
14757             if(this.labelWidth > 12){
14758                 labelCfg.style = "width: " + this.labelWidth + 'px';
14759             }
14760             
14761             if(this.labelWidth < 13 && this.labelmd == 0){
14762                 this.labelmd = this.labelWidth;
14763             }
14764             
14765             if(this.labellg > 0){
14766                 labelCfg.cls += ' col-lg-' + this.labellg;
14767                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14768             }
14769             
14770             if(this.labelmd > 0){
14771                 labelCfg.cls += ' col-md-' + this.labelmd;
14772                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14773             }
14774             
14775             if(this.labelsm > 0){
14776                 labelCfg.cls += ' col-sm-' + this.labelsm;
14777                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14778             }
14779             
14780             if(this.labelxs > 0){
14781                 labelCfg.cls += ' col-xs-' + this.labelxs;
14782                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14783             }
14784                 
14785                 
14786         } else if ( this.fieldLabel.length) {
14787             cfg.cn = [
14788                 {
14789                    tag : 'i',
14790                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14791                    tooltip : 'This field is required'
14792                 },
14793                 {
14794                     tag: 'label',
14795                     cls : 'control-label',
14796                     html : this.fieldLabel
14797
14798                 },
14799                 {
14800                     cls : '', 
14801                     cn: [
14802                         combobox
14803                     ]
14804                 }
14805             ];
14806             
14807             if(this.indicatorpos == 'right'){
14808                 cfg.cn = [
14809                     {
14810                         tag: 'label',
14811                         cls : 'control-label',
14812                         html : this.fieldLabel,
14813                         cn : [
14814                             {
14815                                tag : 'i',
14816                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14817                                tooltip : 'This field is required'
14818                             }
14819                         ]
14820                     },
14821                     {
14822                         cls : '', 
14823                         cn: [
14824                             combobox
14825                         ]
14826                     }
14827                 ];
14828             }
14829         } else {
14830             cfg.cn = combobox;    
14831         }
14832         
14833         
14834         var settings = this;
14835         
14836         ['xs','sm','md','lg'].map(function(size){
14837             if (settings[size]) {
14838                 cfg.cls += ' col-' + size + '-' + settings[size];
14839             }
14840         });
14841         
14842         return cfg;
14843     },
14844     
14845     initTouchView : function()
14846     {
14847         this.renderTouchView();
14848         
14849         this.touchViewEl.on('scroll', function(){
14850             this.el.dom.scrollTop = 0;
14851         }, this);
14852         
14853         this.originalValue = this.getValue();
14854         
14855         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14856         
14857         this.inputEl().on("click", this.showTouchView, this);
14858         if (this.triggerEl) {
14859             this.triggerEl.on("click", this.showTouchView, this);
14860         }
14861         
14862         
14863         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14864         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14865         
14866         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14867         
14868         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14869         this.store.on('load', this.onTouchViewLoad, this);
14870         this.store.on('loadexception', this.onTouchViewLoadException, this);
14871         
14872         if(this.hiddenName){
14873             
14874             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14875             
14876             this.hiddenField.dom.value =
14877                 this.hiddenValue !== undefined ? this.hiddenValue :
14878                 this.value !== undefined ? this.value : '';
14879         
14880             this.el.dom.removeAttribute('name');
14881             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14882         }
14883         
14884         if(this.multiple){
14885             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14886             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14887         }
14888         
14889         if(this.removable && !this.multiple){
14890             var close = this.closeTriggerEl();
14891             if(close){
14892                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14893                 close.on('click', this.removeBtnClick, this, close);
14894             }
14895         }
14896         /*
14897          * fix the bug in Safari iOS8
14898          */
14899         this.inputEl().on("focus", function(e){
14900             document.activeElement.blur();
14901         }, this);
14902         
14903         return;
14904         
14905         
14906     },
14907     
14908     renderTouchView : function()
14909     {
14910         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14911         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14912         
14913         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14914         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14915         
14916         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14917         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14918         this.touchViewBodyEl.setStyle('overflow', 'auto');
14919         
14920         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14921         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14922         
14923         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14924         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14925         
14926     },
14927     
14928     showTouchView : function()
14929     {
14930         if(this.disabled){
14931             return;
14932         }
14933         
14934         this.touchViewHeaderEl.hide();
14935
14936         if(this.modalTitle.length){
14937             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14938             this.touchViewHeaderEl.show();
14939         }
14940
14941         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14942         this.touchViewEl.show();
14943
14944         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14945         
14946         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14947         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14948
14949         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14950
14951         if(this.modalTitle.length){
14952             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14953         }
14954         
14955         this.touchViewBodyEl.setHeight(bodyHeight);
14956
14957         if(this.animate){
14958             var _this = this;
14959             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14960         }else{
14961             this.touchViewEl.addClass('in');
14962         }
14963
14964         this.doTouchViewQuery();
14965         
14966     },
14967     
14968     hideTouchView : function()
14969     {
14970         this.touchViewEl.removeClass('in');
14971
14972         if(this.animate){
14973             var _this = this;
14974             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14975         }else{
14976             this.touchViewEl.setStyle('display', 'none');
14977         }
14978         
14979     },
14980     
14981     setTouchViewValue : function()
14982     {
14983         if(this.multiple){
14984             this.clearItem();
14985         
14986             var _this = this;
14987
14988             Roo.each(this.tickItems, function(o){
14989                 this.addItem(o);
14990             }, this);
14991         }
14992         
14993         this.hideTouchView();
14994     },
14995     
14996     doTouchViewQuery : function()
14997     {
14998         var qe = {
14999             query: '',
15000             forceAll: true,
15001             combo: this,
15002             cancel:false
15003         };
15004         
15005         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15006             return false;
15007         }
15008         
15009         if(!this.alwaysQuery || this.mode == 'local'){
15010             this.onTouchViewLoad();
15011             return;
15012         }
15013         
15014         this.store.load();
15015     },
15016     
15017     onTouchViewBeforeLoad : function(combo,opts)
15018     {
15019         return;
15020     },
15021
15022     // private
15023     onTouchViewLoad : function()
15024     {
15025         if(this.store.getCount() < 1){
15026             this.onTouchViewEmptyResults();
15027             return;
15028         }
15029         
15030         this.clearTouchView();
15031         
15032         var rawValue = this.getRawValue();
15033         
15034         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15035         
15036         this.tickItems = [];
15037         
15038         this.store.data.each(function(d, rowIndex){
15039             var row = this.touchViewListGroup.createChild(template);
15040             
15041             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15042                 row.addClass(d.data.cls);
15043             }
15044             
15045             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15046                 var cfg = {
15047                     data : d.data,
15048                     html : d.data[this.displayField]
15049                 };
15050                 
15051                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15052                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15053                 }
15054             }
15055             row.removeClass('selected');
15056             if(!this.multiple && this.valueField &&
15057                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15058             {
15059                 // radio buttons..
15060                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15061                 row.addClass('selected');
15062             }
15063             
15064             if(this.multiple && this.valueField &&
15065                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15066             {
15067                 
15068                 // checkboxes...
15069                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15070                 this.tickItems.push(d.data);
15071             }
15072             
15073             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15074             
15075         }, this);
15076         
15077         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15078         
15079         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15080
15081         if(this.modalTitle.length){
15082             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15083         }
15084
15085         var listHeight = this.touchViewListGroup.getHeight();
15086         
15087         var _this = this;
15088         
15089         if(firstChecked && listHeight > bodyHeight){
15090             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15091         }
15092         
15093     },
15094     
15095     onTouchViewLoadException : function()
15096     {
15097         this.hideTouchView();
15098     },
15099     
15100     onTouchViewEmptyResults : function()
15101     {
15102         this.clearTouchView();
15103         
15104         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15105         
15106         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15107         
15108     },
15109     
15110     clearTouchView : function()
15111     {
15112         this.touchViewListGroup.dom.innerHTML = '';
15113     },
15114     
15115     onTouchViewClick : function(e, el, o)
15116     {
15117         e.preventDefault();
15118         
15119         var row = o.row;
15120         var rowIndex = o.rowIndex;
15121         
15122         var r = this.store.getAt(rowIndex);
15123         
15124         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15125             
15126             if(!this.multiple){
15127                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15128                     c.dom.removeAttribute('checked');
15129                 }, this);
15130
15131                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15132
15133                 this.setFromData(r.data);
15134
15135                 var close = this.closeTriggerEl();
15136
15137                 if(close){
15138                     close.show();
15139                 }
15140
15141                 this.hideTouchView();
15142
15143                 this.fireEvent('select', this, r, rowIndex);
15144
15145                 return;
15146             }
15147
15148             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15149                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15150                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15151                 return;
15152             }
15153
15154             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15155             this.addItem(r.data);
15156             this.tickItems.push(r.data);
15157         }
15158     },
15159     
15160     getAutoCreateNativeIOS : function()
15161     {
15162         var cfg = {
15163             cls: 'form-group' //input-group,
15164         };
15165         
15166         var combobox =  {
15167             tag: 'select',
15168             cls : 'roo-ios-select'
15169         };
15170         
15171         if (this.name) {
15172             combobox.name = this.name;
15173         }
15174         
15175         if (this.disabled) {
15176             combobox.disabled = true;
15177         }
15178         
15179         var settings = this;
15180         
15181         ['xs','sm','md','lg'].map(function(size){
15182             if (settings[size]) {
15183                 cfg.cls += ' col-' + size + '-' + settings[size];
15184             }
15185         });
15186         
15187         cfg.cn = combobox;
15188         
15189         return cfg;
15190         
15191     },
15192     
15193     initIOSView : function()
15194     {
15195         this.store.on('load', this.onIOSViewLoad, this);
15196         
15197         return;
15198     },
15199     
15200     onIOSViewLoad : function()
15201     {
15202         if(this.store.getCount() < 1){
15203             return;
15204         }
15205         
15206         this.clearIOSView();
15207         
15208         if(this.allowBlank) {
15209             
15210             var default_text = '-- SELECT --';
15211             
15212             var opt = this.inputEl().createChild({
15213                 tag: 'option',
15214                 value : 0,
15215                 html : default_text
15216             });
15217             
15218             var o = {};
15219             o[this.valueField] = 0;
15220             o[this.displayField] = default_text;
15221             
15222             this.ios_options.push({
15223                 data : o,
15224                 el : opt
15225             });
15226             
15227         }
15228         
15229         this.store.data.each(function(d, rowIndex){
15230             
15231             var html = '';
15232             
15233             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15234                 html = d.data[this.displayField];
15235             }
15236             
15237             var value = '';
15238             
15239             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15240                 value = d.data[this.valueField];
15241             }
15242             
15243             var option = {
15244                 tag: 'option',
15245                 value : value,
15246                 html : html
15247             };
15248             
15249             if(this.value == d.data[this.valueField]){
15250                 option['selected'] = true;
15251             }
15252             
15253             var opt = this.inputEl().createChild(option);
15254             
15255             this.ios_options.push({
15256                 data : d.data,
15257                 el : opt
15258             });
15259             
15260         }, this);
15261         
15262         this.inputEl().on('change', function(){
15263            this.fireEvent('select', this);
15264         }, this);
15265         
15266     },
15267     
15268     clearIOSView: function()
15269     {
15270         this.inputEl().dom.innerHTML = '';
15271         
15272         this.ios_options = [];
15273     },
15274     
15275     setIOSValue: function(v)
15276     {
15277         this.value = v;
15278         
15279         if(!this.ios_options){
15280             return;
15281         }
15282         
15283         Roo.each(this.ios_options, function(opts){
15284            
15285            opts.el.dom.removeAttribute('selected');
15286            
15287            if(opts.data[this.valueField] != v){
15288                return;
15289            }
15290            
15291            opts.el.dom.setAttribute('selected', true);
15292            
15293         }, this);
15294     }
15295
15296     /** 
15297     * @cfg {Boolean} grow 
15298     * @hide 
15299     */
15300     /** 
15301     * @cfg {Number} growMin 
15302     * @hide 
15303     */
15304     /** 
15305     * @cfg {Number} growMax 
15306     * @hide 
15307     */
15308     /**
15309      * @hide
15310      * @method autoSize
15311      */
15312 });
15313
15314 Roo.apply(Roo.bootstrap.ComboBox,  {
15315     
15316     header : {
15317         tag: 'div',
15318         cls: 'modal-header',
15319         cn: [
15320             {
15321                 tag: 'h4',
15322                 cls: 'modal-title'
15323             }
15324         ]
15325     },
15326     
15327     body : {
15328         tag: 'div',
15329         cls: 'modal-body',
15330         cn: [
15331             {
15332                 tag: 'ul',
15333                 cls: 'list-group'
15334             }
15335         ]
15336     },
15337     
15338     listItemRadio : {
15339         tag: 'li',
15340         cls: 'list-group-item',
15341         cn: [
15342             {
15343                 tag: 'span',
15344                 cls: 'roo-combobox-list-group-item-value'
15345             },
15346             {
15347                 tag: 'div',
15348                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15349                 cn: [
15350                     {
15351                         tag: 'input',
15352                         type: 'radio'
15353                     },
15354                     {
15355                         tag: 'label'
15356                     }
15357                 ]
15358             }
15359         ]
15360     },
15361     
15362     listItemCheckbox : {
15363         tag: 'li',
15364         cls: 'list-group-item',
15365         cn: [
15366             {
15367                 tag: 'span',
15368                 cls: 'roo-combobox-list-group-item-value'
15369             },
15370             {
15371                 tag: 'div',
15372                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15373                 cn: [
15374                     {
15375                         tag: 'input',
15376                         type: 'checkbox'
15377                     },
15378                     {
15379                         tag: 'label'
15380                     }
15381                 ]
15382             }
15383         ]
15384     },
15385     
15386     emptyResult : {
15387         tag: 'div',
15388         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15389     },
15390     
15391     footer : {
15392         tag: 'div',
15393         cls: 'modal-footer',
15394         cn: [
15395             {
15396                 tag: 'div',
15397                 cls: 'row',
15398                 cn: [
15399                     {
15400                         tag: 'div',
15401                         cls: 'col-xs-6 text-left',
15402                         cn: {
15403                             tag: 'button',
15404                             cls: 'btn btn-danger roo-touch-view-cancel',
15405                             html: 'Cancel'
15406                         }
15407                     },
15408                     {
15409                         tag: 'div',
15410                         cls: 'col-xs-6 text-right',
15411                         cn: {
15412                             tag: 'button',
15413                             cls: 'btn btn-success roo-touch-view-ok',
15414                             html: 'OK'
15415                         }
15416                     }
15417                 ]
15418             }
15419         ]
15420         
15421     }
15422 });
15423
15424 Roo.apply(Roo.bootstrap.ComboBox,  {
15425     
15426     touchViewTemplate : {
15427         tag: 'div',
15428         cls: 'modal fade roo-combobox-touch-view',
15429         cn: [
15430             {
15431                 tag: 'div',
15432                 cls: 'modal-dialog',
15433                 style : 'position:fixed', // we have to fix position....
15434                 cn: [
15435                     {
15436                         tag: 'div',
15437                         cls: 'modal-content',
15438                         cn: [
15439                             Roo.bootstrap.ComboBox.header,
15440                             Roo.bootstrap.ComboBox.body,
15441                             Roo.bootstrap.ComboBox.footer
15442                         ]
15443                     }
15444                 ]
15445             }
15446         ]
15447     }
15448 });/*
15449  * Based on:
15450  * Ext JS Library 1.1.1
15451  * Copyright(c) 2006-2007, Ext JS, LLC.
15452  *
15453  * Originally Released Under LGPL - original licence link has changed is not relivant.
15454  *
15455  * Fork - LGPL
15456  * <script type="text/javascript">
15457  */
15458
15459 /**
15460  * @class Roo.View
15461  * @extends Roo.util.Observable
15462  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15463  * This class also supports single and multi selection modes. <br>
15464  * Create a data model bound view:
15465  <pre><code>
15466  var store = new Roo.data.Store(...);
15467
15468  var view = new Roo.View({
15469     el : "my-element",
15470     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15471  
15472     singleSelect: true,
15473     selectedClass: "ydataview-selected",
15474     store: store
15475  });
15476
15477  // listen for node click?
15478  view.on("click", function(vw, index, node, e){
15479  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15480  });
15481
15482  // load XML data
15483  dataModel.load("foobar.xml");
15484  </code></pre>
15485  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15486  * <br><br>
15487  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15488  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15489  * 
15490  * Note: old style constructor is still suported (container, template, config)
15491  * 
15492  * @constructor
15493  * Create a new View
15494  * @param {Object} config The config object
15495  * 
15496  */
15497 Roo.View = function(config, depreciated_tpl, depreciated_config){
15498     
15499     this.parent = false;
15500     
15501     if (typeof(depreciated_tpl) == 'undefined') {
15502         // new way.. - universal constructor.
15503         Roo.apply(this, config);
15504         this.el  = Roo.get(this.el);
15505     } else {
15506         // old format..
15507         this.el  = Roo.get(config);
15508         this.tpl = depreciated_tpl;
15509         Roo.apply(this, depreciated_config);
15510     }
15511     this.wrapEl  = this.el.wrap().wrap();
15512     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15513     
15514     
15515     if(typeof(this.tpl) == "string"){
15516         this.tpl = new Roo.Template(this.tpl);
15517     } else {
15518         // support xtype ctors..
15519         this.tpl = new Roo.factory(this.tpl, Roo);
15520     }
15521     
15522     
15523     this.tpl.compile();
15524     
15525     /** @private */
15526     this.addEvents({
15527         /**
15528          * @event beforeclick
15529          * Fires before a click is processed. Returns false to cancel the default action.
15530          * @param {Roo.View} this
15531          * @param {Number} index The index of the target node
15532          * @param {HTMLElement} node The target node
15533          * @param {Roo.EventObject} e The raw event object
15534          */
15535             "beforeclick" : true,
15536         /**
15537          * @event click
15538          * Fires when a template node is clicked.
15539          * @param {Roo.View} this
15540          * @param {Number} index The index of the target node
15541          * @param {HTMLElement} node The target node
15542          * @param {Roo.EventObject} e The raw event object
15543          */
15544             "click" : true,
15545         /**
15546          * @event dblclick
15547          * Fires when a template node is double clicked.
15548          * @param {Roo.View} this
15549          * @param {Number} index The index of the target node
15550          * @param {HTMLElement} node The target node
15551          * @param {Roo.EventObject} e The raw event object
15552          */
15553             "dblclick" : true,
15554         /**
15555          * @event contextmenu
15556          * Fires when a template node is right clicked.
15557          * @param {Roo.View} this
15558          * @param {Number} index The index of the target node
15559          * @param {HTMLElement} node The target node
15560          * @param {Roo.EventObject} e The raw event object
15561          */
15562             "contextmenu" : true,
15563         /**
15564          * @event selectionchange
15565          * Fires when the selected nodes change.
15566          * @param {Roo.View} this
15567          * @param {Array} selections Array of the selected nodes
15568          */
15569             "selectionchange" : true,
15570     
15571         /**
15572          * @event beforeselect
15573          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15574          * @param {Roo.View} this
15575          * @param {HTMLElement} node The node to be selected
15576          * @param {Array} selections Array of currently selected nodes
15577          */
15578             "beforeselect" : true,
15579         /**
15580          * @event preparedata
15581          * Fires on every row to render, to allow you to change the data.
15582          * @param {Roo.View} this
15583          * @param {Object} data to be rendered (change this)
15584          */
15585           "preparedata" : true
15586           
15587           
15588         });
15589
15590
15591
15592     this.el.on({
15593         "click": this.onClick,
15594         "dblclick": this.onDblClick,
15595         "contextmenu": this.onContextMenu,
15596         scope:this
15597     });
15598
15599     this.selections = [];
15600     this.nodes = [];
15601     this.cmp = new Roo.CompositeElementLite([]);
15602     if(this.store){
15603         this.store = Roo.factory(this.store, Roo.data);
15604         this.setStore(this.store, true);
15605     }
15606     
15607     if ( this.footer && this.footer.xtype) {
15608            
15609          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15610         
15611         this.footer.dataSource = this.store;
15612         this.footer.container = fctr;
15613         this.footer = Roo.factory(this.footer, Roo);
15614         fctr.insertFirst(this.el);
15615         
15616         // this is a bit insane - as the paging toolbar seems to detach the el..
15617 //        dom.parentNode.parentNode.parentNode
15618          // they get detached?
15619     }
15620     
15621     
15622     Roo.View.superclass.constructor.call(this);
15623     
15624     
15625 };
15626
15627 Roo.extend(Roo.View, Roo.util.Observable, {
15628     
15629      /**
15630      * @cfg {Roo.data.Store} store Data store to load data from.
15631      */
15632     store : false,
15633     
15634     /**
15635      * @cfg {String|Roo.Element} el The container element.
15636      */
15637     el : '',
15638     
15639     /**
15640      * @cfg {String|Roo.Template} tpl The template used by this View 
15641      */
15642     tpl : false,
15643     /**
15644      * @cfg {String} dataName the named area of the template to use as the data area
15645      *                          Works with domtemplates roo-name="name"
15646      */
15647     dataName: false,
15648     /**
15649      * @cfg {String} selectedClass The css class to add to selected nodes
15650      */
15651     selectedClass : "x-view-selected",
15652      /**
15653      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15654      */
15655     emptyText : "",
15656     
15657     /**
15658      * @cfg {String} text to display on mask (default Loading)
15659      */
15660     mask : false,
15661     /**
15662      * @cfg {Boolean} multiSelect Allow multiple selection
15663      */
15664     multiSelect : false,
15665     /**
15666      * @cfg {Boolean} singleSelect Allow single selection
15667      */
15668     singleSelect:  false,
15669     
15670     /**
15671      * @cfg {Boolean} toggleSelect - selecting 
15672      */
15673     toggleSelect : false,
15674     
15675     /**
15676      * @cfg {Boolean} tickable - selecting 
15677      */
15678     tickable : false,
15679     
15680     /**
15681      * Returns the element this view is bound to.
15682      * @return {Roo.Element}
15683      */
15684     getEl : function(){
15685         return this.wrapEl;
15686     },
15687     
15688     
15689
15690     /**
15691      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15692      */
15693     refresh : function(){
15694         //Roo.log('refresh');
15695         var t = this.tpl;
15696         
15697         // if we are using something like 'domtemplate', then
15698         // the what gets used is:
15699         // t.applySubtemplate(NAME, data, wrapping data..)
15700         // the outer template then get' applied with
15701         //     the store 'extra data'
15702         // and the body get's added to the
15703         //      roo-name="data" node?
15704         //      <span class='roo-tpl-{name}'></span> ?????
15705         
15706         
15707         
15708         this.clearSelections();
15709         this.el.update("");
15710         var html = [];
15711         var records = this.store.getRange();
15712         if(records.length < 1) {
15713             
15714             // is this valid??  = should it render a template??
15715             
15716             this.el.update(this.emptyText);
15717             return;
15718         }
15719         var el = this.el;
15720         if (this.dataName) {
15721             this.el.update(t.apply(this.store.meta)); //????
15722             el = this.el.child('.roo-tpl-' + this.dataName);
15723         }
15724         
15725         for(var i = 0, len = records.length; i < len; i++){
15726             var data = this.prepareData(records[i].data, i, records[i]);
15727             this.fireEvent("preparedata", this, data, i, records[i]);
15728             
15729             var d = Roo.apply({}, data);
15730             
15731             if(this.tickable){
15732                 Roo.apply(d, {'roo-id' : Roo.id()});
15733                 
15734                 var _this = this;
15735             
15736                 Roo.each(this.parent.item, function(item){
15737                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15738                         return;
15739                     }
15740                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15741                 });
15742             }
15743             
15744             html[html.length] = Roo.util.Format.trim(
15745                 this.dataName ?
15746                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15747                     t.apply(d)
15748             );
15749         }
15750         
15751         
15752         
15753         el.update(html.join(""));
15754         this.nodes = el.dom.childNodes;
15755         this.updateIndexes(0);
15756     },
15757     
15758
15759     /**
15760      * Function to override to reformat the data that is sent to
15761      * the template for each node.
15762      * DEPRICATED - use the preparedata event handler.
15763      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15764      * a JSON object for an UpdateManager bound view).
15765      */
15766     prepareData : function(data, index, record)
15767     {
15768         this.fireEvent("preparedata", this, data, index, record);
15769         return data;
15770     },
15771
15772     onUpdate : function(ds, record){
15773         // Roo.log('on update');   
15774         this.clearSelections();
15775         var index = this.store.indexOf(record);
15776         var n = this.nodes[index];
15777         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15778         n.parentNode.removeChild(n);
15779         this.updateIndexes(index, index);
15780     },
15781
15782     
15783     
15784 // --------- FIXME     
15785     onAdd : function(ds, records, index)
15786     {
15787         //Roo.log(['on Add', ds, records, index] );        
15788         this.clearSelections();
15789         if(this.nodes.length == 0){
15790             this.refresh();
15791             return;
15792         }
15793         var n = this.nodes[index];
15794         for(var i = 0, len = records.length; i < len; i++){
15795             var d = this.prepareData(records[i].data, i, records[i]);
15796             if(n){
15797                 this.tpl.insertBefore(n, d);
15798             }else{
15799                 
15800                 this.tpl.append(this.el, d);
15801             }
15802         }
15803         this.updateIndexes(index);
15804     },
15805
15806     onRemove : function(ds, record, index){
15807        // Roo.log('onRemove');
15808         this.clearSelections();
15809         var el = this.dataName  ?
15810             this.el.child('.roo-tpl-' + this.dataName) :
15811             this.el; 
15812         
15813         el.dom.removeChild(this.nodes[index]);
15814         this.updateIndexes(index);
15815     },
15816
15817     /**
15818      * Refresh an individual node.
15819      * @param {Number} index
15820      */
15821     refreshNode : function(index){
15822         this.onUpdate(this.store, this.store.getAt(index));
15823     },
15824
15825     updateIndexes : function(startIndex, endIndex){
15826         var ns = this.nodes;
15827         startIndex = startIndex || 0;
15828         endIndex = endIndex || ns.length - 1;
15829         for(var i = startIndex; i <= endIndex; i++){
15830             ns[i].nodeIndex = i;
15831         }
15832     },
15833
15834     /**
15835      * Changes the data store this view uses and refresh the view.
15836      * @param {Store} store
15837      */
15838     setStore : function(store, initial){
15839         if(!initial && this.store){
15840             this.store.un("datachanged", this.refresh);
15841             this.store.un("add", this.onAdd);
15842             this.store.un("remove", this.onRemove);
15843             this.store.un("update", this.onUpdate);
15844             this.store.un("clear", this.refresh);
15845             this.store.un("beforeload", this.onBeforeLoad);
15846             this.store.un("load", this.onLoad);
15847             this.store.un("loadexception", this.onLoad);
15848         }
15849         if(store){
15850           
15851             store.on("datachanged", this.refresh, this);
15852             store.on("add", this.onAdd, this);
15853             store.on("remove", this.onRemove, this);
15854             store.on("update", this.onUpdate, this);
15855             store.on("clear", this.refresh, this);
15856             store.on("beforeload", this.onBeforeLoad, this);
15857             store.on("load", this.onLoad, this);
15858             store.on("loadexception", this.onLoad, this);
15859         }
15860         
15861         if(store){
15862             this.refresh();
15863         }
15864     },
15865     /**
15866      * onbeforeLoad - masks the loading area.
15867      *
15868      */
15869     onBeforeLoad : function(store,opts)
15870     {
15871          //Roo.log('onBeforeLoad');   
15872         if (!opts.add) {
15873             this.el.update("");
15874         }
15875         this.el.mask(this.mask ? this.mask : "Loading" ); 
15876     },
15877     onLoad : function ()
15878     {
15879         this.el.unmask();
15880     },
15881     
15882
15883     /**
15884      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15885      * @param {HTMLElement} node
15886      * @return {HTMLElement} The template node
15887      */
15888     findItemFromChild : function(node){
15889         var el = this.dataName  ?
15890             this.el.child('.roo-tpl-' + this.dataName,true) :
15891             this.el.dom; 
15892         
15893         if(!node || node.parentNode == el){
15894                     return node;
15895             }
15896             var p = node.parentNode;
15897             while(p && p != el){
15898             if(p.parentNode == el){
15899                 return p;
15900             }
15901             p = p.parentNode;
15902         }
15903             return null;
15904     },
15905
15906     /** @ignore */
15907     onClick : function(e){
15908         var item = this.findItemFromChild(e.getTarget());
15909         if(item){
15910             var index = this.indexOf(item);
15911             if(this.onItemClick(item, index, e) !== false){
15912                 this.fireEvent("click", this, index, item, e);
15913             }
15914         }else{
15915             this.clearSelections();
15916         }
15917     },
15918
15919     /** @ignore */
15920     onContextMenu : function(e){
15921         var item = this.findItemFromChild(e.getTarget());
15922         if(item){
15923             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15924         }
15925     },
15926
15927     /** @ignore */
15928     onDblClick : function(e){
15929         var item = this.findItemFromChild(e.getTarget());
15930         if(item){
15931             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15932         }
15933     },
15934
15935     onItemClick : function(item, index, e)
15936     {
15937         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15938             return false;
15939         }
15940         if (this.toggleSelect) {
15941             var m = this.isSelected(item) ? 'unselect' : 'select';
15942             //Roo.log(m);
15943             var _t = this;
15944             _t[m](item, true, false);
15945             return true;
15946         }
15947         if(this.multiSelect || this.singleSelect){
15948             if(this.multiSelect && e.shiftKey && this.lastSelection){
15949                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15950             }else{
15951                 this.select(item, this.multiSelect && e.ctrlKey);
15952                 this.lastSelection = item;
15953             }
15954             
15955             if(!this.tickable){
15956                 e.preventDefault();
15957             }
15958             
15959         }
15960         return true;
15961     },
15962
15963     /**
15964      * Get the number of selected nodes.
15965      * @return {Number}
15966      */
15967     getSelectionCount : function(){
15968         return this.selections.length;
15969     },
15970
15971     /**
15972      * Get the currently selected nodes.
15973      * @return {Array} An array of HTMLElements
15974      */
15975     getSelectedNodes : function(){
15976         return this.selections;
15977     },
15978
15979     /**
15980      * Get the indexes of the selected nodes.
15981      * @return {Array}
15982      */
15983     getSelectedIndexes : function(){
15984         var indexes = [], s = this.selections;
15985         for(var i = 0, len = s.length; i < len; i++){
15986             indexes.push(s[i].nodeIndex);
15987         }
15988         return indexes;
15989     },
15990
15991     /**
15992      * Clear all selections
15993      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15994      */
15995     clearSelections : function(suppressEvent){
15996         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15997             this.cmp.elements = this.selections;
15998             this.cmp.removeClass(this.selectedClass);
15999             this.selections = [];
16000             if(!suppressEvent){
16001                 this.fireEvent("selectionchange", this, this.selections);
16002             }
16003         }
16004     },
16005
16006     /**
16007      * Returns true if the passed node is selected
16008      * @param {HTMLElement/Number} node The node or node index
16009      * @return {Boolean}
16010      */
16011     isSelected : function(node){
16012         var s = this.selections;
16013         if(s.length < 1){
16014             return false;
16015         }
16016         node = this.getNode(node);
16017         return s.indexOf(node) !== -1;
16018     },
16019
16020     /**
16021      * Selects nodes.
16022      * @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
16023      * @param {Boolean} keepExisting (optional) true to keep existing selections
16024      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16025      */
16026     select : function(nodeInfo, keepExisting, suppressEvent){
16027         if(nodeInfo instanceof Array){
16028             if(!keepExisting){
16029                 this.clearSelections(true);
16030             }
16031             for(var i = 0, len = nodeInfo.length; i < len; i++){
16032                 this.select(nodeInfo[i], true, true);
16033             }
16034             return;
16035         } 
16036         var node = this.getNode(nodeInfo);
16037         if(!node || this.isSelected(node)){
16038             return; // already selected.
16039         }
16040         if(!keepExisting){
16041             this.clearSelections(true);
16042         }
16043         
16044         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16045             Roo.fly(node).addClass(this.selectedClass);
16046             this.selections.push(node);
16047             if(!suppressEvent){
16048                 this.fireEvent("selectionchange", this, this.selections);
16049             }
16050         }
16051         
16052         
16053     },
16054       /**
16055      * Unselects nodes.
16056      * @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
16057      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16058      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16059      */
16060     unselect : function(nodeInfo, keepExisting, suppressEvent)
16061     {
16062         if(nodeInfo instanceof Array){
16063             Roo.each(this.selections, function(s) {
16064                 this.unselect(s, nodeInfo);
16065             }, this);
16066             return;
16067         }
16068         var node = this.getNode(nodeInfo);
16069         if(!node || !this.isSelected(node)){
16070             //Roo.log("not selected");
16071             return; // not selected.
16072         }
16073         // fireevent???
16074         var ns = [];
16075         Roo.each(this.selections, function(s) {
16076             if (s == node ) {
16077                 Roo.fly(node).removeClass(this.selectedClass);
16078
16079                 return;
16080             }
16081             ns.push(s);
16082         },this);
16083         
16084         this.selections= ns;
16085         this.fireEvent("selectionchange", this, this.selections);
16086     },
16087
16088     /**
16089      * Gets a template node.
16090      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16091      * @return {HTMLElement} The node or null if it wasn't found
16092      */
16093     getNode : function(nodeInfo){
16094         if(typeof nodeInfo == "string"){
16095             return document.getElementById(nodeInfo);
16096         }else if(typeof nodeInfo == "number"){
16097             return this.nodes[nodeInfo];
16098         }
16099         return nodeInfo;
16100     },
16101
16102     /**
16103      * Gets a range template nodes.
16104      * @param {Number} startIndex
16105      * @param {Number} endIndex
16106      * @return {Array} An array of nodes
16107      */
16108     getNodes : function(start, end){
16109         var ns = this.nodes;
16110         start = start || 0;
16111         end = typeof end == "undefined" ? ns.length - 1 : end;
16112         var nodes = [];
16113         if(start <= end){
16114             for(var i = start; i <= end; i++){
16115                 nodes.push(ns[i]);
16116             }
16117         } else{
16118             for(var i = start; i >= end; i--){
16119                 nodes.push(ns[i]);
16120             }
16121         }
16122         return nodes;
16123     },
16124
16125     /**
16126      * Finds the index of the passed node
16127      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16128      * @return {Number} The index of the node or -1
16129      */
16130     indexOf : function(node){
16131         node = this.getNode(node);
16132         if(typeof node.nodeIndex == "number"){
16133             return node.nodeIndex;
16134         }
16135         var ns = this.nodes;
16136         for(var i = 0, len = ns.length; i < len; i++){
16137             if(ns[i] == node){
16138                 return i;
16139             }
16140         }
16141         return -1;
16142     }
16143 });
16144 /*
16145  * - LGPL
16146  *
16147  * based on jquery fullcalendar
16148  * 
16149  */
16150
16151 Roo.bootstrap = Roo.bootstrap || {};
16152 /**
16153  * @class Roo.bootstrap.Calendar
16154  * @extends Roo.bootstrap.Component
16155  * Bootstrap Calendar class
16156  * @cfg {Boolean} loadMask (true|false) default false
16157  * @cfg {Object} header generate the user specific header of the calendar, default false
16158
16159  * @constructor
16160  * Create a new Container
16161  * @param {Object} config The config object
16162  */
16163
16164
16165
16166 Roo.bootstrap.Calendar = function(config){
16167     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16168      this.addEvents({
16169         /**
16170              * @event select
16171              * Fires when a date is selected
16172              * @param {DatePicker} this
16173              * @param {Date} date The selected date
16174              */
16175         'select': true,
16176         /**
16177              * @event monthchange
16178              * Fires when the displayed month changes 
16179              * @param {DatePicker} this
16180              * @param {Date} date The selected month
16181              */
16182         'monthchange': true,
16183         /**
16184              * @event evententer
16185              * Fires when mouse over an event
16186              * @param {Calendar} this
16187              * @param {event} Event
16188              */
16189         'evententer': true,
16190         /**
16191              * @event eventleave
16192              * Fires when the mouse leaves an
16193              * @param {Calendar} this
16194              * @param {event}
16195              */
16196         'eventleave': true,
16197         /**
16198              * @event eventclick
16199              * Fires when the mouse click an
16200              * @param {Calendar} this
16201              * @param {event}
16202              */
16203         'eventclick': true
16204         
16205     });
16206
16207 };
16208
16209 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16210     
16211      /**
16212      * @cfg {Number} startDay
16213      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16214      */
16215     startDay : 0,
16216     
16217     loadMask : false,
16218     
16219     header : false,
16220       
16221     getAutoCreate : function(){
16222         
16223         
16224         var fc_button = function(name, corner, style, content ) {
16225             return Roo.apply({},{
16226                 tag : 'span',
16227                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16228                          (corner.length ?
16229                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16230                             ''
16231                         ),
16232                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16233                 unselectable: 'on'
16234             });
16235         };
16236         
16237         var header = {};
16238         
16239         if(!this.header){
16240             header = {
16241                 tag : 'table',
16242                 cls : 'fc-header',
16243                 style : 'width:100%',
16244                 cn : [
16245                     {
16246                         tag: 'tr',
16247                         cn : [
16248                             {
16249                                 tag : 'td',
16250                                 cls : 'fc-header-left',
16251                                 cn : [
16252                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16253                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16254                                     { tag: 'span', cls: 'fc-header-space' },
16255                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16256
16257
16258                                 ]
16259                             },
16260
16261                             {
16262                                 tag : 'td',
16263                                 cls : 'fc-header-center',
16264                                 cn : [
16265                                     {
16266                                         tag: 'span',
16267                                         cls: 'fc-header-title',
16268                                         cn : {
16269                                             tag: 'H2',
16270                                             html : 'month / year'
16271                                         }
16272                                     }
16273
16274                                 ]
16275                             },
16276                             {
16277                                 tag : 'td',
16278                                 cls : 'fc-header-right',
16279                                 cn : [
16280                               /*      fc_button('month', 'left', '', 'month' ),
16281                                     fc_button('week', '', '', 'week' ),
16282                                     fc_button('day', 'right', '', 'day' )
16283                                 */    
16284
16285                                 ]
16286                             }
16287
16288                         ]
16289                     }
16290                 ]
16291             };
16292         }
16293         
16294         header = this.header;
16295         
16296        
16297         var cal_heads = function() {
16298             var ret = [];
16299             // fixme - handle this.
16300             
16301             for (var i =0; i < Date.dayNames.length; i++) {
16302                 var d = Date.dayNames[i];
16303                 ret.push({
16304                     tag: 'th',
16305                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16306                     html : d.substring(0,3)
16307                 });
16308                 
16309             }
16310             ret[0].cls += ' fc-first';
16311             ret[6].cls += ' fc-last';
16312             return ret;
16313         };
16314         var cal_cell = function(n) {
16315             return  {
16316                 tag: 'td',
16317                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16318                 cn : [
16319                     {
16320                         cn : [
16321                             {
16322                                 cls: 'fc-day-number',
16323                                 html: 'D'
16324                             },
16325                             {
16326                                 cls: 'fc-day-content',
16327                              
16328                                 cn : [
16329                                      {
16330                                         style: 'position: relative;' // height: 17px;
16331                                     }
16332                                 ]
16333                             }
16334                             
16335                             
16336                         ]
16337                     }
16338                 ]
16339                 
16340             }
16341         };
16342         var cal_rows = function() {
16343             
16344             var ret = [];
16345             for (var r = 0; r < 6; r++) {
16346                 var row= {
16347                     tag : 'tr',
16348                     cls : 'fc-week',
16349                     cn : []
16350                 };
16351                 
16352                 for (var i =0; i < Date.dayNames.length; i++) {
16353                     var d = Date.dayNames[i];
16354                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16355
16356                 }
16357                 row.cn[0].cls+=' fc-first';
16358                 row.cn[0].cn[0].style = 'min-height:90px';
16359                 row.cn[6].cls+=' fc-last';
16360                 ret.push(row);
16361                 
16362             }
16363             ret[0].cls += ' fc-first';
16364             ret[4].cls += ' fc-prev-last';
16365             ret[5].cls += ' fc-last';
16366             return ret;
16367             
16368         };
16369         
16370         var cal_table = {
16371             tag: 'table',
16372             cls: 'fc-border-separate',
16373             style : 'width:100%',
16374             cellspacing  : 0,
16375             cn : [
16376                 { 
16377                     tag: 'thead',
16378                     cn : [
16379                         { 
16380                             tag: 'tr',
16381                             cls : 'fc-first fc-last',
16382                             cn : cal_heads()
16383                         }
16384                     ]
16385                 },
16386                 { 
16387                     tag: 'tbody',
16388                     cn : cal_rows()
16389                 }
16390                   
16391             ]
16392         };
16393          
16394          var cfg = {
16395             cls : 'fc fc-ltr',
16396             cn : [
16397                 header,
16398                 {
16399                     cls : 'fc-content',
16400                     style : "position: relative;",
16401                     cn : [
16402                         {
16403                             cls : 'fc-view fc-view-month fc-grid',
16404                             style : 'position: relative',
16405                             unselectable : 'on',
16406                             cn : [
16407                                 {
16408                                     cls : 'fc-event-container',
16409                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16410                                 },
16411                                 cal_table
16412                             ]
16413                         }
16414                     ]
16415     
16416                 }
16417            ] 
16418             
16419         };
16420         
16421          
16422         
16423         return cfg;
16424     },
16425     
16426     
16427     initEvents : function()
16428     {
16429         if(!this.store){
16430             throw "can not find store for calendar";
16431         }
16432         
16433         var mark = {
16434             tag: "div",
16435             cls:"x-dlg-mask",
16436             style: "text-align:center",
16437             cn: [
16438                 {
16439                     tag: "div",
16440                     style: "background-color:white;width:50%;margin:250 auto",
16441                     cn: [
16442                         {
16443                             tag: "img",
16444                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16445                         },
16446                         {
16447                             tag: "span",
16448                             html: "Loading"
16449                         }
16450                         
16451                     ]
16452                 }
16453             ]
16454         };
16455         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16456         
16457         var size = this.el.select('.fc-content', true).first().getSize();
16458         this.maskEl.setSize(size.width, size.height);
16459         this.maskEl.enableDisplayMode("block");
16460         if(!this.loadMask){
16461             this.maskEl.hide();
16462         }
16463         
16464         this.store = Roo.factory(this.store, Roo.data);
16465         this.store.on('load', this.onLoad, this);
16466         this.store.on('beforeload', this.onBeforeLoad, this);
16467         
16468         this.resize();
16469         
16470         this.cells = this.el.select('.fc-day',true);
16471         //Roo.log(this.cells);
16472         this.textNodes = this.el.query('.fc-day-number');
16473         this.cells.addClassOnOver('fc-state-hover');
16474         
16475         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16476         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16477         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16478         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16479         
16480         this.on('monthchange', this.onMonthChange, this);
16481         
16482         this.update(new Date().clearTime());
16483     },
16484     
16485     resize : function() {
16486         var sz  = this.el.getSize();
16487         
16488         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16489         this.el.select('.fc-day-content div',true).setHeight(34);
16490     },
16491     
16492     
16493     // private
16494     showPrevMonth : function(e){
16495         this.update(this.activeDate.add("mo", -1));
16496     },
16497     showToday : function(e){
16498         this.update(new Date().clearTime());
16499     },
16500     // private
16501     showNextMonth : function(e){
16502         this.update(this.activeDate.add("mo", 1));
16503     },
16504
16505     // private
16506     showPrevYear : function(){
16507         this.update(this.activeDate.add("y", -1));
16508     },
16509
16510     // private
16511     showNextYear : function(){
16512         this.update(this.activeDate.add("y", 1));
16513     },
16514
16515     
16516    // private
16517     update : function(date)
16518     {
16519         var vd = this.activeDate;
16520         this.activeDate = date;
16521 //        if(vd && this.el){
16522 //            var t = date.getTime();
16523 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16524 //                Roo.log('using add remove');
16525 //                
16526 //                this.fireEvent('monthchange', this, date);
16527 //                
16528 //                this.cells.removeClass("fc-state-highlight");
16529 //                this.cells.each(function(c){
16530 //                   if(c.dateValue == t){
16531 //                       c.addClass("fc-state-highlight");
16532 //                       setTimeout(function(){
16533 //                            try{c.dom.firstChild.focus();}catch(e){}
16534 //                       }, 50);
16535 //                       return false;
16536 //                   }
16537 //                   return true;
16538 //                });
16539 //                return;
16540 //            }
16541 //        }
16542         
16543         var days = date.getDaysInMonth();
16544         
16545         var firstOfMonth = date.getFirstDateOfMonth();
16546         var startingPos = firstOfMonth.getDay()-this.startDay;
16547         
16548         if(startingPos < this.startDay){
16549             startingPos += 7;
16550         }
16551         
16552         var pm = date.add(Date.MONTH, -1);
16553         var prevStart = pm.getDaysInMonth()-startingPos;
16554 //        
16555         this.cells = this.el.select('.fc-day',true);
16556         this.textNodes = this.el.query('.fc-day-number');
16557         this.cells.addClassOnOver('fc-state-hover');
16558         
16559         var cells = this.cells.elements;
16560         var textEls = this.textNodes;
16561         
16562         Roo.each(cells, function(cell){
16563             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16564         });
16565         
16566         days += startingPos;
16567
16568         // convert everything to numbers so it's fast
16569         var day = 86400000;
16570         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16571         //Roo.log(d);
16572         //Roo.log(pm);
16573         //Roo.log(prevStart);
16574         
16575         var today = new Date().clearTime().getTime();
16576         var sel = date.clearTime().getTime();
16577         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16578         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16579         var ddMatch = this.disabledDatesRE;
16580         var ddText = this.disabledDatesText;
16581         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16582         var ddaysText = this.disabledDaysText;
16583         var format = this.format;
16584         
16585         var setCellClass = function(cal, cell){
16586             cell.row = 0;
16587             cell.events = [];
16588             cell.more = [];
16589             //Roo.log('set Cell Class');
16590             cell.title = "";
16591             var t = d.getTime();
16592             
16593             //Roo.log(d);
16594             
16595             cell.dateValue = t;
16596             if(t == today){
16597                 cell.className += " fc-today";
16598                 cell.className += " fc-state-highlight";
16599                 cell.title = cal.todayText;
16600             }
16601             if(t == sel){
16602                 // disable highlight in other month..
16603                 //cell.className += " fc-state-highlight";
16604                 
16605             }
16606             // disabling
16607             if(t < min) {
16608                 cell.className = " fc-state-disabled";
16609                 cell.title = cal.minText;
16610                 return;
16611             }
16612             if(t > max) {
16613                 cell.className = " fc-state-disabled";
16614                 cell.title = cal.maxText;
16615                 return;
16616             }
16617             if(ddays){
16618                 if(ddays.indexOf(d.getDay()) != -1){
16619                     cell.title = ddaysText;
16620                     cell.className = " fc-state-disabled";
16621                 }
16622             }
16623             if(ddMatch && format){
16624                 var fvalue = d.dateFormat(format);
16625                 if(ddMatch.test(fvalue)){
16626                     cell.title = ddText.replace("%0", fvalue);
16627                     cell.className = " fc-state-disabled";
16628                 }
16629             }
16630             
16631             if (!cell.initialClassName) {
16632                 cell.initialClassName = cell.dom.className;
16633             }
16634             
16635             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16636         };
16637
16638         var i = 0;
16639         
16640         for(; i < startingPos; i++) {
16641             textEls[i].innerHTML = (++prevStart);
16642             d.setDate(d.getDate()+1);
16643             
16644             cells[i].className = "fc-past fc-other-month";
16645             setCellClass(this, cells[i]);
16646         }
16647         
16648         var intDay = 0;
16649         
16650         for(; i < days; i++){
16651             intDay = i - startingPos + 1;
16652             textEls[i].innerHTML = (intDay);
16653             d.setDate(d.getDate()+1);
16654             
16655             cells[i].className = ''; // "x-date-active";
16656             setCellClass(this, cells[i]);
16657         }
16658         var extraDays = 0;
16659         
16660         for(; i < 42; i++) {
16661             textEls[i].innerHTML = (++extraDays);
16662             d.setDate(d.getDate()+1);
16663             
16664             cells[i].className = "fc-future fc-other-month";
16665             setCellClass(this, cells[i]);
16666         }
16667         
16668         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16669         
16670         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16671         
16672         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16673         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16674         
16675         if(totalRows != 6){
16676             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16677             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16678         }
16679         
16680         this.fireEvent('monthchange', this, date);
16681         
16682         
16683         /*
16684         if(!this.internalRender){
16685             var main = this.el.dom.firstChild;
16686             var w = main.offsetWidth;
16687             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16688             Roo.fly(main).setWidth(w);
16689             this.internalRender = true;
16690             // opera does not respect the auto grow header center column
16691             // then, after it gets a width opera refuses to recalculate
16692             // without a second pass
16693             if(Roo.isOpera && !this.secondPass){
16694                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16695                 this.secondPass = true;
16696                 this.update.defer(10, this, [date]);
16697             }
16698         }
16699         */
16700         
16701     },
16702     
16703     findCell : function(dt) {
16704         dt = dt.clearTime().getTime();
16705         var ret = false;
16706         this.cells.each(function(c){
16707             //Roo.log("check " +c.dateValue + '?=' + dt);
16708             if(c.dateValue == dt){
16709                 ret = c;
16710                 return false;
16711             }
16712             return true;
16713         });
16714         
16715         return ret;
16716     },
16717     
16718     findCells : function(ev) {
16719         var s = ev.start.clone().clearTime().getTime();
16720        // Roo.log(s);
16721         var e= ev.end.clone().clearTime().getTime();
16722        // Roo.log(e);
16723         var ret = [];
16724         this.cells.each(function(c){
16725              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16726             
16727             if(c.dateValue > e){
16728                 return ;
16729             }
16730             if(c.dateValue < s){
16731                 return ;
16732             }
16733             ret.push(c);
16734         });
16735         
16736         return ret;    
16737     },
16738     
16739 //    findBestRow: function(cells)
16740 //    {
16741 //        var ret = 0;
16742 //        
16743 //        for (var i =0 ; i < cells.length;i++) {
16744 //            ret  = Math.max(cells[i].rows || 0,ret);
16745 //        }
16746 //        return ret;
16747 //        
16748 //    },
16749     
16750     
16751     addItem : function(ev)
16752     {
16753         // look for vertical location slot in
16754         var cells = this.findCells(ev);
16755         
16756 //        ev.row = this.findBestRow(cells);
16757         
16758         // work out the location.
16759         
16760         var crow = false;
16761         var rows = [];
16762         for(var i =0; i < cells.length; i++) {
16763             
16764             cells[i].row = cells[0].row;
16765             
16766             if(i == 0){
16767                 cells[i].row = cells[i].row + 1;
16768             }
16769             
16770             if (!crow) {
16771                 crow = {
16772                     start : cells[i],
16773                     end :  cells[i]
16774                 };
16775                 continue;
16776             }
16777             if (crow.start.getY() == cells[i].getY()) {
16778                 // on same row.
16779                 crow.end = cells[i];
16780                 continue;
16781             }
16782             // different row.
16783             rows.push(crow);
16784             crow = {
16785                 start: cells[i],
16786                 end : cells[i]
16787             };
16788             
16789         }
16790         
16791         rows.push(crow);
16792         ev.els = [];
16793         ev.rows = rows;
16794         ev.cells = cells;
16795         
16796         cells[0].events.push(ev);
16797         
16798         this.calevents.push(ev);
16799     },
16800     
16801     clearEvents: function() {
16802         
16803         if(!this.calevents){
16804             return;
16805         }
16806         
16807         Roo.each(this.cells.elements, function(c){
16808             c.row = 0;
16809             c.events = [];
16810             c.more = [];
16811         });
16812         
16813         Roo.each(this.calevents, function(e) {
16814             Roo.each(e.els, function(el) {
16815                 el.un('mouseenter' ,this.onEventEnter, this);
16816                 el.un('mouseleave' ,this.onEventLeave, this);
16817                 el.remove();
16818             },this);
16819         },this);
16820         
16821         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16822             e.remove();
16823         });
16824         
16825     },
16826     
16827     renderEvents: function()
16828     {   
16829         var _this = this;
16830         
16831         this.cells.each(function(c) {
16832             
16833             if(c.row < 5){
16834                 return;
16835             }
16836             
16837             var ev = c.events;
16838             
16839             var r = 4;
16840             if(c.row != c.events.length){
16841                 r = 4 - (4 - (c.row - c.events.length));
16842             }
16843             
16844             c.events = ev.slice(0, r);
16845             c.more = ev.slice(r);
16846             
16847             if(c.more.length && c.more.length == 1){
16848                 c.events.push(c.more.pop());
16849             }
16850             
16851             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16852             
16853         });
16854             
16855         this.cells.each(function(c) {
16856             
16857             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16858             
16859             
16860             for (var e = 0; e < c.events.length; e++){
16861                 var ev = c.events[e];
16862                 var rows = ev.rows;
16863                 
16864                 for(var i = 0; i < rows.length; i++) {
16865                 
16866                     // how many rows should it span..
16867
16868                     var  cfg = {
16869                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16870                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16871
16872                         unselectable : "on",
16873                         cn : [
16874                             {
16875                                 cls: 'fc-event-inner',
16876                                 cn : [
16877     //                                {
16878     //                                  tag:'span',
16879     //                                  cls: 'fc-event-time',
16880     //                                  html : cells.length > 1 ? '' : ev.time
16881     //                                },
16882                                     {
16883                                       tag:'span',
16884                                       cls: 'fc-event-title',
16885                                       html : String.format('{0}', ev.title)
16886                                     }
16887
16888
16889                                 ]
16890                             },
16891                             {
16892                                 cls: 'ui-resizable-handle ui-resizable-e',
16893                                 html : '&nbsp;&nbsp;&nbsp'
16894                             }
16895
16896                         ]
16897                     };
16898
16899                     if (i == 0) {
16900                         cfg.cls += ' fc-event-start';
16901                     }
16902                     if ((i+1) == rows.length) {
16903                         cfg.cls += ' fc-event-end';
16904                     }
16905
16906                     var ctr = _this.el.select('.fc-event-container',true).first();
16907                     var cg = ctr.createChild(cfg);
16908
16909                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16910                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16911
16912                     var r = (c.more.length) ? 1 : 0;
16913                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16914                     cg.setWidth(ebox.right - sbox.x -2);
16915
16916                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16917                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16918                     cg.on('click', _this.onEventClick, _this, ev);
16919
16920                     ev.els.push(cg);
16921                     
16922                 }
16923                 
16924             }
16925             
16926             
16927             if(c.more.length){
16928                 var  cfg = {
16929                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16930                     style : 'position: absolute',
16931                     unselectable : "on",
16932                     cn : [
16933                         {
16934                             cls: 'fc-event-inner',
16935                             cn : [
16936                                 {
16937                                   tag:'span',
16938                                   cls: 'fc-event-title',
16939                                   html : 'More'
16940                                 }
16941
16942
16943                             ]
16944                         },
16945                         {
16946                             cls: 'ui-resizable-handle ui-resizable-e',
16947                             html : '&nbsp;&nbsp;&nbsp'
16948                         }
16949
16950                     ]
16951                 };
16952
16953                 var ctr = _this.el.select('.fc-event-container',true).first();
16954                 var cg = ctr.createChild(cfg);
16955
16956                 var sbox = c.select('.fc-day-content',true).first().getBox();
16957                 var ebox = c.select('.fc-day-content',true).first().getBox();
16958                 //Roo.log(cg);
16959                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16960                 cg.setWidth(ebox.right - sbox.x -2);
16961
16962                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16963                 
16964             }
16965             
16966         });
16967         
16968         
16969         
16970     },
16971     
16972     onEventEnter: function (e, el,event,d) {
16973         this.fireEvent('evententer', this, el, event);
16974     },
16975     
16976     onEventLeave: function (e, el,event,d) {
16977         this.fireEvent('eventleave', this, el, event);
16978     },
16979     
16980     onEventClick: function (e, el,event,d) {
16981         this.fireEvent('eventclick', this, el, event);
16982     },
16983     
16984     onMonthChange: function () {
16985         this.store.load();
16986     },
16987     
16988     onMoreEventClick: function(e, el, more)
16989     {
16990         var _this = this;
16991         
16992         this.calpopover.placement = 'right';
16993         this.calpopover.setTitle('More');
16994         
16995         this.calpopover.setContent('');
16996         
16997         var ctr = this.calpopover.el.select('.popover-content', true).first();
16998         
16999         Roo.each(more, function(m){
17000             var cfg = {
17001                 cls : 'fc-event-hori fc-event-draggable',
17002                 html : m.title
17003             };
17004             var cg = ctr.createChild(cfg);
17005             
17006             cg.on('click', _this.onEventClick, _this, m);
17007         });
17008         
17009         this.calpopover.show(el);
17010         
17011         
17012     },
17013     
17014     onLoad: function () 
17015     {   
17016         this.calevents = [];
17017         var cal = this;
17018         
17019         if(this.store.getCount() > 0){
17020             this.store.data.each(function(d){
17021                cal.addItem({
17022                     id : d.data.id,
17023                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17024                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17025                     time : d.data.start_time,
17026                     title : d.data.title,
17027                     description : d.data.description,
17028                     venue : d.data.venue
17029                 });
17030             });
17031         }
17032         
17033         this.renderEvents();
17034         
17035         if(this.calevents.length && this.loadMask){
17036             this.maskEl.hide();
17037         }
17038     },
17039     
17040     onBeforeLoad: function()
17041     {
17042         this.clearEvents();
17043         if(this.loadMask){
17044             this.maskEl.show();
17045         }
17046     }
17047 });
17048
17049  
17050  /*
17051  * - LGPL
17052  *
17053  * element
17054  * 
17055  */
17056
17057 /**
17058  * @class Roo.bootstrap.Popover
17059  * @extends Roo.bootstrap.Component
17060  * Bootstrap Popover class
17061  * @cfg {String} html contents of the popover   (or false to use children..)
17062  * @cfg {String} title of popover (or false to hide)
17063  * @cfg {String} placement how it is placed
17064  * @cfg {String} trigger click || hover (or false to trigger manually)
17065  * @cfg {String} over what (parent or false to trigger manually.)
17066  * @cfg {Number} delay - delay before showing
17067  
17068  * @constructor
17069  * Create a new Popover
17070  * @param {Object} config The config object
17071  */
17072
17073 Roo.bootstrap.Popover = function(config){
17074     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17075     
17076     this.addEvents({
17077         // raw events
17078          /**
17079          * @event show
17080          * After the popover show
17081          * 
17082          * @param {Roo.bootstrap.Popover} this
17083          */
17084         "show" : true,
17085         /**
17086          * @event hide
17087          * After the popover hide
17088          * 
17089          * @param {Roo.bootstrap.Popover} this
17090          */
17091         "hide" : true
17092     });
17093 };
17094
17095 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17096     
17097     title: 'Fill in a title',
17098     html: false,
17099     
17100     placement : 'right',
17101     trigger : 'hover', // hover
17102     
17103     delay : 0,
17104     
17105     over: 'parent',
17106     
17107     can_build_overlaid : false,
17108     
17109     getChildContainer : function()
17110     {
17111         return this.el.select('.popover-content',true).first();
17112     },
17113     
17114     getAutoCreate : function(){
17115          
17116         var cfg = {
17117            cls : 'popover roo-dynamic',
17118            style: 'display:block',
17119            cn : [
17120                 {
17121                     cls : 'arrow'
17122                 },
17123                 {
17124                     cls : 'popover-inner',
17125                     cn : [
17126                         {
17127                             tag: 'h3',
17128                             cls: 'popover-title',
17129                             html : this.title
17130                         },
17131                         {
17132                             cls : 'popover-content',
17133                             html : this.html
17134                         }
17135                     ]
17136                     
17137                 }
17138            ]
17139         };
17140         
17141         return cfg;
17142     },
17143     setTitle: function(str)
17144     {
17145         this.title = str;
17146         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17147     },
17148     setContent: function(str)
17149     {
17150         this.html = str;
17151         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17152     },
17153     // as it get's added to the bottom of the page.
17154     onRender : function(ct, position)
17155     {
17156         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17157         if(!this.el){
17158             var cfg = Roo.apply({},  this.getAutoCreate());
17159             cfg.id = Roo.id();
17160             
17161             if (this.cls) {
17162                 cfg.cls += ' ' + this.cls;
17163             }
17164             if (this.style) {
17165                 cfg.style = this.style;
17166             }
17167             //Roo.log("adding to ");
17168             this.el = Roo.get(document.body).createChild(cfg, position);
17169 //            Roo.log(this.el);
17170         }
17171         this.initEvents();
17172     },
17173     
17174     initEvents : function()
17175     {
17176         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17177         this.el.enableDisplayMode('block');
17178         this.el.hide();
17179         if (this.over === false) {
17180             return; 
17181         }
17182         if (this.triggers === false) {
17183             return;
17184         }
17185         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17186         var triggers = this.trigger ? this.trigger.split(' ') : [];
17187         Roo.each(triggers, function(trigger) {
17188         
17189             if (trigger == 'click') {
17190                 on_el.on('click', this.toggle, this);
17191             } else if (trigger != 'manual') {
17192                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17193                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17194       
17195                 on_el.on(eventIn  ,this.enter, this);
17196                 on_el.on(eventOut, this.leave, this);
17197             }
17198         }, this);
17199         
17200     },
17201     
17202     
17203     // private
17204     timeout : null,
17205     hoverState : null,
17206     
17207     toggle : function () {
17208         this.hoverState == 'in' ? this.leave() : this.enter();
17209     },
17210     
17211     enter : function () {
17212         
17213         clearTimeout(this.timeout);
17214     
17215         this.hoverState = 'in';
17216     
17217         if (!this.delay || !this.delay.show) {
17218             this.show();
17219             return;
17220         }
17221         var _t = this;
17222         this.timeout = setTimeout(function () {
17223             if (_t.hoverState == 'in') {
17224                 _t.show();
17225             }
17226         }, this.delay.show)
17227     },
17228     
17229     leave : function() {
17230         clearTimeout(this.timeout);
17231     
17232         this.hoverState = 'out';
17233     
17234         if (!this.delay || !this.delay.hide) {
17235             this.hide();
17236             return;
17237         }
17238         var _t = this;
17239         this.timeout = setTimeout(function () {
17240             if (_t.hoverState == 'out') {
17241                 _t.hide();
17242             }
17243         }, this.delay.hide)
17244     },
17245     
17246     show : function (on_el)
17247     {
17248         if (!on_el) {
17249             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17250         }
17251         
17252         // set content.
17253         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17254         if (this.html !== false) {
17255             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17256         }
17257         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17258         if (!this.title.length) {
17259             this.el.select('.popover-title',true).hide();
17260         }
17261         
17262         var placement = typeof this.placement == 'function' ?
17263             this.placement.call(this, this.el, on_el) :
17264             this.placement;
17265             
17266         var autoToken = /\s?auto?\s?/i;
17267         var autoPlace = autoToken.test(placement);
17268         if (autoPlace) {
17269             placement = placement.replace(autoToken, '') || 'top';
17270         }
17271         
17272         //this.el.detach()
17273         //this.el.setXY([0,0]);
17274         this.el.show();
17275         this.el.dom.style.display='block';
17276         this.el.addClass(placement);
17277         
17278         //this.el.appendTo(on_el);
17279         
17280         var p = this.getPosition();
17281         var box = this.el.getBox();
17282         
17283         if (autoPlace) {
17284             // fixme..
17285         }
17286         var align = Roo.bootstrap.Popover.alignment[placement];
17287         this.el.alignTo(on_el, align[0],align[1]);
17288         //var arrow = this.el.select('.arrow',true).first();
17289         //arrow.set(align[2], 
17290         
17291         this.el.addClass('in');
17292         
17293         
17294         if (this.el.hasClass('fade')) {
17295             // fade it?
17296         }
17297         
17298         this.hoverState = 'in';
17299         
17300         this.fireEvent('show', this);
17301         
17302     },
17303     hide : function()
17304     {
17305         this.el.setXY([0,0]);
17306         this.el.removeClass('in');
17307         this.el.hide();
17308         this.hoverState = null;
17309         
17310         this.fireEvent('hide', this);
17311     }
17312     
17313 });
17314
17315 Roo.bootstrap.Popover.alignment = {
17316     'left' : ['r-l', [-10,0], 'right'],
17317     'right' : ['l-r', [10,0], 'left'],
17318     'bottom' : ['t-b', [0,10], 'top'],
17319     'top' : [ 'b-t', [0,-10], 'bottom']
17320 };
17321
17322  /*
17323  * - LGPL
17324  *
17325  * Progress
17326  * 
17327  */
17328
17329 /**
17330  * @class Roo.bootstrap.Progress
17331  * @extends Roo.bootstrap.Component
17332  * Bootstrap Progress class
17333  * @cfg {Boolean} striped striped of the progress bar
17334  * @cfg {Boolean} active animated of the progress bar
17335  * 
17336  * 
17337  * @constructor
17338  * Create a new Progress
17339  * @param {Object} config The config object
17340  */
17341
17342 Roo.bootstrap.Progress = function(config){
17343     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17344 };
17345
17346 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17347     
17348     striped : false,
17349     active: false,
17350     
17351     getAutoCreate : function(){
17352         var cfg = {
17353             tag: 'div',
17354             cls: 'progress'
17355         };
17356         
17357         
17358         if(this.striped){
17359             cfg.cls += ' progress-striped';
17360         }
17361       
17362         if(this.active){
17363             cfg.cls += ' active';
17364         }
17365         
17366         
17367         return cfg;
17368     }
17369    
17370 });
17371
17372  
17373
17374  /*
17375  * - LGPL
17376  *
17377  * ProgressBar
17378  * 
17379  */
17380
17381 /**
17382  * @class Roo.bootstrap.ProgressBar
17383  * @extends Roo.bootstrap.Component
17384  * Bootstrap ProgressBar class
17385  * @cfg {Number} aria_valuenow aria-value now
17386  * @cfg {Number} aria_valuemin aria-value min
17387  * @cfg {Number} aria_valuemax aria-value max
17388  * @cfg {String} label label for the progress bar
17389  * @cfg {String} panel (success | info | warning | danger )
17390  * @cfg {String} role role of the progress bar
17391  * @cfg {String} sr_only text
17392  * 
17393  * 
17394  * @constructor
17395  * Create a new ProgressBar
17396  * @param {Object} config The config object
17397  */
17398
17399 Roo.bootstrap.ProgressBar = function(config){
17400     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17401 };
17402
17403 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17404     
17405     aria_valuenow : 0,
17406     aria_valuemin : 0,
17407     aria_valuemax : 100,
17408     label : false,
17409     panel : false,
17410     role : false,
17411     sr_only: false,
17412     
17413     getAutoCreate : function()
17414     {
17415         
17416         var cfg = {
17417             tag: 'div',
17418             cls: 'progress-bar',
17419             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17420         };
17421         
17422         if(this.sr_only){
17423             cfg.cn = {
17424                 tag: 'span',
17425                 cls: 'sr-only',
17426                 html: this.sr_only
17427             }
17428         }
17429         
17430         if(this.role){
17431             cfg.role = this.role;
17432         }
17433         
17434         if(this.aria_valuenow){
17435             cfg['aria-valuenow'] = this.aria_valuenow;
17436         }
17437         
17438         if(this.aria_valuemin){
17439             cfg['aria-valuemin'] = this.aria_valuemin;
17440         }
17441         
17442         if(this.aria_valuemax){
17443             cfg['aria-valuemax'] = this.aria_valuemax;
17444         }
17445         
17446         if(this.label && !this.sr_only){
17447             cfg.html = this.label;
17448         }
17449         
17450         if(this.panel){
17451             cfg.cls += ' progress-bar-' + this.panel;
17452         }
17453         
17454         return cfg;
17455     },
17456     
17457     update : function(aria_valuenow)
17458     {
17459         this.aria_valuenow = aria_valuenow;
17460         
17461         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17462     }
17463    
17464 });
17465
17466  
17467
17468  /*
17469  * - LGPL
17470  *
17471  * column
17472  * 
17473  */
17474
17475 /**
17476  * @class Roo.bootstrap.TabGroup
17477  * @extends Roo.bootstrap.Column
17478  * Bootstrap Column class
17479  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17480  * @cfg {Boolean} carousel true to make the group behave like a carousel
17481  * @cfg {Boolean} bullets show bullets for the panels
17482  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17483  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17484  * @cfg {Boolean} showarrow (true|false) show arrow default true
17485  * 
17486  * @constructor
17487  * Create a new TabGroup
17488  * @param {Object} config The config object
17489  */
17490
17491 Roo.bootstrap.TabGroup = function(config){
17492     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17493     if (!this.navId) {
17494         this.navId = Roo.id();
17495     }
17496     this.tabs = [];
17497     Roo.bootstrap.TabGroup.register(this);
17498     
17499 };
17500
17501 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17502     
17503     carousel : false,
17504     transition : false,
17505     bullets : 0,
17506     timer : 0,
17507     autoslide : false,
17508     slideFn : false,
17509     slideOnTouch : false,
17510     showarrow : true,
17511     
17512     getAutoCreate : function()
17513     {
17514         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17515         
17516         cfg.cls += ' tab-content';
17517         
17518         if (this.carousel) {
17519             cfg.cls += ' carousel slide';
17520             
17521             cfg.cn = [{
17522                cls : 'carousel-inner',
17523                cn : []
17524             }];
17525         
17526             if(this.bullets  && !Roo.isTouch){
17527                 
17528                 var bullets = {
17529                     cls : 'carousel-bullets',
17530                     cn : []
17531                 };
17532                
17533                 if(this.bullets_cls){
17534                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17535                 }
17536                 
17537                 bullets.cn.push({
17538                     cls : 'clear'
17539                 });
17540                 
17541                 cfg.cn[0].cn.push(bullets);
17542             }
17543             
17544             if(this.showarrow){
17545                 cfg.cn[0].cn.push({
17546                     tag : 'div',
17547                     class : 'carousel-arrow',
17548                     cn : [
17549                         {
17550                             tag : 'div',
17551                             class : 'carousel-prev',
17552                             cn : [
17553                                 {
17554                                     tag : 'i',
17555                                     class : 'fa fa-chevron-left'
17556                                 }
17557                             ]
17558                         },
17559                         {
17560                             tag : 'div',
17561                             class : 'carousel-next',
17562                             cn : [
17563                                 {
17564                                     tag : 'i',
17565                                     class : 'fa fa-chevron-right'
17566                                 }
17567                             ]
17568                         }
17569                     ]
17570                 });
17571             }
17572             
17573         }
17574         
17575         return cfg;
17576     },
17577     
17578     initEvents:  function()
17579     {
17580 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17581 //            this.el.on("touchstart", this.onTouchStart, this);
17582 //        }
17583         
17584         if(this.autoslide){
17585             var _this = this;
17586             
17587             this.slideFn = window.setInterval(function() {
17588                 _this.showPanelNext();
17589             }, this.timer);
17590         }
17591         
17592         if(this.showarrow){
17593             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17594             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17595         }
17596         
17597         
17598     },
17599     
17600 //    onTouchStart : function(e, el, o)
17601 //    {
17602 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17603 //            return;
17604 //        }
17605 //        
17606 //        this.showPanelNext();
17607 //    },
17608     
17609     
17610     getChildContainer : function()
17611     {
17612         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17613     },
17614     
17615     /**
17616     * register a Navigation item
17617     * @param {Roo.bootstrap.NavItem} the navitem to add
17618     */
17619     register : function(item)
17620     {
17621         this.tabs.push( item);
17622         item.navId = this.navId; // not really needed..
17623         this.addBullet();
17624     
17625     },
17626     
17627     getActivePanel : function()
17628     {
17629         var r = false;
17630         Roo.each(this.tabs, function(t) {
17631             if (t.active) {
17632                 r = t;
17633                 return false;
17634             }
17635             return null;
17636         });
17637         return r;
17638         
17639     },
17640     getPanelByName : function(n)
17641     {
17642         var r = false;
17643         Roo.each(this.tabs, function(t) {
17644             if (t.tabId == n) {
17645                 r = t;
17646                 return false;
17647             }
17648             return null;
17649         });
17650         return r;
17651     },
17652     indexOfPanel : function(p)
17653     {
17654         var r = false;
17655         Roo.each(this.tabs, function(t,i) {
17656             if (t.tabId == p.tabId) {
17657                 r = i;
17658                 return false;
17659             }
17660             return null;
17661         });
17662         return r;
17663     },
17664     /**
17665      * show a specific panel
17666      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17667      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17668      */
17669     showPanel : function (pan)
17670     {
17671         if(this.transition || typeof(pan) == 'undefined'){
17672             Roo.log("waiting for the transitionend");
17673             return;
17674         }
17675         
17676         if (typeof(pan) == 'number') {
17677             pan = this.tabs[pan];
17678         }
17679         
17680         if (typeof(pan) == 'string') {
17681             pan = this.getPanelByName(pan);
17682         }
17683         
17684         var cur = this.getActivePanel();
17685         
17686         if(!pan || !cur){
17687             Roo.log('pan or acitve pan is undefined');
17688             return false;
17689         }
17690         
17691         if (pan.tabId == this.getActivePanel().tabId) {
17692             return true;
17693         }
17694         
17695         if (false === cur.fireEvent('beforedeactivate')) {
17696             return false;
17697         }
17698         
17699         if(this.bullets > 0 && !Roo.isTouch){
17700             this.setActiveBullet(this.indexOfPanel(pan));
17701         }
17702         
17703         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17704             
17705             this.transition = true;
17706             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17707             var lr = dir == 'next' ? 'left' : 'right';
17708             pan.el.addClass(dir); // or prev
17709             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17710             cur.el.addClass(lr); // or right
17711             pan.el.addClass(lr);
17712             
17713             var _this = this;
17714             cur.el.on('transitionend', function() {
17715                 Roo.log("trans end?");
17716                 
17717                 pan.el.removeClass([lr,dir]);
17718                 pan.setActive(true);
17719                 
17720                 cur.el.removeClass([lr]);
17721                 cur.setActive(false);
17722                 
17723                 _this.transition = false;
17724                 
17725             }, this, { single:  true } );
17726             
17727             return true;
17728         }
17729         
17730         cur.setActive(false);
17731         pan.setActive(true);
17732         
17733         return true;
17734         
17735     },
17736     showPanelNext : function()
17737     {
17738         var i = this.indexOfPanel(this.getActivePanel());
17739         
17740         if (i >= this.tabs.length - 1 && !this.autoslide) {
17741             return;
17742         }
17743         
17744         if (i >= this.tabs.length - 1 && this.autoslide) {
17745             i = -1;
17746         }
17747         
17748         this.showPanel(this.tabs[i+1]);
17749     },
17750     
17751     showPanelPrev : function()
17752     {
17753         var i = this.indexOfPanel(this.getActivePanel());
17754         
17755         if (i  < 1 && !this.autoslide) {
17756             return;
17757         }
17758         
17759         if (i < 1 && this.autoslide) {
17760             i = this.tabs.length;
17761         }
17762         
17763         this.showPanel(this.tabs[i-1]);
17764     },
17765     
17766     
17767     addBullet: function()
17768     {
17769         if(!this.bullets || Roo.isTouch){
17770             return;
17771         }
17772         var ctr = this.el.select('.carousel-bullets',true).first();
17773         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17774         var bullet = ctr.createChild({
17775             cls : 'bullet bullet-' + i
17776         },ctr.dom.lastChild);
17777         
17778         
17779         var _this = this;
17780         
17781         bullet.on('click', (function(e, el, o, ii, t){
17782
17783             e.preventDefault();
17784
17785             this.showPanel(ii);
17786
17787             if(this.autoslide && this.slideFn){
17788                 clearInterval(this.slideFn);
17789                 this.slideFn = window.setInterval(function() {
17790                     _this.showPanelNext();
17791                 }, this.timer);
17792             }
17793
17794         }).createDelegate(this, [i, bullet], true));
17795                 
17796         
17797     },
17798      
17799     setActiveBullet : function(i)
17800     {
17801         if(Roo.isTouch){
17802             return;
17803         }
17804         
17805         Roo.each(this.el.select('.bullet', true).elements, function(el){
17806             el.removeClass('selected');
17807         });
17808
17809         var bullet = this.el.select('.bullet-' + i, true).first();
17810         
17811         if(!bullet){
17812             return;
17813         }
17814         
17815         bullet.addClass('selected');
17816     }
17817     
17818     
17819   
17820 });
17821
17822  
17823
17824  
17825  
17826 Roo.apply(Roo.bootstrap.TabGroup, {
17827     
17828     groups: {},
17829      /**
17830     * register a Navigation Group
17831     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17832     */
17833     register : function(navgrp)
17834     {
17835         this.groups[navgrp.navId] = navgrp;
17836         
17837     },
17838     /**
17839     * fetch a Navigation Group based on the navigation ID
17840     * if one does not exist , it will get created.
17841     * @param {string} the navgroup to add
17842     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17843     */
17844     get: function(navId) {
17845         if (typeof(this.groups[navId]) == 'undefined') {
17846             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17847         }
17848         return this.groups[navId] ;
17849     }
17850     
17851     
17852     
17853 });
17854
17855  /*
17856  * - LGPL
17857  *
17858  * TabPanel
17859  * 
17860  */
17861
17862 /**
17863  * @class Roo.bootstrap.TabPanel
17864  * @extends Roo.bootstrap.Component
17865  * Bootstrap TabPanel class
17866  * @cfg {Boolean} active panel active
17867  * @cfg {String} html panel content
17868  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17869  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17870  * @cfg {String} href click to link..
17871  * 
17872  * 
17873  * @constructor
17874  * Create a new TabPanel
17875  * @param {Object} config The config object
17876  */
17877
17878 Roo.bootstrap.TabPanel = function(config){
17879     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17880     this.addEvents({
17881         /**
17882              * @event changed
17883              * Fires when the active status changes
17884              * @param {Roo.bootstrap.TabPanel} this
17885              * @param {Boolean} state the new state
17886             
17887          */
17888         'changed': true,
17889         /**
17890              * @event beforedeactivate
17891              * Fires before a tab is de-activated - can be used to do validation on a form.
17892              * @param {Roo.bootstrap.TabPanel} this
17893              * @return {Boolean} false if there is an error
17894             
17895          */
17896         'beforedeactivate': true
17897      });
17898     
17899     this.tabId = this.tabId || Roo.id();
17900   
17901 };
17902
17903 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17904     
17905     active: false,
17906     html: false,
17907     tabId: false,
17908     navId : false,
17909     href : '',
17910     
17911     getAutoCreate : function(){
17912         var cfg = {
17913             tag: 'div',
17914             // item is needed for carousel - not sure if it has any effect otherwise
17915             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17916             html: this.html || ''
17917         };
17918         
17919         if(this.active){
17920             cfg.cls += ' active';
17921         }
17922         
17923         if(this.tabId){
17924             cfg.tabId = this.tabId;
17925         }
17926         
17927         
17928         return cfg;
17929     },
17930     
17931     initEvents:  function()
17932     {
17933         var p = this.parent();
17934         
17935         this.navId = this.navId || p.navId;
17936         
17937         if (typeof(this.navId) != 'undefined') {
17938             // not really needed.. but just in case.. parent should be a NavGroup.
17939             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17940             
17941             tg.register(this);
17942             
17943             var i = tg.tabs.length - 1;
17944             
17945             if(this.active && tg.bullets > 0 && i < tg.bullets){
17946                 tg.setActiveBullet(i);
17947             }
17948         }
17949         
17950         this.el.on('click', this.onClick, this);
17951         
17952         if(Roo.isTouch){
17953             this.el.on("touchstart", this.onTouchStart, this);
17954             this.el.on("touchmove", this.onTouchMove, this);
17955             this.el.on("touchend", this.onTouchEnd, this);
17956         }
17957         
17958     },
17959     
17960     onRender : function(ct, position)
17961     {
17962         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17963     },
17964     
17965     setActive : function(state)
17966     {
17967         Roo.log("panel - set active " + this.tabId + "=" + state);
17968         
17969         this.active = state;
17970         if (!state) {
17971             this.el.removeClass('active');
17972             
17973         } else  if (!this.el.hasClass('active')) {
17974             this.el.addClass('active');
17975         }
17976         
17977         this.fireEvent('changed', this, state);
17978     },
17979     
17980     onClick : function(e)
17981     {
17982         e.preventDefault();
17983         
17984         if(!this.href.length){
17985             return;
17986         }
17987         
17988         window.location.href = this.href;
17989     },
17990     
17991     startX : 0,
17992     startY : 0,
17993     endX : 0,
17994     endY : 0,
17995     swiping : false,
17996     
17997     onTouchStart : function(e)
17998     {
17999         this.swiping = false;
18000         
18001         this.startX = e.browserEvent.touches[0].clientX;
18002         this.startY = e.browserEvent.touches[0].clientY;
18003     },
18004     
18005     onTouchMove : function(e)
18006     {
18007         this.swiping = true;
18008         
18009         this.endX = e.browserEvent.touches[0].clientX;
18010         this.endY = e.browserEvent.touches[0].clientY;
18011     },
18012     
18013     onTouchEnd : function(e)
18014     {
18015         if(!this.swiping){
18016             this.onClick(e);
18017             return;
18018         }
18019         
18020         var tabGroup = this.parent();
18021         
18022         if(this.endX > this.startX){ // swiping right
18023             tabGroup.showPanelPrev();
18024             return;
18025         }
18026         
18027         if(this.startX > this.endX){ // swiping left
18028             tabGroup.showPanelNext();
18029             return;
18030         }
18031     }
18032     
18033     
18034 });
18035  
18036
18037  
18038
18039  /*
18040  * - LGPL
18041  *
18042  * DateField
18043  * 
18044  */
18045
18046 /**
18047  * @class Roo.bootstrap.DateField
18048  * @extends Roo.bootstrap.Input
18049  * Bootstrap DateField class
18050  * @cfg {Number} weekStart default 0
18051  * @cfg {String} viewMode default empty, (months|years)
18052  * @cfg {String} minViewMode default empty, (months|years)
18053  * @cfg {Number} startDate default -Infinity
18054  * @cfg {Number} endDate default Infinity
18055  * @cfg {Boolean} todayHighlight default false
18056  * @cfg {Boolean} todayBtn default false
18057  * @cfg {Boolean} calendarWeeks default false
18058  * @cfg {Object} daysOfWeekDisabled default empty
18059  * @cfg {Boolean} singleMode default false (true | false)
18060  * 
18061  * @cfg {Boolean} keyboardNavigation default true
18062  * @cfg {String} language default en
18063  * 
18064  * @constructor
18065  * Create a new DateField
18066  * @param {Object} config The config object
18067  */
18068
18069 Roo.bootstrap.DateField = function(config){
18070     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18071      this.addEvents({
18072             /**
18073              * @event show
18074              * Fires when this field show.
18075              * @param {Roo.bootstrap.DateField} this
18076              * @param {Mixed} date The date value
18077              */
18078             show : true,
18079             /**
18080              * @event show
18081              * Fires when this field hide.
18082              * @param {Roo.bootstrap.DateField} this
18083              * @param {Mixed} date The date value
18084              */
18085             hide : true,
18086             /**
18087              * @event select
18088              * Fires when select a date.
18089              * @param {Roo.bootstrap.DateField} this
18090              * @param {Mixed} date The date value
18091              */
18092             select : true,
18093             /**
18094              * @event beforeselect
18095              * Fires when before select a date.
18096              * @param {Roo.bootstrap.DateField} this
18097              * @param {Mixed} date The date value
18098              */
18099             beforeselect : true
18100         });
18101 };
18102
18103 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18104     
18105     /**
18106      * @cfg {String} format
18107      * The default date format string which can be overriden for localization support.  The format must be
18108      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18109      */
18110     format : "m/d/y",
18111     /**
18112      * @cfg {String} altFormats
18113      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18114      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18115      */
18116     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18117     
18118     weekStart : 0,
18119     
18120     viewMode : '',
18121     
18122     minViewMode : '',
18123     
18124     todayHighlight : false,
18125     
18126     todayBtn: false,
18127     
18128     language: 'en',
18129     
18130     keyboardNavigation: true,
18131     
18132     calendarWeeks: false,
18133     
18134     startDate: -Infinity,
18135     
18136     endDate: Infinity,
18137     
18138     daysOfWeekDisabled: [],
18139     
18140     _events: [],
18141     
18142     singleMode : false,
18143     
18144     UTCDate: function()
18145     {
18146         return new Date(Date.UTC.apply(Date, arguments));
18147     },
18148     
18149     UTCToday: function()
18150     {
18151         var today = new Date();
18152         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18153     },
18154     
18155     getDate: function() {
18156             var d = this.getUTCDate();
18157             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18158     },
18159     
18160     getUTCDate: function() {
18161             return this.date;
18162     },
18163     
18164     setDate: function(d) {
18165             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18166     },
18167     
18168     setUTCDate: function(d) {
18169             this.date = d;
18170             this.setValue(this.formatDate(this.date));
18171     },
18172         
18173     onRender: function(ct, position)
18174     {
18175         
18176         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18177         
18178         this.language = this.language || 'en';
18179         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18180         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18181         
18182         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18183         this.format = this.format || 'm/d/y';
18184         this.isInline = false;
18185         this.isInput = true;
18186         this.component = this.el.select('.add-on', true).first() || false;
18187         this.component = (this.component && this.component.length === 0) ? false : this.component;
18188         this.hasInput = this.component && this.inputEl().length;
18189         
18190         if (typeof(this.minViewMode === 'string')) {
18191             switch (this.minViewMode) {
18192                 case 'months':
18193                     this.minViewMode = 1;
18194                     break;
18195                 case 'years':
18196                     this.minViewMode = 2;
18197                     break;
18198                 default:
18199                     this.minViewMode = 0;
18200                     break;
18201             }
18202         }
18203         
18204         if (typeof(this.viewMode === 'string')) {
18205             switch (this.viewMode) {
18206                 case 'months':
18207                     this.viewMode = 1;
18208                     break;
18209                 case 'years':
18210                     this.viewMode = 2;
18211                     break;
18212                 default:
18213                     this.viewMode = 0;
18214                     break;
18215             }
18216         }
18217                 
18218         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18219         
18220 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18221         
18222         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18223         
18224         this.picker().on('mousedown', this.onMousedown, this);
18225         this.picker().on('click', this.onClick, this);
18226         
18227         this.picker().addClass('datepicker-dropdown');
18228         
18229         this.startViewMode = this.viewMode;
18230         
18231         if(this.singleMode){
18232             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18233                 v.setVisibilityMode(Roo.Element.DISPLAY);
18234                 v.hide();
18235             });
18236             
18237             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18238                 v.setStyle('width', '189px');
18239             });
18240         }
18241         
18242         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18243             if(!this.calendarWeeks){
18244                 v.remove();
18245                 return;
18246             }
18247             
18248             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18249             v.attr('colspan', function(i, val){
18250                 return parseInt(val) + 1;
18251             });
18252         });
18253                         
18254         
18255         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18256         
18257         this.setStartDate(this.startDate);
18258         this.setEndDate(this.endDate);
18259         
18260         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18261         
18262         this.fillDow();
18263         this.fillMonths();
18264         this.update();
18265         this.showMode();
18266         
18267         if(this.isInline) {
18268             this.show();
18269         }
18270     },
18271     
18272     picker : function()
18273     {
18274         return this.pickerEl;
18275 //        return this.el.select('.datepicker', true).first();
18276     },
18277     
18278     fillDow: function()
18279     {
18280         var dowCnt = this.weekStart;
18281         
18282         var dow = {
18283             tag: 'tr',
18284             cn: [
18285                 
18286             ]
18287         };
18288         
18289         if(this.calendarWeeks){
18290             dow.cn.push({
18291                 tag: 'th',
18292                 cls: 'cw',
18293                 html: '&nbsp;'
18294             })
18295         }
18296         
18297         while (dowCnt < this.weekStart + 7) {
18298             dow.cn.push({
18299                 tag: 'th',
18300                 cls: 'dow',
18301                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18302             });
18303         }
18304         
18305         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18306     },
18307     
18308     fillMonths: function()
18309     {    
18310         var i = 0;
18311         var months = this.picker().select('>.datepicker-months td', true).first();
18312         
18313         months.dom.innerHTML = '';
18314         
18315         while (i < 12) {
18316             var month = {
18317                 tag: 'span',
18318                 cls: 'month',
18319                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18320             };
18321             
18322             months.createChild(month);
18323         }
18324         
18325     },
18326     
18327     update: function()
18328     {
18329         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;
18330         
18331         if (this.date < this.startDate) {
18332             this.viewDate = new Date(this.startDate);
18333         } else if (this.date > this.endDate) {
18334             this.viewDate = new Date(this.endDate);
18335         } else {
18336             this.viewDate = new Date(this.date);
18337         }
18338         
18339         this.fill();
18340     },
18341     
18342     fill: function() 
18343     {
18344         var d = new Date(this.viewDate),
18345                 year = d.getUTCFullYear(),
18346                 month = d.getUTCMonth(),
18347                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18348                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18349                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18350                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18351                 currentDate = this.date && this.date.valueOf(),
18352                 today = this.UTCToday();
18353         
18354         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18355         
18356 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18357         
18358 //        this.picker.select('>tfoot th.today').
18359 //                                              .text(dates[this.language].today)
18360 //                                              .toggle(this.todayBtn !== false);
18361     
18362         this.updateNavArrows();
18363         this.fillMonths();
18364                                                 
18365         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18366         
18367         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18368          
18369         prevMonth.setUTCDate(day);
18370         
18371         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18372         
18373         var nextMonth = new Date(prevMonth);
18374         
18375         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18376         
18377         nextMonth = nextMonth.valueOf();
18378         
18379         var fillMonths = false;
18380         
18381         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18382         
18383         while(prevMonth.valueOf() < nextMonth) {
18384             var clsName = '';
18385             
18386             if (prevMonth.getUTCDay() === this.weekStart) {
18387                 if(fillMonths){
18388                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18389                 }
18390                     
18391                 fillMonths = {
18392                     tag: 'tr',
18393                     cn: []
18394                 };
18395                 
18396                 if(this.calendarWeeks){
18397                     // ISO 8601: First week contains first thursday.
18398                     // ISO also states week starts on Monday, but we can be more abstract here.
18399                     var
18400                     // Start of current week: based on weekstart/current date
18401                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18402                     // Thursday of this week
18403                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18404                     // First Thursday of year, year from thursday
18405                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18406                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18407                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18408                     
18409                     fillMonths.cn.push({
18410                         tag: 'td',
18411                         cls: 'cw',
18412                         html: calWeek
18413                     });
18414                 }
18415             }
18416             
18417             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18418                 clsName += ' old';
18419             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18420                 clsName += ' new';
18421             }
18422             if (this.todayHighlight &&
18423                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18424                 prevMonth.getUTCMonth() == today.getMonth() &&
18425                 prevMonth.getUTCDate() == today.getDate()) {
18426                 clsName += ' today';
18427             }
18428             
18429             if (currentDate && prevMonth.valueOf() === currentDate) {
18430                 clsName += ' active';
18431             }
18432             
18433             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18434                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18435                     clsName += ' disabled';
18436             }
18437             
18438             fillMonths.cn.push({
18439                 tag: 'td',
18440                 cls: 'day ' + clsName,
18441                 html: prevMonth.getDate()
18442             });
18443             
18444             prevMonth.setDate(prevMonth.getDate()+1);
18445         }
18446           
18447         var currentYear = this.date && this.date.getUTCFullYear();
18448         var currentMonth = this.date && this.date.getUTCMonth();
18449         
18450         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18451         
18452         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18453             v.removeClass('active');
18454             
18455             if(currentYear === year && k === currentMonth){
18456                 v.addClass('active');
18457             }
18458             
18459             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18460                 v.addClass('disabled');
18461             }
18462             
18463         });
18464         
18465         
18466         year = parseInt(year/10, 10) * 10;
18467         
18468         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18469         
18470         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18471         
18472         year -= 1;
18473         for (var i = -1; i < 11; i++) {
18474             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18475                 tag: 'span',
18476                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18477                 html: year
18478             });
18479             
18480             year += 1;
18481         }
18482     },
18483     
18484     showMode: function(dir) 
18485     {
18486         if (dir) {
18487             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18488         }
18489         
18490         Roo.each(this.picker().select('>div',true).elements, function(v){
18491             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18492             v.hide();
18493         });
18494         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18495     },
18496     
18497     place: function()
18498     {
18499         if(this.isInline) {
18500             return;
18501         }
18502         
18503         this.picker().removeClass(['bottom', 'top']);
18504         
18505         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18506             /*
18507              * place to the top of element!
18508              *
18509              */
18510             
18511             this.picker().addClass('top');
18512             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18513             
18514             return;
18515         }
18516         
18517         this.picker().addClass('bottom');
18518         
18519         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18520     },
18521     
18522     parseDate : function(value)
18523     {
18524         if(!value || value instanceof Date){
18525             return value;
18526         }
18527         var v = Date.parseDate(value, this.format);
18528         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18529             v = Date.parseDate(value, 'Y-m-d');
18530         }
18531         if(!v && this.altFormats){
18532             if(!this.altFormatsArray){
18533                 this.altFormatsArray = this.altFormats.split("|");
18534             }
18535             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18536                 v = Date.parseDate(value, this.altFormatsArray[i]);
18537             }
18538         }
18539         return v;
18540     },
18541     
18542     formatDate : function(date, fmt)
18543     {   
18544         return (!date || !(date instanceof Date)) ?
18545         date : date.dateFormat(fmt || this.format);
18546     },
18547     
18548     onFocus : function()
18549     {
18550         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18551         this.show();
18552     },
18553     
18554     onBlur : function()
18555     {
18556         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18557         
18558         var d = this.inputEl().getValue();
18559         
18560         this.setValue(d);
18561                 
18562         this.hide();
18563     },
18564     
18565     show : function()
18566     {
18567         this.picker().show();
18568         this.update();
18569         this.place();
18570         
18571         this.fireEvent('show', this, this.date);
18572     },
18573     
18574     hide : function()
18575     {
18576         if(this.isInline) {
18577             return;
18578         }
18579         this.picker().hide();
18580         this.viewMode = this.startViewMode;
18581         this.showMode();
18582         
18583         this.fireEvent('hide', this, this.date);
18584         
18585     },
18586     
18587     onMousedown: function(e)
18588     {
18589         e.stopPropagation();
18590         e.preventDefault();
18591     },
18592     
18593     keyup: function(e)
18594     {
18595         Roo.bootstrap.DateField.superclass.keyup.call(this);
18596         this.update();
18597     },
18598
18599     setValue: function(v)
18600     {
18601         if(this.fireEvent('beforeselect', this, v) !== false){
18602             var d = new Date(this.parseDate(v) ).clearTime();
18603         
18604             if(isNaN(d.getTime())){
18605                 this.date = this.viewDate = '';
18606                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18607                 return;
18608             }
18609
18610             v = this.formatDate(d);
18611
18612             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18613
18614             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18615
18616             this.update();
18617
18618             this.fireEvent('select', this, this.date);
18619         }
18620     },
18621     
18622     getValue: function()
18623     {
18624         return this.formatDate(this.date);
18625     },
18626     
18627     fireKey: function(e)
18628     {
18629         if (!this.picker().isVisible()){
18630             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18631                 this.show();
18632             }
18633             return;
18634         }
18635         
18636         var dateChanged = false,
18637         dir, day, month,
18638         newDate, newViewDate;
18639         
18640         switch(e.keyCode){
18641             case 27: // escape
18642                 this.hide();
18643                 e.preventDefault();
18644                 break;
18645             case 37: // left
18646             case 39: // right
18647                 if (!this.keyboardNavigation) {
18648                     break;
18649                 }
18650                 dir = e.keyCode == 37 ? -1 : 1;
18651                 
18652                 if (e.ctrlKey){
18653                     newDate = this.moveYear(this.date, dir);
18654                     newViewDate = this.moveYear(this.viewDate, dir);
18655                 } else if (e.shiftKey){
18656                     newDate = this.moveMonth(this.date, dir);
18657                     newViewDate = this.moveMonth(this.viewDate, dir);
18658                 } else {
18659                     newDate = new Date(this.date);
18660                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18661                     newViewDate = new Date(this.viewDate);
18662                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18663                 }
18664                 if (this.dateWithinRange(newDate)){
18665                     this.date = newDate;
18666                     this.viewDate = newViewDate;
18667                     this.setValue(this.formatDate(this.date));
18668 //                    this.update();
18669                     e.preventDefault();
18670                     dateChanged = true;
18671                 }
18672                 break;
18673             case 38: // up
18674             case 40: // down
18675                 if (!this.keyboardNavigation) {
18676                     break;
18677                 }
18678                 dir = e.keyCode == 38 ? -1 : 1;
18679                 if (e.ctrlKey){
18680                     newDate = this.moveYear(this.date, dir);
18681                     newViewDate = this.moveYear(this.viewDate, dir);
18682                 } else if (e.shiftKey){
18683                     newDate = this.moveMonth(this.date, dir);
18684                     newViewDate = this.moveMonth(this.viewDate, dir);
18685                 } else {
18686                     newDate = new Date(this.date);
18687                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18688                     newViewDate = new Date(this.viewDate);
18689                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18690                 }
18691                 if (this.dateWithinRange(newDate)){
18692                     this.date = newDate;
18693                     this.viewDate = newViewDate;
18694                     this.setValue(this.formatDate(this.date));
18695 //                    this.update();
18696                     e.preventDefault();
18697                     dateChanged = true;
18698                 }
18699                 break;
18700             case 13: // enter
18701                 this.setValue(this.formatDate(this.date));
18702                 this.hide();
18703                 e.preventDefault();
18704                 break;
18705             case 9: // tab
18706                 this.setValue(this.formatDate(this.date));
18707                 this.hide();
18708                 break;
18709             case 16: // shift
18710             case 17: // ctrl
18711             case 18: // alt
18712                 break;
18713             default :
18714                 this.hide();
18715                 
18716         }
18717     },
18718     
18719     
18720     onClick: function(e) 
18721     {
18722         e.stopPropagation();
18723         e.preventDefault();
18724         
18725         var target = e.getTarget();
18726         
18727         if(target.nodeName.toLowerCase() === 'i'){
18728             target = Roo.get(target).dom.parentNode;
18729         }
18730         
18731         var nodeName = target.nodeName;
18732         var className = target.className;
18733         var html = target.innerHTML;
18734         //Roo.log(nodeName);
18735         
18736         switch(nodeName.toLowerCase()) {
18737             case 'th':
18738                 switch(className) {
18739                     case 'switch':
18740                         this.showMode(1);
18741                         break;
18742                     case 'prev':
18743                     case 'next':
18744                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18745                         switch(this.viewMode){
18746                                 case 0:
18747                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18748                                         break;
18749                                 case 1:
18750                                 case 2:
18751                                         this.viewDate = this.moveYear(this.viewDate, dir);
18752                                         break;
18753                         }
18754                         this.fill();
18755                         break;
18756                     case 'today':
18757                         var date = new Date();
18758                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18759 //                        this.fill()
18760                         this.setValue(this.formatDate(this.date));
18761                         
18762                         this.hide();
18763                         break;
18764                 }
18765                 break;
18766             case 'span':
18767                 if (className.indexOf('disabled') < 0) {
18768                     this.viewDate.setUTCDate(1);
18769                     if (className.indexOf('month') > -1) {
18770                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18771                     } else {
18772                         var year = parseInt(html, 10) || 0;
18773                         this.viewDate.setUTCFullYear(year);
18774                         
18775                     }
18776                     
18777                     if(this.singleMode){
18778                         this.setValue(this.formatDate(this.viewDate));
18779                         this.hide();
18780                         return;
18781                     }
18782                     
18783                     this.showMode(-1);
18784                     this.fill();
18785                 }
18786                 break;
18787                 
18788             case 'td':
18789                 //Roo.log(className);
18790                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18791                     var day = parseInt(html, 10) || 1;
18792                     var year = this.viewDate.getUTCFullYear(),
18793                         month = this.viewDate.getUTCMonth();
18794
18795                     if (className.indexOf('old') > -1) {
18796                         if(month === 0 ){
18797                             month = 11;
18798                             year -= 1;
18799                         }else{
18800                             month -= 1;
18801                         }
18802                     } else if (className.indexOf('new') > -1) {
18803                         if (month == 11) {
18804                             month = 0;
18805                             year += 1;
18806                         } else {
18807                             month += 1;
18808                         }
18809                     }
18810                     //Roo.log([year,month,day]);
18811                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18812                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18813 //                    this.fill();
18814                     //Roo.log(this.formatDate(this.date));
18815                     this.setValue(this.formatDate(this.date));
18816                     this.hide();
18817                 }
18818                 break;
18819         }
18820     },
18821     
18822     setStartDate: function(startDate)
18823     {
18824         this.startDate = startDate || -Infinity;
18825         if (this.startDate !== -Infinity) {
18826             this.startDate = this.parseDate(this.startDate);
18827         }
18828         this.update();
18829         this.updateNavArrows();
18830     },
18831
18832     setEndDate: function(endDate)
18833     {
18834         this.endDate = endDate || Infinity;
18835         if (this.endDate !== Infinity) {
18836             this.endDate = this.parseDate(this.endDate);
18837         }
18838         this.update();
18839         this.updateNavArrows();
18840     },
18841     
18842     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18843     {
18844         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18845         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18846             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18847         }
18848         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18849             return parseInt(d, 10);
18850         });
18851         this.update();
18852         this.updateNavArrows();
18853     },
18854     
18855     updateNavArrows: function() 
18856     {
18857         if(this.singleMode){
18858             return;
18859         }
18860         
18861         var d = new Date(this.viewDate),
18862         year = d.getUTCFullYear(),
18863         month = d.getUTCMonth();
18864         
18865         Roo.each(this.picker().select('.prev', true).elements, function(v){
18866             v.show();
18867             switch (this.viewMode) {
18868                 case 0:
18869
18870                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18871                         v.hide();
18872                     }
18873                     break;
18874                 case 1:
18875                 case 2:
18876                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18877                         v.hide();
18878                     }
18879                     break;
18880             }
18881         });
18882         
18883         Roo.each(this.picker().select('.next', true).elements, function(v){
18884             v.show();
18885             switch (this.viewMode) {
18886                 case 0:
18887
18888                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18889                         v.hide();
18890                     }
18891                     break;
18892                 case 1:
18893                 case 2:
18894                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18895                         v.hide();
18896                     }
18897                     break;
18898             }
18899         })
18900     },
18901     
18902     moveMonth: function(date, dir)
18903     {
18904         if (!dir) {
18905             return date;
18906         }
18907         var new_date = new Date(date.valueOf()),
18908         day = new_date.getUTCDate(),
18909         month = new_date.getUTCMonth(),
18910         mag = Math.abs(dir),
18911         new_month, test;
18912         dir = dir > 0 ? 1 : -1;
18913         if (mag == 1){
18914             test = dir == -1
18915             // If going back one month, make sure month is not current month
18916             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18917             ? function(){
18918                 return new_date.getUTCMonth() == month;
18919             }
18920             // If going forward one month, make sure month is as expected
18921             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18922             : function(){
18923                 return new_date.getUTCMonth() != new_month;
18924             };
18925             new_month = month + dir;
18926             new_date.setUTCMonth(new_month);
18927             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18928             if (new_month < 0 || new_month > 11) {
18929                 new_month = (new_month + 12) % 12;
18930             }
18931         } else {
18932             // For magnitudes >1, move one month at a time...
18933             for (var i=0; i<mag; i++) {
18934                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18935                 new_date = this.moveMonth(new_date, dir);
18936             }
18937             // ...then reset the day, keeping it in the new month
18938             new_month = new_date.getUTCMonth();
18939             new_date.setUTCDate(day);
18940             test = function(){
18941                 return new_month != new_date.getUTCMonth();
18942             };
18943         }
18944         // Common date-resetting loop -- if date is beyond end of month, make it
18945         // end of month
18946         while (test()){
18947             new_date.setUTCDate(--day);
18948             new_date.setUTCMonth(new_month);
18949         }
18950         return new_date;
18951     },
18952
18953     moveYear: function(date, dir)
18954     {
18955         return this.moveMonth(date, dir*12);
18956     },
18957
18958     dateWithinRange: function(date)
18959     {
18960         return date >= this.startDate && date <= this.endDate;
18961     },
18962
18963     
18964     remove: function() 
18965     {
18966         this.picker().remove();
18967     },
18968     
18969     validateValue : function(value)
18970     {
18971         if(value.length < 1)  {
18972             if(this.allowBlank){
18973                 return true;
18974             }
18975             return false;
18976         }
18977         
18978         if(value.length < this.minLength){
18979             return false;
18980         }
18981         if(value.length > this.maxLength){
18982             return false;
18983         }
18984         if(this.vtype){
18985             var vt = Roo.form.VTypes;
18986             if(!vt[this.vtype](value, this)){
18987                 return false;
18988             }
18989         }
18990         if(typeof this.validator == "function"){
18991             var msg = this.validator(value);
18992             if(msg !== true){
18993                 return false;
18994             }
18995         }
18996         
18997         if(this.regex && !this.regex.test(value)){
18998             return false;
18999         }
19000         
19001         if(typeof(this.parseDate(value)) == 'undefined'){
19002             return false;
19003         }
19004         
19005         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19006             return false;
19007         }      
19008         
19009         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19010             return false;
19011         } 
19012         
19013         
19014         return true;
19015     }
19016    
19017 });
19018
19019 Roo.apply(Roo.bootstrap.DateField,  {
19020     
19021     head : {
19022         tag: 'thead',
19023         cn: [
19024         {
19025             tag: 'tr',
19026             cn: [
19027             {
19028                 tag: 'th',
19029                 cls: 'prev',
19030                 html: '<i class="fa fa-arrow-left"/>'
19031             },
19032             {
19033                 tag: 'th',
19034                 cls: 'switch',
19035                 colspan: '5'
19036             },
19037             {
19038                 tag: 'th',
19039                 cls: 'next',
19040                 html: '<i class="fa fa-arrow-right"/>'
19041             }
19042
19043             ]
19044         }
19045         ]
19046     },
19047     
19048     content : {
19049         tag: 'tbody',
19050         cn: [
19051         {
19052             tag: 'tr',
19053             cn: [
19054             {
19055                 tag: 'td',
19056                 colspan: '7'
19057             }
19058             ]
19059         }
19060         ]
19061     },
19062     
19063     footer : {
19064         tag: 'tfoot',
19065         cn: [
19066         {
19067             tag: 'tr',
19068             cn: [
19069             {
19070                 tag: 'th',
19071                 colspan: '7',
19072                 cls: 'today'
19073             }
19074                     
19075             ]
19076         }
19077         ]
19078     },
19079     
19080     dates:{
19081         en: {
19082             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19083             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19084             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19085             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19086             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19087             today: "Today"
19088         }
19089     },
19090     
19091     modes: [
19092     {
19093         clsName: 'days',
19094         navFnc: 'Month',
19095         navStep: 1
19096     },
19097     {
19098         clsName: 'months',
19099         navFnc: 'FullYear',
19100         navStep: 1
19101     },
19102     {
19103         clsName: 'years',
19104         navFnc: 'FullYear',
19105         navStep: 10
19106     }]
19107 });
19108
19109 Roo.apply(Roo.bootstrap.DateField,  {
19110   
19111     template : {
19112         tag: 'div',
19113         cls: 'datepicker dropdown-menu roo-dynamic',
19114         cn: [
19115         {
19116             tag: 'div',
19117             cls: 'datepicker-days',
19118             cn: [
19119             {
19120                 tag: 'table',
19121                 cls: 'table-condensed',
19122                 cn:[
19123                 Roo.bootstrap.DateField.head,
19124                 {
19125                     tag: 'tbody'
19126                 },
19127                 Roo.bootstrap.DateField.footer
19128                 ]
19129             }
19130             ]
19131         },
19132         {
19133             tag: 'div',
19134             cls: 'datepicker-months',
19135             cn: [
19136             {
19137                 tag: 'table',
19138                 cls: 'table-condensed',
19139                 cn:[
19140                 Roo.bootstrap.DateField.head,
19141                 Roo.bootstrap.DateField.content,
19142                 Roo.bootstrap.DateField.footer
19143                 ]
19144             }
19145             ]
19146         },
19147         {
19148             tag: 'div',
19149             cls: 'datepicker-years',
19150             cn: [
19151             {
19152                 tag: 'table',
19153                 cls: 'table-condensed',
19154                 cn:[
19155                 Roo.bootstrap.DateField.head,
19156                 Roo.bootstrap.DateField.content,
19157                 Roo.bootstrap.DateField.footer
19158                 ]
19159             }
19160             ]
19161         }
19162         ]
19163     }
19164 });
19165
19166  
19167
19168  /*
19169  * - LGPL
19170  *
19171  * TimeField
19172  * 
19173  */
19174
19175 /**
19176  * @class Roo.bootstrap.TimeField
19177  * @extends Roo.bootstrap.Input
19178  * Bootstrap DateField class
19179  * 
19180  * 
19181  * @constructor
19182  * Create a new TimeField
19183  * @param {Object} config The config object
19184  */
19185
19186 Roo.bootstrap.TimeField = function(config){
19187     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19188     this.addEvents({
19189             /**
19190              * @event show
19191              * Fires when this field show.
19192              * @param {Roo.bootstrap.DateField} thisthis
19193              * @param {Mixed} date The date value
19194              */
19195             show : true,
19196             /**
19197              * @event show
19198              * Fires when this field hide.
19199              * @param {Roo.bootstrap.DateField} this
19200              * @param {Mixed} date The date value
19201              */
19202             hide : true,
19203             /**
19204              * @event select
19205              * Fires when select a date.
19206              * @param {Roo.bootstrap.DateField} this
19207              * @param {Mixed} date The date value
19208              */
19209             select : true
19210         });
19211 };
19212
19213 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19214     
19215     /**
19216      * @cfg {String} format
19217      * The default time format string which can be overriden for localization support.  The format must be
19218      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19219      */
19220     format : "H:i",
19221        
19222     onRender: function(ct, position)
19223     {
19224         
19225         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19226                 
19227         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19228         
19229         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19230         
19231         this.pop = this.picker().select('>.datepicker-time',true).first();
19232         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19233         
19234         this.picker().on('mousedown', this.onMousedown, this);
19235         this.picker().on('click', this.onClick, this);
19236         
19237         this.picker().addClass('datepicker-dropdown');
19238     
19239         this.fillTime();
19240         this.update();
19241             
19242         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19243         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19244         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19245         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19246         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19247         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19248
19249     },
19250     
19251     fireKey: function(e){
19252         if (!this.picker().isVisible()){
19253             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19254                 this.show();
19255             }
19256             return;
19257         }
19258
19259         e.preventDefault();
19260         
19261         switch(e.keyCode){
19262             case 27: // escape
19263                 this.hide();
19264                 break;
19265             case 37: // left
19266             case 39: // right
19267                 this.onTogglePeriod();
19268                 break;
19269             case 38: // up
19270                 this.onIncrementMinutes();
19271                 break;
19272             case 40: // down
19273                 this.onDecrementMinutes();
19274                 break;
19275             case 13: // enter
19276             case 9: // tab
19277                 this.setTime();
19278                 break;
19279         }
19280     },
19281     
19282     onClick: function(e) {
19283         e.stopPropagation();
19284         e.preventDefault();
19285     },
19286     
19287     picker : function()
19288     {
19289         return this.el.select('.datepicker', true).first();
19290     },
19291     
19292     fillTime: function()
19293     {    
19294         var time = this.pop.select('tbody', true).first();
19295         
19296         time.dom.innerHTML = '';
19297         
19298         time.createChild({
19299             tag: 'tr',
19300             cn: [
19301                 {
19302                     tag: 'td',
19303                     cn: [
19304                         {
19305                             tag: 'a',
19306                             href: '#',
19307                             cls: 'btn',
19308                             cn: [
19309                                 {
19310                                     tag: 'span',
19311                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19312                                 }
19313                             ]
19314                         } 
19315                     ]
19316                 },
19317                 {
19318                     tag: 'td',
19319                     cls: 'separator'
19320                 },
19321                 {
19322                     tag: 'td',
19323                     cn: [
19324                         {
19325                             tag: 'a',
19326                             href: '#',
19327                             cls: 'btn',
19328                             cn: [
19329                                 {
19330                                     tag: 'span',
19331                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19332                                 }
19333                             ]
19334                         }
19335                     ]
19336                 },
19337                 {
19338                     tag: 'td',
19339                     cls: 'separator'
19340                 }
19341             ]
19342         });
19343         
19344         time.createChild({
19345             tag: 'tr',
19346             cn: [
19347                 {
19348                     tag: 'td',
19349                     cn: [
19350                         {
19351                             tag: 'span',
19352                             cls: 'timepicker-hour',
19353                             html: '00'
19354                         }  
19355                     ]
19356                 },
19357                 {
19358                     tag: 'td',
19359                     cls: 'separator',
19360                     html: ':'
19361                 },
19362                 {
19363                     tag: 'td',
19364                     cn: [
19365                         {
19366                             tag: 'span',
19367                             cls: 'timepicker-minute',
19368                             html: '00'
19369                         }  
19370                     ]
19371                 },
19372                 {
19373                     tag: 'td',
19374                     cls: 'separator'
19375                 },
19376                 {
19377                     tag: 'td',
19378                     cn: [
19379                         {
19380                             tag: 'button',
19381                             type: 'button',
19382                             cls: 'btn btn-primary period',
19383                             html: 'AM'
19384                             
19385                         }
19386                     ]
19387                 }
19388             ]
19389         });
19390         
19391         time.createChild({
19392             tag: 'tr',
19393             cn: [
19394                 {
19395                     tag: 'td',
19396                     cn: [
19397                         {
19398                             tag: 'a',
19399                             href: '#',
19400                             cls: 'btn',
19401                             cn: [
19402                                 {
19403                                     tag: 'span',
19404                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19405                                 }
19406                             ]
19407                         }
19408                     ]
19409                 },
19410                 {
19411                     tag: 'td',
19412                     cls: 'separator'
19413                 },
19414                 {
19415                     tag: 'td',
19416                     cn: [
19417                         {
19418                             tag: 'a',
19419                             href: '#',
19420                             cls: 'btn',
19421                             cn: [
19422                                 {
19423                                     tag: 'span',
19424                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19425                                 }
19426                             ]
19427                         }
19428                     ]
19429                 },
19430                 {
19431                     tag: 'td',
19432                     cls: 'separator'
19433                 }
19434             ]
19435         });
19436         
19437     },
19438     
19439     update: function()
19440     {
19441         
19442         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19443         
19444         this.fill();
19445     },
19446     
19447     fill: function() 
19448     {
19449         var hours = this.time.getHours();
19450         var minutes = this.time.getMinutes();
19451         var period = 'AM';
19452         
19453         if(hours > 11){
19454             period = 'PM';
19455         }
19456         
19457         if(hours == 0){
19458             hours = 12;
19459         }
19460         
19461         
19462         if(hours > 12){
19463             hours = hours - 12;
19464         }
19465         
19466         if(hours < 10){
19467             hours = '0' + hours;
19468         }
19469         
19470         if(minutes < 10){
19471             minutes = '0' + minutes;
19472         }
19473         
19474         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19475         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19476         this.pop.select('button', true).first().dom.innerHTML = period;
19477         
19478     },
19479     
19480     place: function()
19481     {   
19482         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19483         
19484         var cls = ['bottom'];
19485         
19486         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19487             cls.pop();
19488             cls.push('top');
19489         }
19490         
19491         cls.push('right');
19492         
19493         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19494             cls.pop();
19495             cls.push('left');
19496         }
19497         
19498         this.picker().addClass(cls.join('-'));
19499         
19500         var _this = this;
19501         
19502         Roo.each(cls, function(c){
19503             if(c == 'bottom'){
19504                 _this.picker().setTop(_this.inputEl().getHeight());
19505                 return;
19506             }
19507             if(c == 'top'){
19508                 _this.picker().setTop(0 - _this.picker().getHeight());
19509                 return;
19510             }
19511             
19512             if(c == 'left'){
19513                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19514                 return;
19515             }
19516             if(c == 'right'){
19517                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19518                 return;
19519             }
19520         });
19521         
19522     },
19523   
19524     onFocus : function()
19525     {
19526         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19527         this.show();
19528     },
19529     
19530     onBlur : function()
19531     {
19532         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19533         this.hide();
19534     },
19535     
19536     show : function()
19537     {
19538         this.picker().show();
19539         this.pop.show();
19540         this.update();
19541         this.place();
19542         
19543         this.fireEvent('show', this, this.date);
19544     },
19545     
19546     hide : function()
19547     {
19548         this.picker().hide();
19549         this.pop.hide();
19550         
19551         this.fireEvent('hide', this, this.date);
19552     },
19553     
19554     setTime : function()
19555     {
19556         this.hide();
19557         this.setValue(this.time.format(this.format));
19558         
19559         this.fireEvent('select', this, this.date);
19560         
19561         
19562     },
19563     
19564     onMousedown: function(e){
19565         e.stopPropagation();
19566         e.preventDefault();
19567     },
19568     
19569     onIncrementHours: function()
19570     {
19571         Roo.log('onIncrementHours');
19572         this.time = this.time.add(Date.HOUR, 1);
19573         this.update();
19574         
19575     },
19576     
19577     onDecrementHours: function()
19578     {
19579         Roo.log('onDecrementHours');
19580         this.time = this.time.add(Date.HOUR, -1);
19581         this.update();
19582     },
19583     
19584     onIncrementMinutes: function()
19585     {
19586         Roo.log('onIncrementMinutes');
19587         this.time = this.time.add(Date.MINUTE, 1);
19588         this.update();
19589     },
19590     
19591     onDecrementMinutes: function()
19592     {
19593         Roo.log('onDecrementMinutes');
19594         this.time = this.time.add(Date.MINUTE, -1);
19595         this.update();
19596     },
19597     
19598     onTogglePeriod: function()
19599     {
19600         Roo.log('onTogglePeriod');
19601         this.time = this.time.add(Date.HOUR, 12);
19602         this.update();
19603     }
19604     
19605    
19606 });
19607
19608 Roo.apply(Roo.bootstrap.TimeField,  {
19609     
19610     content : {
19611         tag: 'tbody',
19612         cn: [
19613             {
19614                 tag: 'tr',
19615                 cn: [
19616                 {
19617                     tag: 'td',
19618                     colspan: '7'
19619                 }
19620                 ]
19621             }
19622         ]
19623     },
19624     
19625     footer : {
19626         tag: 'tfoot',
19627         cn: [
19628             {
19629                 tag: 'tr',
19630                 cn: [
19631                 {
19632                     tag: 'th',
19633                     colspan: '7',
19634                     cls: '',
19635                     cn: [
19636                         {
19637                             tag: 'button',
19638                             cls: 'btn btn-info ok',
19639                             html: 'OK'
19640                         }
19641                     ]
19642                 }
19643
19644                 ]
19645             }
19646         ]
19647     }
19648 });
19649
19650 Roo.apply(Roo.bootstrap.TimeField,  {
19651   
19652     template : {
19653         tag: 'div',
19654         cls: 'datepicker dropdown-menu',
19655         cn: [
19656             {
19657                 tag: 'div',
19658                 cls: 'datepicker-time',
19659                 cn: [
19660                 {
19661                     tag: 'table',
19662                     cls: 'table-condensed',
19663                     cn:[
19664                     Roo.bootstrap.TimeField.content,
19665                     Roo.bootstrap.TimeField.footer
19666                     ]
19667                 }
19668                 ]
19669             }
19670         ]
19671     }
19672 });
19673
19674  
19675
19676  /*
19677  * - LGPL
19678  *
19679  * MonthField
19680  * 
19681  */
19682
19683 /**
19684  * @class Roo.bootstrap.MonthField
19685  * @extends Roo.bootstrap.Input
19686  * Bootstrap MonthField class
19687  * 
19688  * @cfg {String} language default en
19689  * 
19690  * @constructor
19691  * Create a new MonthField
19692  * @param {Object} config The config object
19693  */
19694
19695 Roo.bootstrap.MonthField = function(config){
19696     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19697     
19698     this.addEvents({
19699         /**
19700          * @event show
19701          * Fires when this field show.
19702          * @param {Roo.bootstrap.MonthField} this
19703          * @param {Mixed} date The date value
19704          */
19705         show : true,
19706         /**
19707          * @event show
19708          * Fires when this field hide.
19709          * @param {Roo.bootstrap.MonthField} this
19710          * @param {Mixed} date The date value
19711          */
19712         hide : true,
19713         /**
19714          * @event select
19715          * Fires when select a date.
19716          * @param {Roo.bootstrap.MonthField} this
19717          * @param {String} oldvalue The old value
19718          * @param {String} newvalue The new value
19719          */
19720         select : true
19721     });
19722 };
19723
19724 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19725     
19726     onRender: function(ct, position)
19727     {
19728         
19729         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19730         
19731         this.language = this.language || 'en';
19732         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19733         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19734         
19735         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19736         this.isInline = false;
19737         this.isInput = true;
19738         this.component = this.el.select('.add-on', true).first() || false;
19739         this.component = (this.component && this.component.length === 0) ? false : this.component;
19740         this.hasInput = this.component && this.inputEL().length;
19741         
19742         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19743         
19744         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19745         
19746         this.picker().on('mousedown', this.onMousedown, this);
19747         this.picker().on('click', this.onClick, this);
19748         
19749         this.picker().addClass('datepicker-dropdown');
19750         
19751         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19752             v.setStyle('width', '189px');
19753         });
19754         
19755         this.fillMonths();
19756         
19757         this.update();
19758         
19759         if(this.isInline) {
19760             this.show();
19761         }
19762         
19763     },
19764     
19765     setValue: function(v, suppressEvent)
19766     {   
19767         var o = this.getValue();
19768         
19769         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19770         
19771         this.update();
19772
19773         if(suppressEvent !== true){
19774             this.fireEvent('select', this, o, v);
19775         }
19776         
19777     },
19778     
19779     getValue: function()
19780     {
19781         return this.value;
19782     },
19783     
19784     onClick: function(e) 
19785     {
19786         e.stopPropagation();
19787         e.preventDefault();
19788         
19789         var target = e.getTarget();
19790         
19791         if(target.nodeName.toLowerCase() === 'i'){
19792             target = Roo.get(target).dom.parentNode;
19793         }
19794         
19795         var nodeName = target.nodeName;
19796         var className = target.className;
19797         var html = target.innerHTML;
19798         
19799         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19800             return;
19801         }
19802         
19803         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19804         
19805         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19806         
19807         this.hide();
19808                         
19809     },
19810     
19811     picker : function()
19812     {
19813         return this.pickerEl;
19814     },
19815     
19816     fillMonths: function()
19817     {    
19818         var i = 0;
19819         var months = this.picker().select('>.datepicker-months td', true).first();
19820         
19821         months.dom.innerHTML = '';
19822         
19823         while (i < 12) {
19824             var month = {
19825                 tag: 'span',
19826                 cls: 'month',
19827                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19828             };
19829             
19830             months.createChild(month);
19831         }
19832         
19833     },
19834     
19835     update: function()
19836     {
19837         var _this = this;
19838         
19839         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19840             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19841         }
19842         
19843         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19844             e.removeClass('active');
19845             
19846             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19847                 e.addClass('active');
19848             }
19849         })
19850     },
19851     
19852     place: function()
19853     {
19854         if(this.isInline) {
19855             return;
19856         }
19857         
19858         this.picker().removeClass(['bottom', 'top']);
19859         
19860         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19861             /*
19862              * place to the top of element!
19863              *
19864              */
19865             
19866             this.picker().addClass('top');
19867             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19868             
19869             return;
19870         }
19871         
19872         this.picker().addClass('bottom');
19873         
19874         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19875     },
19876     
19877     onFocus : function()
19878     {
19879         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19880         this.show();
19881     },
19882     
19883     onBlur : function()
19884     {
19885         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19886         
19887         var d = this.inputEl().getValue();
19888         
19889         this.setValue(d);
19890                 
19891         this.hide();
19892     },
19893     
19894     show : function()
19895     {
19896         this.picker().show();
19897         this.picker().select('>.datepicker-months', true).first().show();
19898         this.update();
19899         this.place();
19900         
19901         this.fireEvent('show', this, this.date);
19902     },
19903     
19904     hide : function()
19905     {
19906         if(this.isInline) {
19907             return;
19908         }
19909         this.picker().hide();
19910         this.fireEvent('hide', this, this.date);
19911         
19912     },
19913     
19914     onMousedown: function(e)
19915     {
19916         e.stopPropagation();
19917         e.preventDefault();
19918     },
19919     
19920     keyup: function(e)
19921     {
19922         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19923         this.update();
19924     },
19925
19926     fireKey: function(e)
19927     {
19928         if (!this.picker().isVisible()){
19929             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19930                 this.show();
19931             }
19932             return;
19933         }
19934         
19935         var dir;
19936         
19937         switch(e.keyCode){
19938             case 27: // escape
19939                 this.hide();
19940                 e.preventDefault();
19941                 break;
19942             case 37: // left
19943             case 39: // right
19944                 dir = e.keyCode == 37 ? -1 : 1;
19945                 
19946                 this.vIndex = this.vIndex + dir;
19947                 
19948                 if(this.vIndex < 0){
19949                     this.vIndex = 0;
19950                 }
19951                 
19952                 if(this.vIndex > 11){
19953                     this.vIndex = 11;
19954                 }
19955                 
19956                 if(isNaN(this.vIndex)){
19957                     this.vIndex = 0;
19958                 }
19959                 
19960                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19961                 
19962                 break;
19963             case 38: // up
19964             case 40: // down
19965                 
19966                 dir = e.keyCode == 38 ? -1 : 1;
19967                 
19968                 this.vIndex = this.vIndex + dir * 4;
19969                 
19970                 if(this.vIndex < 0){
19971                     this.vIndex = 0;
19972                 }
19973                 
19974                 if(this.vIndex > 11){
19975                     this.vIndex = 11;
19976                 }
19977                 
19978                 if(isNaN(this.vIndex)){
19979                     this.vIndex = 0;
19980                 }
19981                 
19982                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19983                 break;
19984                 
19985             case 13: // enter
19986                 
19987                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19988                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19989                 }
19990                 
19991                 this.hide();
19992                 e.preventDefault();
19993                 break;
19994             case 9: // tab
19995                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19996                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19997                 }
19998                 this.hide();
19999                 break;
20000             case 16: // shift
20001             case 17: // ctrl
20002             case 18: // alt
20003                 break;
20004             default :
20005                 this.hide();
20006                 
20007         }
20008     },
20009     
20010     remove: function() 
20011     {
20012         this.picker().remove();
20013     }
20014    
20015 });
20016
20017 Roo.apply(Roo.bootstrap.MonthField,  {
20018     
20019     content : {
20020         tag: 'tbody',
20021         cn: [
20022         {
20023             tag: 'tr',
20024             cn: [
20025             {
20026                 tag: 'td',
20027                 colspan: '7'
20028             }
20029             ]
20030         }
20031         ]
20032     },
20033     
20034     dates:{
20035         en: {
20036             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20037             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20038         }
20039     }
20040 });
20041
20042 Roo.apply(Roo.bootstrap.MonthField,  {
20043   
20044     template : {
20045         tag: 'div',
20046         cls: 'datepicker dropdown-menu roo-dynamic',
20047         cn: [
20048             {
20049                 tag: 'div',
20050                 cls: 'datepicker-months',
20051                 cn: [
20052                 {
20053                     tag: 'table',
20054                     cls: 'table-condensed',
20055                     cn:[
20056                         Roo.bootstrap.DateField.content
20057                     ]
20058                 }
20059                 ]
20060             }
20061         ]
20062     }
20063 });
20064
20065  
20066
20067  
20068  /*
20069  * - LGPL
20070  *
20071  * CheckBox
20072  * 
20073  */
20074
20075 /**
20076  * @class Roo.bootstrap.CheckBox
20077  * @extends Roo.bootstrap.Input
20078  * Bootstrap CheckBox class
20079  * 
20080  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20081  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20082  * @cfg {String} boxLabel The text that appears beside the checkbox
20083  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20084  * @cfg {Boolean} checked initnal the element
20085  * @cfg {Boolean} inline inline the element (default false)
20086  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20087  * 
20088  * @constructor
20089  * Create a new CheckBox
20090  * @param {Object} config The config object
20091  */
20092
20093 Roo.bootstrap.CheckBox = function(config){
20094     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20095    
20096     this.addEvents({
20097         /**
20098         * @event check
20099         * Fires when the element is checked or unchecked.
20100         * @param {Roo.bootstrap.CheckBox} this This input
20101         * @param {Boolean} checked The new checked value
20102         */
20103        check : true
20104     });
20105     
20106 };
20107
20108 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20109   
20110     inputType: 'checkbox',
20111     inputValue: 1,
20112     valueOff: 0,
20113     boxLabel: false,
20114     checked: false,
20115     weight : false,
20116     inline: false,
20117     
20118     getAutoCreate : function()
20119     {
20120         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20121         
20122         var id = Roo.id();
20123         
20124         var cfg = {};
20125         
20126         cfg.cls = 'form-group ' + this.inputType; //input-group
20127         
20128         if(this.inline){
20129             cfg.cls += ' ' + this.inputType + '-inline';
20130         }
20131         
20132         var input =  {
20133             tag: 'input',
20134             id : id,
20135             type : this.inputType,
20136             value : this.inputValue,
20137             cls : 'roo-' + this.inputType, //'form-box',
20138             placeholder : this.placeholder || ''
20139             
20140         };
20141         
20142         if(this.inputType != 'radio'){
20143             var hidden =  {
20144                 tag: 'input',
20145                 type : 'hidden',
20146                 cls : 'roo-hidden-value',
20147                 value : this.checked ? this.valueOff : this.inputValue
20148             };
20149         }
20150         
20151             
20152         if (this.weight) { // Validity check?
20153             cfg.cls += " " + this.inputType + "-" + this.weight;
20154         }
20155         
20156         if (this.disabled) {
20157             input.disabled=true;
20158         }
20159         
20160         if(this.checked){
20161             input.checked = this.checked;
20162             
20163         }
20164         
20165         
20166         if (this.name) {
20167             
20168             input.name = this.name;
20169             
20170             if(this.inputType != 'radio'){
20171                 hidden.name = this.name;
20172                 input.name = '_hidden_' + this.name;
20173             }
20174         }
20175         
20176         if (this.size) {
20177             input.cls += ' input-' + this.size;
20178         }
20179         
20180         var settings=this;
20181         
20182         ['xs','sm','md','lg'].map(function(size){
20183             if (settings[size]) {
20184                 cfg.cls += ' col-' + size + '-' + settings[size];
20185             }
20186         });
20187         
20188         var inputblock = input;
20189          
20190         if (this.before || this.after) {
20191             
20192             inputblock = {
20193                 cls : 'input-group',
20194                 cn :  [] 
20195             };
20196             
20197             if (this.before) {
20198                 inputblock.cn.push({
20199                     tag :'span',
20200                     cls : 'input-group-addon',
20201                     html : this.before
20202                 });
20203             }
20204             
20205             inputblock.cn.push(input);
20206             
20207             if(this.inputType != 'radio'){
20208                 inputblock.cn.push(hidden);
20209             }
20210             
20211             if (this.after) {
20212                 inputblock.cn.push({
20213                     tag :'span',
20214                     cls : 'input-group-addon',
20215                     html : this.after
20216                 });
20217             }
20218             
20219         }
20220         
20221         if (align ==='left' && this.fieldLabel.length) {
20222 //                Roo.log("left and has label");
20223             cfg.cn = [
20224                 {
20225                     tag: 'label',
20226                     'for' :  id,
20227                     cls : 'control-label',
20228                     html : this.fieldLabel
20229
20230                 },
20231                 {
20232                     cls : "", 
20233                     cn: [
20234                         inputblock
20235                     ]
20236                 }
20237             ];
20238             
20239             if(this.labelWidth > 12){
20240                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20241             }
20242             
20243             if(this.labelWidth < 13 && this.labelmd == 0){
20244                 this.labelmd = this.labelWidth;
20245             }
20246             
20247             if(this.labellg > 0){
20248                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20249                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20250             }
20251             
20252             if(this.labelmd > 0){
20253                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20254                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20255             }
20256             
20257             if(this.labelsm > 0){
20258                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20259                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20260             }
20261             
20262             if(this.labelxs > 0){
20263                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20264                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20265             }
20266             
20267         } else if ( this.fieldLabel.length) {
20268 //                Roo.log(" label");
20269                 cfg.cn = [
20270                    
20271                     {
20272                         tag: this.boxLabel ? 'span' : 'label',
20273                         'for': id,
20274                         cls: 'control-label box-input-label',
20275                         //cls : 'input-group-addon',
20276                         html : this.fieldLabel
20277                         
20278                     },
20279                     
20280                     inputblock
20281                     
20282                 ];
20283
20284         } else {
20285             
20286 //                Roo.log(" no label && no align");
20287                 cfg.cn = [  inputblock ] ;
20288                 
20289                 
20290         }
20291         
20292         if(this.boxLabel){
20293              var boxLabelCfg = {
20294                 tag: 'label',
20295                 //'for': id, // box label is handled by onclick - so no for...
20296                 cls: 'box-label',
20297                 html: this.boxLabel
20298             };
20299             
20300             if(this.tooltip){
20301                 boxLabelCfg.tooltip = this.tooltip;
20302             }
20303              
20304             cfg.cn.push(boxLabelCfg);
20305         }
20306         
20307         if(this.inputType != 'radio'){
20308             cfg.cn.push(hidden);
20309         }
20310         
20311         return cfg;
20312         
20313     },
20314     
20315     /**
20316      * return the real input element.
20317      */
20318     inputEl: function ()
20319     {
20320         return this.el.select('input.roo-' + this.inputType,true).first();
20321     },
20322     hiddenEl: function ()
20323     {
20324         return this.el.select('input.roo-hidden-value',true).first();
20325     },
20326     
20327     labelEl: function()
20328     {
20329         return this.el.select('label.control-label',true).first();
20330     },
20331     /* depricated... */
20332     
20333     label: function()
20334     {
20335         return this.labelEl();
20336     },
20337     
20338     boxLabelEl: function()
20339     {
20340         return this.el.select('label.box-label',true).first();
20341     },
20342     
20343     initEvents : function()
20344     {
20345 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20346         
20347         this.inputEl().on('click', this.onClick,  this);
20348         
20349         if (this.boxLabel) { 
20350             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20351         }
20352         
20353         this.startValue = this.getValue();
20354         
20355         if(this.groupId){
20356             Roo.bootstrap.CheckBox.register(this);
20357         }
20358     },
20359     
20360     onClick : function()
20361     {   
20362         this.setChecked(!this.checked);
20363     },
20364     
20365     setChecked : function(state,suppressEvent)
20366     {
20367         this.startValue = this.getValue();
20368
20369         if(this.inputType == 'radio'){
20370             
20371             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20372                 e.dom.checked = false;
20373             });
20374             
20375             this.inputEl().dom.checked = true;
20376             
20377             this.inputEl().dom.value = this.inputValue;
20378             
20379             if(suppressEvent !== true){
20380                 this.fireEvent('check', this, true);
20381             }
20382             
20383             this.validate();
20384             
20385             return;
20386         }
20387         
20388         this.checked = state;
20389         
20390         this.inputEl().dom.checked = state;
20391         
20392         
20393         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20394         
20395         if(suppressEvent !== true){
20396             this.fireEvent('check', this, state);
20397         }
20398         
20399         this.validate();
20400     },
20401     
20402     getValue : function()
20403     {
20404         if(this.inputType == 'radio'){
20405             return this.getGroupValue();
20406         }
20407         
20408         return this.hiddenEl().dom.value;
20409         
20410     },
20411     
20412     getGroupValue : function()
20413     {
20414         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20415             return '';
20416         }
20417         
20418         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20419     },
20420     
20421     setValue : function(v,suppressEvent)
20422     {
20423         if(this.inputType == 'radio'){
20424             this.setGroupValue(v, suppressEvent);
20425             return;
20426         }
20427         
20428         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20429         
20430         this.validate();
20431     },
20432     
20433     setGroupValue : function(v, suppressEvent)
20434     {
20435         this.startValue = this.getValue();
20436         
20437         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438             e.dom.checked = false;
20439             
20440             if(e.dom.value == v){
20441                 e.dom.checked = true;
20442             }
20443         });
20444         
20445         if(suppressEvent !== true){
20446             this.fireEvent('check', this, true);
20447         }
20448
20449         this.validate();
20450         
20451         return;
20452     },
20453     
20454     validate : function()
20455     {
20456         if(
20457                 this.disabled || 
20458                 (this.inputType == 'radio' && this.validateRadio()) ||
20459                 (this.inputType == 'checkbox' && this.validateCheckbox())
20460         ){
20461             this.markValid();
20462             return true;
20463         }
20464         
20465         this.markInvalid();
20466         return false;
20467     },
20468     
20469     validateRadio : function()
20470     {
20471         if(this.allowBlank){
20472             return true;
20473         }
20474         
20475         var valid = false;
20476         
20477         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20478             if(!e.dom.checked){
20479                 return;
20480             }
20481             
20482             valid = true;
20483             
20484             return false;
20485         });
20486         
20487         return valid;
20488     },
20489     
20490     validateCheckbox : function()
20491     {
20492         if(!this.groupId){
20493             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20494             //return (this.getValue() == this.inputValue) ? true : false;
20495         }
20496         
20497         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20498         
20499         if(!group){
20500             return false;
20501         }
20502         
20503         var r = false;
20504         
20505         for(var i in group){
20506             if(r){
20507                 break;
20508             }
20509             
20510             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20511         }
20512         
20513         return r;
20514     },
20515     
20516     /**
20517      * Mark this field as valid
20518      */
20519     markValid : function()
20520     {
20521         var _this = this;
20522         
20523         this.fireEvent('valid', this);
20524         
20525         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20526         
20527         if(this.groupId){
20528             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20529         }
20530         
20531         if(label){
20532             label.markValid();
20533         }
20534
20535         if(this.inputType == 'radio'){
20536             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20537                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20538                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20539             });
20540             
20541             return;
20542         }
20543
20544         if(!this.groupId){
20545             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20546             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20547             return;
20548         }
20549         
20550         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20551         
20552         if(!group){
20553             return;
20554         }
20555         
20556         for(var i in group){
20557             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20558             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20559         }
20560     },
20561     
20562      /**
20563      * Mark this field as invalid
20564      * @param {String} msg The validation message
20565      */
20566     markInvalid : function(msg)
20567     {
20568         if(this.allowBlank){
20569             return;
20570         }
20571         
20572         var _this = this;
20573         
20574         this.fireEvent('invalid', this, msg);
20575         
20576         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20577         
20578         if(this.groupId){
20579             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20580         }
20581         
20582         if(label){
20583             label.markInvalid();
20584         }
20585             
20586         if(this.inputType == 'radio'){
20587             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20588                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20589                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20590             });
20591             
20592             return;
20593         }
20594         
20595         if(!this.groupId){
20596             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20597             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20598             return;
20599         }
20600         
20601         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20602         
20603         if(!group){
20604             return;
20605         }
20606         
20607         for(var i in group){
20608             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20609             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20610         }
20611         
20612     },
20613     
20614     clearInvalid : function()
20615     {
20616         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20617         
20618         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20619         
20620         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20621         
20622         if (label) {
20623             label.iconEl.removeClass(label.validClass);
20624             label.iconEl.removeClass(label.invalidClass);
20625         }
20626     },
20627     
20628     disable : function()
20629     {
20630         if(this.inputType != 'radio'){
20631             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20632             return;
20633         }
20634         
20635         var _this = this;
20636         
20637         if(this.rendered){
20638             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20639                 _this.getActionEl().addClass(this.disabledClass);
20640                 e.dom.disabled = true;
20641             });
20642         }
20643         
20644         this.disabled = true;
20645         this.fireEvent("disable", this);
20646         return this;
20647     },
20648
20649     enable : function()
20650     {
20651         if(this.inputType != 'radio'){
20652             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20653             return;
20654         }
20655         
20656         var _this = this;
20657         
20658         if(this.rendered){
20659             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20660                 _this.getActionEl().removeClass(this.disabledClass);
20661                 e.dom.disabled = false;
20662             });
20663         }
20664         
20665         this.disabled = false;
20666         this.fireEvent("enable", this);
20667         return this;
20668     }
20669
20670 });
20671
20672 Roo.apply(Roo.bootstrap.CheckBox, {
20673     
20674     groups: {},
20675     
20676      /**
20677     * register a CheckBox Group
20678     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20679     */
20680     register : function(checkbox)
20681     {
20682         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20683             this.groups[checkbox.groupId] = {};
20684         }
20685         
20686         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20687             return;
20688         }
20689         
20690         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20691         
20692     },
20693     /**
20694     * fetch a CheckBox Group based on the group ID
20695     * @param {string} the group ID
20696     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20697     */
20698     get: function(groupId) {
20699         if (typeof(this.groups[groupId]) == 'undefined') {
20700             return false;
20701         }
20702         
20703         return this.groups[groupId] ;
20704     }
20705     
20706     
20707 });
20708 /*
20709  * - LGPL
20710  *
20711  * RadioItem
20712  * 
20713  */
20714
20715 /**
20716  * @class Roo.bootstrap.Radio
20717  * @extends Roo.bootstrap.Component
20718  * Bootstrap Radio class
20719  * @cfg {String} boxLabel - the label associated
20720  * @cfg {String} value - the value of radio
20721  * 
20722  * @constructor
20723  * Create a new Radio
20724  * @param {Object} config The config object
20725  */
20726 Roo.bootstrap.Radio = function(config){
20727     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20728     
20729 };
20730
20731 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20732     
20733     boxLabel : '',
20734     
20735     value : '',
20736     
20737     getAutoCreate : function()
20738     {
20739         var cfg = {
20740             tag : 'div',
20741             cls : 'form-group radio',
20742             cn : [
20743                 {
20744                     tag : 'label',
20745                     cls : 'box-label',
20746                     html : this.boxLabel
20747                 }
20748             ]
20749         };
20750         
20751         return cfg;
20752     },
20753     
20754     initEvents : function() 
20755     {
20756         this.parent().register(this);
20757         
20758         this.el.on('click', this.onClick, this);
20759         
20760     },
20761     
20762     onClick : function()
20763     {
20764         this.setChecked(true);
20765     },
20766     
20767     setChecked : function(state, suppressEvent)
20768     {
20769         this.parent().setValue(this.value, suppressEvent);
20770         
20771     }
20772     
20773 });
20774  
20775
20776  /*
20777  * - LGPL
20778  *
20779  * Input
20780  * 
20781  */
20782
20783 /**
20784  * @class Roo.bootstrap.SecurePass
20785  * @extends Roo.bootstrap.Input
20786  * Bootstrap SecurePass class
20787  *
20788  * 
20789  * @constructor
20790  * Create a new SecurePass
20791  * @param {Object} config The config object
20792  */
20793  
20794 Roo.bootstrap.SecurePass = function (config) {
20795     // these go here, so the translation tool can replace them..
20796     this.errors = {
20797         PwdEmpty: "Please type a password, and then retype it to confirm.",
20798         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20799         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20800         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20801         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20802         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20803         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20804         TooWeak: "Your password is Too Weak."
20805     },
20806     this.meterLabel = "Password strength:";
20807     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20808     this.meterClass = [
20809         "roo-password-meter-tooweak", 
20810         "roo-password-meter-weak", 
20811         "roo-password-meter-medium", 
20812         "roo-password-meter-strong", 
20813         "roo-password-meter-grey"
20814     ];
20815     
20816     this.errors = {};
20817     
20818     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20819 }
20820
20821 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20822     /**
20823      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20824      * {
20825      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20826      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20827      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20828      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20829      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20830      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20831      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20832      * })
20833      */
20834     // private
20835     
20836     meterWidth: 300,
20837     errorMsg :'',    
20838     errors: false,
20839     imageRoot: '/',
20840     /**
20841      * @cfg {String/Object} Label for the strength meter (defaults to
20842      * 'Password strength:')
20843      */
20844     // private
20845     meterLabel: '',
20846     /**
20847      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20848      * ['Weak', 'Medium', 'Strong'])
20849      */
20850     // private    
20851     pwdStrengths: false,    
20852     // private
20853     strength: 0,
20854     // private
20855     _lastPwd: null,
20856     // private
20857     kCapitalLetter: 0,
20858     kSmallLetter: 1,
20859     kDigit: 2,
20860     kPunctuation: 3,
20861     
20862     insecure: false,
20863     // private
20864     initEvents: function ()
20865     {
20866         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20867
20868         if (this.el.is('input[type=password]') && Roo.isSafari) {
20869             this.el.on('keydown', this.SafariOnKeyDown, this);
20870         }
20871
20872         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20873     },
20874     // private
20875     onRender: function (ct, position)
20876     {
20877         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20878         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20879         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20880
20881         this.trigger.createChild({
20882                    cn: [
20883                     {
20884                     //id: 'PwdMeter',
20885                     tag: 'div',
20886                     cls: 'roo-password-meter-grey col-xs-12',
20887                     style: {
20888                         //width: 0,
20889                         //width: this.meterWidth + 'px'                                                
20890                         }
20891                     },
20892                     {                            
20893                          cls: 'roo-password-meter-text'                          
20894                     }
20895                 ]            
20896         });
20897
20898          
20899         if (this.hideTrigger) {
20900             this.trigger.setDisplayed(false);
20901         }
20902         this.setSize(this.width || '', this.height || '');
20903     },
20904     // private
20905     onDestroy: function ()
20906     {
20907         if (this.trigger) {
20908             this.trigger.removeAllListeners();
20909             this.trigger.remove();
20910         }
20911         if (this.wrap) {
20912             this.wrap.remove();
20913         }
20914         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20915     },
20916     // private
20917     checkStrength: function ()
20918     {
20919         var pwd = this.inputEl().getValue();
20920         if (pwd == this._lastPwd) {
20921             return;
20922         }
20923
20924         var strength;
20925         if (this.ClientSideStrongPassword(pwd)) {
20926             strength = 3;
20927         } else if (this.ClientSideMediumPassword(pwd)) {
20928             strength = 2;
20929         } else if (this.ClientSideWeakPassword(pwd)) {
20930             strength = 1;
20931         } else {
20932             strength = 0;
20933         }
20934         
20935         Roo.log('strength1: ' + strength);
20936         
20937         //var pm = this.trigger.child('div/div/div').dom;
20938         var pm = this.trigger.child('div/div');
20939         pm.removeClass(this.meterClass);
20940         pm.addClass(this.meterClass[strength]);
20941                 
20942         
20943         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20944                 
20945         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20946         
20947         this._lastPwd = pwd;
20948     },
20949     reset: function ()
20950     {
20951         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20952         
20953         this._lastPwd = '';
20954         
20955         var pm = this.trigger.child('div/div');
20956         pm.removeClass(this.meterClass);
20957         pm.addClass('roo-password-meter-grey');        
20958         
20959         
20960         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20961         
20962         pt.innerHTML = '';
20963         this.inputEl().dom.type='password';
20964     },
20965     // private
20966     validateValue: function (value)
20967     {
20968         
20969         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20970             return false;
20971         }
20972         if (value.length == 0) {
20973             if (this.allowBlank) {
20974                 this.clearInvalid();
20975                 return true;
20976             }
20977
20978             this.markInvalid(this.errors.PwdEmpty);
20979             this.errorMsg = this.errors.PwdEmpty;
20980             return false;
20981         }
20982         
20983         if(this.insecure){
20984             return true;
20985         }
20986         
20987         if ('[\x21-\x7e]*'.match(value)) {
20988             this.markInvalid(this.errors.PwdBadChar);
20989             this.errorMsg = this.errors.PwdBadChar;
20990             return false;
20991         }
20992         if (value.length < 6) {
20993             this.markInvalid(this.errors.PwdShort);
20994             this.errorMsg = this.errors.PwdShort;
20995             return false;
20996         }
20997         if (value.length > 16) {
20998             this.markInvalid(this.errors.PwdLong);
20999             this.errorMsg = this.errors.PwdLong;
21000             return false;
21001         }
21002         var strength;
21003         if (this.ClientSideStrongPassword(value)) {
21004             strength = 3;
21005         } else if (this.ClientSideMediumPassword(value)) {
21006             strength = 2;
21007         } else if (this.ClientSideWeakPassword(value)) {
21008             strength = 1;
21009         } else {
21010             strength = 0;
21011         }
21012
21013         
21014         if (strength < 2) {
21015             //this.markInvalid(this.errors.TooWeak);
21016             this.errorMsg = this.errors.TooWeak;
21017             //return false;
21018         }
21019         
21020         
21021         console.log('strength2: ' + strength);
21022         
21023         //var pm = this.trigger.child('div/div/div').dom;
21024         
21025         var pm = this.trigger.child('div/div');
21026         pm.removeClass(this.meterClass);
21027         pm.addClass(this.meterClass[strength]);
21028                 
21029         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21030                 
21031         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21032         
21033         this.errorMsg = ''; 
21034         return true;
21035     },
21036     // private
21037     CharacterSetChecks: function (type)
21038     {
21039         this.type = type;
21040         this.fResult = false;
21041     },
21042     // private
21043     isctype: function (character, type)
21044     {
21045         switch (type) {  
21046             case this.kCapitalLetter:
21047                 if (character >= 'A' && character <= 'Z') {
21048                     return true;
21049                 }
21050                 break;
21051             
21052             case this.kSmallLetter:
21053                 if (character >= 'a' && character <= 'z') {
21054                     return true;
21055                 }
21056                 break;
21057             
21058             case this.kDigit:
21059                 if (character >= '0' && character <= '9') {
21060                     return true;
21061                 }
21062                 break;
21063             
21064             case this.kPunctuation:
21065                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21066                     return true;
21067                 }
21068                 break;
21069             
21070             default:
21071                 return false;
21072         }
21073
21074     },
21075     // private
21076     IsLongEnough: function (pwd, size)
21077     {
21078         return !(pwd == null || isNaN(size) || pwd.length < size);
21079     },
21080     // private
21081     SpansEnoughCharacterSets: function (word, nb)
21082     {
21083         if (!this.IsLongEnough(word, nb))
21084         {
21085             return false;
21086         }
21087
21088         var characterSetChecks = new Array(
21089             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21090             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21091         );
21092         
21093         for (var index = 0; index < word.length; ++index) {
21094             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21095                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21096                     characterSetChecks[nCharSet].fResult = true;
21097                     break;
21098                 }
21099             }
21100         }
21101
21102         var nCharSets = 0;
21103         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21104             if (characterSetChecks[nCharSet].fResult) {
21105                 ++nCharSets;
21106             }
21107         }
21108
21109         if (nCharSets < nb) {
21110             return false;
21111         }
21112         return true;
21113     },
21114     // private
21115     ClientSideStrongPassword: function (pwd)
21116     {
21117         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21118     },
21119     // private
21120     ClientSideMediumPassword: function (pwd)
21121     {
21122         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21123     },
21124     // private
21125     ClientSideWeakPassword: function (pwd)
21126     {
21127         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21128     }
21129           
21130 })//<script type="text/javascript">
21131
21132 /*
21133  * Based  Ext JS Library 1.1.1
21134  * Copyright(c) 2006-2007, Ext JS, LLC.
21135  * LGPL
21136  *
21137  */
21138  
21139 /**
21140  * @class Roo.HtmlEditorCore
21141  * @extends Roo.Component
21142  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21143  *
21144  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21145  */
21146
21147 Roo.HtmlEditorCore = function(config){
21148     
21149     
21150     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21151     
21152     
21153     this.addEvents({
21154         /**
21155          * @event initialize
21156          * Fires when the editor is fully initialized (including the iframe)
21157          * @param {Roo.HtmlEditorCore} this
21158          */
21159         initialize: true,
21160         /**
21161          * @event activate
21162          * Fires when the editor is first receives the focus. Any insertion must wait
21163          * until after this event.
21164          * @param {Roo.HtmlEditorCore} this
21165          */
21166         activate: true,
21167          /**
21168          * @event beforesync
21169          * Fires before the textarea is updated with content from the editor iframe. Return false
21170          * to cancel the sync.
21171          * @param {Roo.HtmlEditorCore} this
21172          * @param {String} html
21173          */
21174         beforesync: true,
21175          /**
21176          * @event beforepush
21177          * Fires before the iframe editor is updated with content from the textarea. Return false
21178          * to cancel the push.
21179          * @param {Roo.HtmlEditorCore} this
21180          * @param {String} html
21181          */
21182         beforepush: true,
21183          /**
21184          * @event sync
21185          * Fires when the textarea is updated with content from the editor iframe.
21186          * @param {Roo.HtmlEditorCore} this
21187          * @param {String} html
21188          */
21189         sync: true,
21190          /**
21191          * @event push
21192          * Fires when the iframe editor is updated with content from the textarea.
21193          * @param {Roo.HtmlEditorCore} this
21194          * @param {String} html
21195          */
21196         push: true,
21197         
21198         /**
21199          * @event editorevent
21200          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21201          * @param {Roo.HtmlEditorCore} this
21202          */
21203         editorevent: true
21204         
21205     });
21206     
21207     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21208     
21209     // defaults : white / black...
21210     this.applyBlacklists();
21211     
21212     
21213     
21214 };
21215
21216
21217 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21218
21219
21220      /**
21221      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21222      */
21223     
21224     owner : false,
21225     
21226      /**
21227      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21228      *                        Roo.resizable.
21229      */
21230     resizable : false,
21231      /**
21232      * @cfg {Number} height (in pixels)
21233      */   
21234     height: 300,
21235    /**
21236      * @cfg {Number} width (in pixels)
21237      */   
21238     width: 500,
21239     
21240     /**
21241      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21242      * 
21243      */
21244     stylesheets: false,
21245     
21246     // id of frame..
21247     frameId: false,
21248     
21249     // private properties
21250     validationEvent : false,
21251     deferHeight: true,
21252     initialized : false,
21253     activated : false,
21254     sourceEditMode : false,
21255     onFocus : Roo.emptyFn,
21256     iframePad:3,
21257     hideMode:'offsets',
21258     
21259     clearUp: true,
21260     
21261     // blacklist + whitelisted elements..
21262     black: false,
21263     white: false,
21264      
21265     
21266
21267     /**
21268      * Protected method that will not generally be called directly. It
21269      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21270      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21271      */
21272     getDocMarkup : function(){
21273         // body styles..
21274         var st = '';
21275         
21276         // inherit styels from page...?? 
21277         if (this.stylesheets === false) {
21278             
21279             Roo.get(document.head).select('style').each(function(node) {
21280                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21281             });
21282             
21283             Roo.get(document.head).select('link').each(function(node) { 
21284                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21285             });
21286             
21287         } else if (!this.stylesheets.length) {
21288                 // simple..
21289                 st = '<style type="text/css">' +
21290                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21291                    '</style>';
21292         } else { 
21293             
21294         }
21295         
21296         st +=  '<style type="text/css">' +
21297             'IMG { cursor: pointer } ' +
21298         '</style>';
21299
21300         
21301         return '<html><head>' + st  +
21302             //<style type="text/css">' +
21303             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21304             //'</style>' +
21305             ' </head><body class="roo-htmleditor-body"></body></html>';
21306     },
21307
21308     // private
21309     onRender : function(ct, position)
21310     {
21311         var _t = this;
21312         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21313         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21314         
21315         
21316         this.el.dom.style.border = '0 none';
21317         this.el.dom.setAttribute('tabIndex', -1);
21318         this.el.addClass('x-hidden hide');
21319         
21320         
21321         
21322         if(Roo.isIE){ // fix IE 1px bogus margin
21323             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21324         }
21325        
21326         
21327         this.frameId = Roo.id();
21328         
21329          
21330         
21331         var iframe = this.owner.wrap.createChild({
21332             tag: 'iframe',
21333             cls: 'form-control', // bootstrap..
21334             id: this.frameId,
21335             name: this.frameId,
21336             frameBorder : 'no',
21337             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21338         }, this.el
21339         );
21340         
21341         
21342         this.iframe = iframe.dom;
21343
21344          this.assignDocWin();
21345         
21346         this.doc.designMode = 'on';
21347        
21348         this.doc.open();
21349         this.doc.write(this.getDocMarkup());
21350         this.doc.close();
21351
21352         
21353         var task = { // must defer to wait for browser to be ready
21354             run : function(){
21355                 //console.log("run task?" + this.doc.readyState);
21356                 this.assignDocWin();
21357                 if(this.doc.body || this.doc.readyState == 'complete'){
21358                     try {
21359                         this.doc.designMode="on";
21360                     } catch (e) {
21361                         return;
21362                     }
21363                     Roo.TaskMgr.stop(task);
21364                     this.initEditor.defer(10, this);
21365                 }
21366             },
21367             interval : 10,
21368             duration: 10000,
21369             scope: this
21370         };
21371         Roo.TaskMgr.start(task);
21372
21373     },
21374
21375     // private
21376     onResize : function(w, h)
21377     {
21378          Roo.log('resize: ' +w + ',' + h );
21379         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21380         if(!this.iframe){
21381             return;
21382         }
21383         if(typeof w == 'number'){
21384             
21385             this.iframe.style.width = w + 'px';
21386         }
21387         if(typeof h == 'number'){
21388             
21389             this.iframe.style.height = h + 'px';
21390             if(this.doc){
21391                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21392             }
21393         }
21394         
21395     },
21396
21397     /**
21398      * Toggles the editor between standard and source edit mode.
21399      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21400      */
21401     toggleSourceEdit : function(sourceEditMode){
21402         
21403         this.sourceEditMode = sourceEditMode === true;
21404         
21405         if(this.sourceEditMode){
21406  
21407             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21408             
21409         }else{
21410             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21411             //this.iframe.className = '';
21412             this.deferFocus();
21413         }
21414         //this.setSize(this.owner.wrap.getSize());
21415         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21416     },
21417
21418     
21419   
21420
21421     /**
21422      * Protected method that will not generally be called directly. If you need/want
21423      * custom HTML cleanup, this is the method you should override.
21424      * @param {String} html The HTML to be cleaned
21425      * return {String} The cleaned HTML
21426      */
21427     cleanHtml : function(html){
21428         html = String(html);
21429         if(html.length > 5){
21430             if(Roo.isSafari){ // strip safari nonsense
21431                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21432             }
21433         }
21434         if(html == '&nbsp;'){
21435             html = '';
21436         }
21437         return html;
21438     },
21439
21440     /**
21441      * HTML Editor -> Textarea
21442      * Protected method that will not generally be called directly. Syncs the contents
21443      * of the editor iframe with the textarea.
21444      */
21445     syncValue : function(){
21446         if(this.initialized){
21447             var bd = (this.doc.body || this.doc.documentElement);
21448             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21449             var html = bd.innerHTML;
21450             if(Roo.isSafari){
21451                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21452                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21453                 if(m && m[1]){
21454                     html = '<div style="'+m[0]+'">' + html + '</div>';
21455                 }
21456             }
21457             html = this.cleanHtml(html);
21458             // fix up the special chars.. normaly like back quotes in word...
21459             // however we do not want to do this with chinese..
21460             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21461                 var cc = b.charCodeAt();
21462                 if (
21463                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21464                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21465                     (cc >= 0xf900 && cc < 0xfb00 )
21466                 ) {
21467                         return b;
21468                 }
21469                 return "&#"+cc+";" 
21470             });
21471             if(this.owner.fireEvent('beforesync', this, html) !== false){
21472                 this.el.dom.value = html;
21473                 this.owner.fireEvent('sync', this, html);
21474             }
21475         }
21476     },
21477
21478     /**
21479      * Protected method that will not generally be called directly. Pushes the value of the textarea
21480      * into the iframe editor.
21481      */
21482     pushValue : function(){
21483         if(this.initialized){
21484             var v = this.el.dom.value.trim();
21485             
21486 //            if(v.length < 1){
21487 //                v = '&#160;';
21488 //            }
21489             
21490             if(this.owner.fireEvent('beforepush', this, v) !== false){
21491                 var d = (this.doc.body || this.doc.documentElement);
21492                 d.innerHTML = v;
21493                 this.cleanUpPaste();
21494                 this.el.dom.value = d.innerHTML;
21495                 this.owner.fireEvent('push', this, v);
21496             }
21497         }
21498     },
21499
21500     // private
21501     deferFocus : function(){
21502         this.focus.defer(10, this);
21503     },
21504
21505     // doc'ed in Field
21506     focus : function(){
21507         if(this.win && !this.sourceEditMode){
21508             this.win.focus();
21509         }else{
21510             this.el.focus();
21511         }
21512     },
21513     
21514     assignDocWin: function()
21515     {
21516         var iframe = this.iframe;
21517         
21518          if(Roo.isIE){
21519             this.doc = iframe.contentWindow.document;
21520             this.win = iframe.contentWindow;
21521         } else {
21522 //            if (!Roo.get(this.frameId)) {
21523 //                return;
21524 //            }
21525 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21526 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21527             
21528             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21529                 return;
21530             }
21531             
21532             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21533             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21534         }
21535     },
21536     
21537     // private
21538     initEditor : function(){
21539         //console.log("INIT EDITOR");
21540         this.assignDocWin();
21541         
21542         
21543         
21544         this.doc.designMode="on";
21545         this.doc.open();
21546         this.doc.write(this.getDocMarkup());
21547         this.doc.close();
21548         
21549         var dbody = (this.doc.body || this.doc.documentElement);
21550         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21551         // this copies styles from the containing element into thsi one..
21552         // not sure why we need all of this..
21553         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21554         
21555         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21556         //ss['background-attachment'] = 'fixed'; // w3c
21557         dbody.bgProperties = 'fixed'; // ie
21558         //Roo.DomHelper.applyStyles(dbody, ss);
21559         Roo.EventManager.on(this.doc, {
21560             //'mousedown': this.onEditorEvent,
21561             'mouseup': this.onEditorEvent,
21562             'dblclick': this.onEditorEvent,
21563             'click': this.onEditorEvent,
21564             'keyup': this.onEditorEvent,
21565             buffer:100,
21566             scope: this
21567         });
21568         if(Roo.isGecko){
21569             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21570         }
21571         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21572             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21573         }
21574         this.initialized = true;
21575
21576         this.owner.fireEvent('initialize', this);
21577         this.pushValue();
21578     },
21579
21580     // private
21581     onDestroy : function(){
21582         
21583         
21584         
21585         if(this.rendered){
21586             
21587             //for (var i =0; i < this.toolbars.length;i++) {
21588             //    // fixme - ask toolbars for heights?
21589             //    this.toolbars[i].onDestroy();
21590            // }
21591             
21592             //this.wrap.dom.innerHTML = '';
21593             //this.wrap.remove();
21594         }
21595     },
21596
21597     // private
21598     onFirstFocus : function(){
21599         
21600         this.assignDocWin();
21601         
21602         
21603         this.activated = true;
21604          
21605     
21606         if(Roo.isGecko){ // prevent silly gecko errors
21607             this.win.focus();
21608             var s = this.win.getSelection();
21609             if(!s.focusNode || s.focusNode.nodeType != 3){
21610                 var r = s.getRangeAt(0);
21611                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21612                 r.collapse(true);
21613                 this.deferFocus();
21614             }
21615             try{
21616                 this.execCmd('useCSS', true);
21617                 this.execCmd('styleWithCSS', false);
21618             }catch(e){}
21619         }
21620         this.owner.fireEvent('activate', this);
21621     },
21622
21623     // private
21624     adjustFont: function(btn){
21625         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21626         //if(Roo.isSafari){ // safari
21627         //    adjust *= 2;
21628        // }
21629         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21630         if(Roo.isSafari){ // safari
21631             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21632             v =  (v < 10) ? 10 : v;
21633             v =  (v > 48) ? 48 : v;
21634             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21635             
21636         }
21637         
21638         
21639         v = Math.max(1, v+adjust);
21640         
21641         this.execCmd('FontSize', v  );
21642     },
21643
21644     onEditorEvent : function(e)
21645     {
21646         this.owner.fireEvent('editorevent', this, e);
21647       //  this.updateToolbar();
21648         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21649     },
21650
21651     insertTag : function(tg)
21652     {
21653         // could be a bit smarter... -> wrap the current selected tRoo..
21654         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21655             
21656             range = this.createRange(this.getSelection());
21657             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21658             wrappingNode.appendChild(range.extractContents());
21659             range.insertNode(wrappingNode);
21660
21661             return;
21662             
21663             
21664             
21665         }
21666         this.execCmd("formatblock",   tg);
21667         
21668     },
21669     
21670     insertText : function(txt)
21671     {
21672         
21673         
21674         var range = this.createRange();
21675         range.deleteContents();
21676                //alert(Sender.getAttribute('label'));
21677                
21678         range.insertNode(this.doc.createTextNode(txt));
21679     } ,
21680     
21681      
21682
21683     /**
21684      * Executes a Midas editor command on the editor document and performs necessary focus and
21685      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21686      * @param {String} cmd The Midas command
21687      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21688      */
21689     relayCmd : function(cmd, value){
21690         this.win.focus();
21691         this.execCmd(cmd, value);
21692         this.owner.fireEvent('editorevent', this);
21693         //this.updateToolbar();
21694         this.owner.deferFocus();
21695     },
21696
21697     /**
21698      * Executes a Midas editor command directly on the editor document.
21699      * For visual commands, you should use {@link #relayCmd} instead.
21700      * <b>This should only be called after the editor is initialized.</b>
21701      * @param {String} cmd The Midas command
21702      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21703      */
21704     execCmd : function(cmd, value){
21705         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21706         this.syncValue();
21707     },
21708  
21709  
21710    
21711     /**
21712      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21713      * to insert tRoo.
21714      * @param {String} text | dom node.. 
21715      */
21716     insertAtCursor : function(text)
21717     {
21718         
21719         if(!this.activated){
21720             return;
21721         }
21722         /*
21723         if(Roo.isIE){
21724             this.win.focus();
21725             var r = this.doc.selection.createRange();
21726             if(r){
21727                 r.collapse(true);
21728                 r.pasteHTML(text);
21729                 this.syncValue();
21730                 this.deferFocus();
21731             
21732             }
21733             return;
21734         }
21735         */
21736         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21737             this.win.focus();
21738             
21739             
21740             // from jquery ui (MIT licenced)
21741             var range, node;
21742             var win = this.win;
21743             
21744             if (win.getSelection && win.getSelection().getRangeAt) {
21745                 range = win.getSelection().getRangeAt(0);
21746                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21747                 range.insertNode(node);
21748             } else if (win.document.selection && win.document.selection.createRange) {
21749                 // no firefox support
21750                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21751                 win.document.selection.createRange().pasteHTML(txt);
21752             } else {
21753                 // no firefox support
21754                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21755                 this.execCmd('InsertHTML', txt);
21756             } 
21757             
21758             this.syncValue();
21759             
21760             this.deferFocus();
21761         }
21762     },
21763  // private
21764     mozKeyPress : function(e){
21765         if(e.ctrlKey){
21766             var c = e.getCharCode(), cmd;
21767           
21768             if(c > 0){
21769                 c = String.fromCharCode(c).toLowerCase();
21770                 switch(c){
21771                     case 'b':
21772                         cmd = 'bold';
21773                         break;
21774                     case 'i':
21775                         cmd = 'italic';
21776                         break;
21777                     
21778                     case 'u':
21779                         cmd = 'underline';
21780                         break;
21781                     
21782                     case 'v':
21783                         this.cleanUpPaste.defer(100, this);
21784                         return;
21785                         
21786                 }
21787                 if(cmd){
21788                     this.win.focus();
21789                     this.execCmd(cmd);
21790                     this.deferFocus();
21791                     e.preventDefault();
21792                 }
21793                 
21794             }
21795         }
21796     },
21797
21798     // private
21799     fixKeys : function(){ // load time branching for fastest keydown performance
21800         if(Roo.isIE){
21801             return function(e){
21802                 var k = e.getKey(), r;
21803                 if(k == e.TAB){
21804                     e.stopEvent();
21805                     r = this.doc.selection.createRange();
21806                     if(r){
21807                         r.collapse(true);
21808                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21809                         this.deferFocus();
21810                     }
21811                     return;
21812                 }
21813                 
21814                 if(k == e.ENTER){
21815                     r = this.doc.selection.createRange();
21816                     if(r){
21817                         var target = r.parentElement();
21818                         if(!target || target.tagName.toLowerCase() != 'li'){
21819                             e.stopEvent();
21820                             r.pasteHTML('<br />');
21821                             r.collapse(false);
21822                             r.select();
21823                         }
21824                     }
21825                 }
21826                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21827                     this.cleanUpPaste.defer(100, this);
21828                     return;
21829                 }
21830                 
21831                 
21832             };
21833         }else if(Roo.isOpera){
21834             return function(e){
21835                 var k = e.getKey();
21836                 if(k == e.TAB){
21837                     e.stopEvent();
21838                     this.win.focus();
21839                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21840                     this.deferFocus();
21841                 }
21842                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21843                     this.cleanUpPaste.defer(100, this);
21844                     return;
21845                 }
21846                 
21847             };
21848         }else if(Roo.isSafari){
21849             return function(e){
21850                 var k = e.getKey();
21851                 
21852                 if(k == e.TAB){
21853                     e.stopEvent();
21854                     this.execCmd('InsertText','\t');
21855                     this.deferFocus();
21856                     return;
21857                 }
21858                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21859                     this.cleanUpPaste.defer(100, this);
21860                     return;
21861                 }
21862                 
21863              };
21864         }
21865     }(),
21866     
21867     getAllAncestors: function()
21868     {
21869         var p = this.getSelectedNode();
21870         var a = [];
21871         if (!p) {
21872             a.push(p); // push blank onto stack..
21873             p = this.getParentElement();
21874         }
21875         
21876         
21877         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21878             a.push(p);
21879             p = p.parentNode;
21880         }
21881         a.push(this.doc.body);
21882         return a;
21883     },
21884     lastSel : false,
21885     lastSelNode : false,
21886     
21887     
21888     getSelection : function() 
21889     {
21890         this.assignDocWin();
21891         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21892     },
21893     
21894     getSelectedNode: function() 
21895     {
21896         // this may only work on Gecko!!!
21897         
21898         // should we cache this!!!!
21899         
21900         
21901         
21902          
21903         var range = this.createRange(this.getSelection()).cloneRange();
21904         
21905         if (Roo.isIE) {
21906             var parent = range.parentElement();
21907             while (true) {
21908                 var testRange = range.duplicate();
21909                 testRange.moveToElementText(parent);
21910                 if (testRange.inRange(range)) {
21911                     break;
21912                 }
21913                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21914                     break;
21915                 }
21916                 parent = parent.parentElement;
21917             }
21918             return parent;
21919         }
21920         
21921         // is ancestor a text element.
21922         var ac =  range.commonAncestorContainer;
21923         if (ac.nodeType == 3) {
21924             ac = ac.parentNode;
21925         }
21926         
21927         var ar = ac.childNodes;
21928          
21929         var nodes = [];
21930         var other_nodes = [];
21931         var has_other_nodes = false;
21932         for (var i=0;i<ar.length;i++) {
21933             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21934                 continue;
21935             }
21936             // fullly contained node.
21937             
21938             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21939                 nodes.push(ar[i]);
21940                 continue;
21941             }
21942             
21943             // probably selected..
21944             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21945                 other_nodes.push(ar[i]);
21946                 continue;
21947             }
21948             // outer..
21949             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21950                 continue;
21951             }
21952             
21953             
21954             has_other_nodes = true;
21955         }
21956         if (!nodes.length && other_nodes.length) {
21957             nodes= other_nodes;
21958         }
21959         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21960             return false;
21961         }
21962         
21963         return nodes[0];
21964     },
21965     createRange: function(sel)
21966     {
21967         // this has strange effects when using with 
21968         // top toolbar - not sure if it's a great idea.
21969         //this.editor.contentWindow.focus();
21970         if (typeof sel != "undefined") {
21971             try {
21972                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21973             } catch(e) {
21974                 return this.doc.createRange();
21975             }
21976         } else {
21977             return this.doc.createRange();
21978         }
21979     },
21980     getParentElement: function()
21981     {
21982         
21983         this.assignDocWin();
21984         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21985         
21986         var range = this.createRange(sel);
21987          
21988         try {
21989             var p = range.commonAncestorContainer;
21990             while (p.nodeType == 3) { // text node
21991                 p = p.parentNode;
21992             }
21993             return p;
21994         } catch (e) {
21995             return null;
21996         }
21997     
21998     },
21999     /***
22000      *
22001      * Range intersection.. the hard stuff...
22002      *  '-1' = before
22003      *  '0' = hits..
22004      *  '1' = after.
22005      *         [ -- selected range --- ]
22006      *   [fail]                        [fail]
22007      *
22008      *    basically..
22009      *      if end is before start or  hits it. fail.
22010      *      if start is after end or hits it fail.
22011      *
22012      *   if either hits (but other is outside. - then it's not 
22013      *   
22014      *    
22015      **/
22016     
22017     
22018     // @see http://www.thismuchiknow.co.uk/?p=64.
22019     rangeIntersectsNode : function(range, node)
22020     {
22021         var nodeRange = node.ownerDocument.createRange();
22022         try {
22023             nodeRange.selectNode(node);
22024         } catch (e) {
22025             nodeRange.selectNodeContents(node);
22026         }
22027     
22028         var rangeStartRange = range.cloneRange();
22029         rangeStartRange.collapse(true);
22030     
22031         var rangeEndRange = range.cloneRange();
22032         rangeEndRange.collapse(false);
22033     
22034         var nodeStartRange = nodeRange.cloneRange();
22035         nodeStartRange.collapse(true);
22036     
22037         var nodeEndRange = nodeRange.cloneRange();
22038         nodeEndRange.collapse(false);
22039     
22040         return rangeStartRange.compareBoundaryPoints(
22041                  Range.START_TO_START, nodeEndRange) == -1 &&
22042                rangeEndRange.compareBoundaryPoints(
22043                  Range.START_TO_START, nodeStartRange) == 1;
22044         
22045          
22046     },
22047     rangeCompareNode : function(range, node)
22048     {
22049         var nodeRange = node.ownerDocument.createRange();
22050         try {
22051             nodeRange.selectNode(node);
22052         } catch (e) {
22053             nodeRange.selectNodeContents(node);
22054         }
22055         
22056         
22057         range.collapse(true);
22058     
22059         nodeRange.collapse(true);
22060      
22061         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22062         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22063          
22064         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22065         
22066         var nodeIsBefore   =  ss == 1;
22067         var nodeIsAfter    = ee == -1;
22068         
22069         if (nodeIsBefore && nodeIsAfter) {
22070             return 0; // outer
22071         }
22072         if (!nodeIsBefore && nodeIsAfter) {
22073             return 1; //right trailed.
22074         }
22075         
22076         if (nodeIsBefore && !nodeIsAfter) {
22077             return 2;  // left trailed.
22078         }
22079         // fully contined.
22080         return 3;
22081     },
22082
22083     // private? - in a new class?
22084     cleanUpPaste :  function()
22085     {
22086         // cleans up the whole document..
22087         Roo.log('cleanuppaste');
22088         
22089         this.cleanUpChildren(this.doc.body);
22090         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22091         if (clean != this.doc.body.innerHTML) {
22092             this.doc.body.innerHTML = clean;
22093         }
22094         
22095     },
22096     
22097     cleanWordChars : function(input) {// change the chars to hex code
22098         var he = Roo.HtmlEditorCore;
22099         
22100         var output = input;
22101         Roo.each(he.swapCodes, function(sw) { 
22102             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22103             
22104             output = output.replace(swapper, sw[1]);
22105         });
22106         
22107         return output;
22108     },
22109     
22110     
22111     cleanUpChildren : function (n)
22112     {
22113         if (!n.childNodes.length) {
22114             return;
22115         }
22116         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22117            this.cleanUpChild(n.childNodes[i]);
22118         }
22119     },
22120     
22121     
22122         
22123     
22124     cleanUpChild : function (node)
22125     {
22126         var ed = this;
22127         //console.log(node);
22128         if (node.nodeName == "#text") {
22129             // clean up silly Windows -- stuff?
22130             return; 
22131         }
22132         if (node.nodeName == "#comment") {
22133             node.parentNode.removeChild(node);
22134             // clean up silly Windows -- stuff?
22135             return; 
22136         }
22137         var lcname = node.tagName.toLowerCase();
22138         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22139         // whitelist of tags..
22140         
22141         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22142             // remove node.
22143             node.parentNode.removeChild(node);
22144             return;
22145             
22146         }
22147         
22148         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22149         
22150         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22151         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22152         
22153         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22154         //    remove_keep_children = true;
22155         //}
22156         
22157         if (remove_keep_children) {
22158             this.cleanUpChildren(node);
22159             // inserts everything just before this node...
22160             while (node.childNodes.length) {
22161                 var cn = node.childNodes[0];
22162                 node.removeChild(cn);
22163                 node.parentNode.insertBefore(cn, node);
22164             }
22165             node.parentNode.removeChild(node);
22166             return;
22167         }
22168         
22169         if (!node.attributes || !node.attributes.length) {
22170             this.cleanUpChildren(node);
22171             return;
22172         }
22173         
22174         function cleanAttr(n,v)
22175         {
22176             
22177             if (v.match(/^\./) || v.match(/^\//)) {
22178                 return;
22179             }
22180             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22181                 return;
22182             }
22183             if (v.match(/^#/)) {
22184                 return;
22185             }
22186 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22187             node.removeAttribute(n);
22188             
22189         }
22190         
22191         var cwhite = this.cwhite;
22192         var cblack = this.cblack;
22193             
22194         function cleanStyle(n,v)
22195         {
22196             if (v.match(/expression/)) { //XSS?? should we even bother..
22197                 node.removeAttribute(n);
22198                 return;
22199             }
22200             
22201             var parts = v.split(/;/);
22202             var clean = [];
22203             
22204             Roo.each(parts, function(p) {
22205                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22206                 if (!p.length) {
22207                     return true;
22208                 }
22209                 var l = p.split(':').shift().replace(/\s+/g,'');
22210                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22211                 
22212                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22213 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22214                     //node.removeAttribute(n);
22215                     return true;
22216                 }
22217                 //Roo.log()
22218                 // only allow 'c whitelisted system attributes'
22219                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22220 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22221                     //node.removeAttribute(n);
22222                     return true;
22223                 }
22224                 
22225                 
22226                  
22227                 
22228                 clean.push(p);
22229                 return true;
22230             });
22231             if (clean.length) { 
22232                 node.setAttribute(n, clean.join(';'));
22233             } else {
22234                 node.removeAttribute(n);
22235             }
22236             
22237         }
22238         
22239         
22240         for (var i = node.attributes.length-1; i > -1 ; i--) {
22241             var a = node.attributes[i];
22242             //console.log(a);
22243             
22244             if (a.name.toLowerCase().substr(0,2)=='on')  {
22245                 node.removeAttribute(a.name);
22246                 continue;
22247             }
22248             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22249                 node.removeAttribute(a.name);
22250                 continue;
22251             }
22252             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22253                 cleanAttr(a.name,a.value); // fixme..
22254                 continue;
22255             }
22256             if (a.name == 'style') {
22257                 cleanStyle(a.name,a.value);
22258                 continue;
22259             }
22260             /// clean up MS crap..
22261             // tecnically this should be a list of valid class'es..
22262             
22263             
22264             if (a.name == 'class') {
22265                 if (a.value.match(/^Mso/)) {
22266                     node.className = '';
22267                 }
22268                 
22269                 if (a.value.match(/^body$/)) {
22270                     node.className = '';
22271                 }
22272                 continue;
22273             }
22274             
22275             // style cleanup!?
22276             // class cleanup?
22277             
22278         }
22279         
22280         
22281         this.cleanUpChildren(node);
22282         
22283         
22284     },
22285     
22286     /**
22287      * Clean up MS wordisms...
22288      */
22289     cleanWord : function(node)
22290     {
22291         
22292         
22293         if (!node) {
22294             this.cleanWord(this.doc.body);
22295             return;
22296         }
22297         if (node.nodeName == "#text") {
22298             // clean up silly Windows -- stuff?
22299             return; 
22300         }
22301         if (node.nodeName == "#comment") {
22302             node.parentNode.removeChild(node);
22303             // clean up silly Windows -- stuff?
22304             return; 
22305         }
22306         
22307         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22308             node.parentNode.removeChild(node);
22309             return;
22310         }
22311         
22312         // remove - but keep children..
22313         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22314             while (node.childNodes.length) {
22315                 var cn = node.childNodes[0];
22316                 node.removeChild(cn);
22317                 node.parentNode.insertBefore(cn, node);
22318             }
22319             node.parentNode.removeChild(node);
22320             this.iterateChildren(node, this.cleanWord);
22321             return;
22322         }
22323         // clean styles
22324         if (node.className.length) {
22325             
22326             var cn = node.className.split(/\W+/);
22327             var cna = [];
22328             Roo.each(cn, function(cls) {
22329                 if (cls.match(/Mso[a-zA-Z]+/)) {
22330                     return;
22331                 }
22332                 cna.push(cls);
22333             });
22334             node.className = cna.length ? cna.join(' ') : '';
22335             if (!cna.length) {
22336                 node.removeAttribute("class");
22337             }
22338         }
22339         
22340         if (node.hasAttribute("lang")) {
22341             node.removeAttribute("lang");
22342         }
22343         
22344         if (node.hasAttribute("style")) {
22345             
22346             var styles = node.getAttribute("style").split(";");
22347             var nstyle = [];
22348             Roo.each(styles, function(s) {
22349                 if (!s.match(/:/)) {
22350                     return;
22351                 }
22352                 var kv = s.split(":");
22353                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22354                     return;
22355                 }
22356                 // what ever is left... we allow.
22357                 nstyle.push(s);
22358             });
22359             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22360             if (!nstyle.length) {
22361                 node.removeAttribute('style');
22362             }
22363         }
22364         this.iterateChildren(node, this.cleanWord);
22365         
22366         
22367         
22368     },
22369     /**
22370      * iterateChildren of a Node, calling fn each time, using this as the scole..
22371      * @param {DomNode} node node to iterate children of.
22372      * @param {Function} fn method of this class to call on each item.
22373      */
22374     iterateChildren : function(node, fn)
22375     {
22376         if (!node.childNodes.length) {
22377                 return;
22378         }
22379         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22380            fn.call(this, node.childNodes[i])
22381         }
22382     },
22383     
22384     
22385     /**
22386      * cleanTableWidths.
22387      *
22388      * Quite often pasting from word etc.. results in tables with column and widths.
22389      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22390      *
22391      */
22392     cleanTableWidths : function(node)
22393     {
22394          
22395          
22396         if (!node) {
22397             this.cleanTableWidths(this.doc.body);
22398             return;
22399         }
22400         
22401         // ignore list...
22402         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22403             return; 
22404         }
22405         Roo.log(node.tagName);
22406         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22407             this.iterateChildren(node, this.cleanTableWidths);
22408             return;
22409         }
22410         if (node.hasAttribute('width')) {
22411             node.removeAttribute('width');
22412         }
22413         
22414          
22415         if (node.hasAttribute("style")) {
22416             // pretty basic...
22417             
22418             var styles = node.getAttribute("style").split(";");
22419             var nstyle = [];
22420             Roo.each(styles, function(s) {
22421                 if (!s.match(/:/)) {
22422                     return;
22423                 }
22424                 var kv = s.split(":");
22425                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22426                     return;
22427                 }
22428                 // what ever is left... we allow.
22429                 nstyle.push(s);
22430             });
22431             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22432             if (!nstyle.length) {
22433                 node.removeAttribute('style');
22434             }
22435         }
22436         
22437         this.iterateChildren(node, this.cleanTableWidths);
22438         
22439         
22440     },
22441     
22442     
22443     
22444     
22445     domToHTML : function(currentElement, depth, nopadtext) {
22446         
22447         depth = depth || 0;
22448         nopadtext = nopadtext || false;
22449     
22450         if (!currentElement) {
22451             return this.domToHTML(this.doc.body);
22452         }
22453         
22454         //Roo.log(currentElement);
22455         var j;
22456         var allText = false;
22457         var nodeName = currentElement.nodeName;
22458         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22459         
22460         if  (nodeName == '#text') {
22461             
22462             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22463         }
22464         
22465         
22466         var ret = '';
22467         if (nodeName != 'BODY') {
22468              
22469             var i = 0;
22470             // Prints the node tagName, such as <A>, <IMG>, etc
22471             if (tagName) {
22472                 var attr = [];
22473                 for(i = 0; i < currentElement.attributes.length;i++) {
22474                     // quoting?
22475                     var aname = currentElement.attributes.item(i).name;
22476                     if (!currentElement.attributes.item(i).value.length) {
22477                         continue;
22478                     }
22479                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22480                 }
22481                 
22482                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22483             } 
22484             else {
22485                 
22486                 // eack
22487             }
22488         } else {
22489             tagName = false;
22490         }
22491         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22492             return ret;
22493         }
22494         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22495             nopadtext = true;
22496         }
22497         
22498         
22499         // Traverse the tree
22500         i = 0;
22501         var currentElementChild = currentElement.childNodes.item(i);
22502         var allText = true;
22503         var innerHTML  = '';
22504         lastnode = '';
22505         while (currentElementChild) {
22506             // Formatting code (indent the tree so it looks nice on the screen)
22507             var nopad = nopadtext;
22508             if (lastnode == 'SPAN') {
22509                 nopad  = true;
22510             }
22511             // text
22512             if  (currentElementChild.nodeName == '#text') {
22513                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22514                 toadd = nopadtext ? toadd : toadd.trim();
22515                 if (!nopad && toadd.length > 80) {
22516                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22517                 }
22518                 innerHTML  += toadd;
22519                 
22520                 i++;
22521                 currentElementChild = currentElement.childNodes.item(i);
22522                 lastNode = '';
22523                 continue;
22524             }
22525             allText = false;
22526             
22527             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22528                 
22529             // Recursively traverse the tree structure of the child node
22530             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22531             lastnode = currentElementChild.nodeName;
22532             i++;
22533             currentElementChild=currentElement.childNodes.item(i);
22534         }
22535         
22536         ret += innerHTML;
22537         
22538         if (!allText) {
22539                 // The remaining code is mostly for formatting the tree
22540             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22541         }
22542         
22543         
22544         if (tagName) {
22545             ret+= "</"+tagName+">";
22546         }
22547         return ret;
22548         
22549     },
22550         
22551     applyBlacklists : function()
22552     {
22553         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22554         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22555         
22556         this.white = [];
22557         this.black = [];
22558         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22559             if (b.indexOf(tag) > -1) {
22560                 return;
22561             }
22562             this.white.push(tag);
22563             
22564         }, this);
22565         
22566         Roo.each(w, function(tag) {
22567             if (b.indexOf(tag) > -1) {
22568                 return;
22569             }
22570             if (this.white.indexOf(tag) > -1) {
22571                 return;
22572             }
22573             this.white.push(tag);
22574             
22575         }, this);
22576         
22577         
22578         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22579             if (w.indexOf(tag) > -1) {
22580                 return;
22581             }
22582             this.black.push(tag);
22583             
22584         }, this);
22585         
22586         Roo.each(b, function(tag) {
22587             if (w.indexOf(tag) > -1) {
22588                 return;
22589             }
22590             if (this.black.indexOf(tag) > -1) {
22591                 return;
22592             }
22593             this.black.push(tag);
22594             
22595         }, this);
22596         
22597         
22598         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22599         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22600         
22601         this.cwhite = [];
22602         this.cblack = [];
22603         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22604             if (b.indexOf(tag) > -1) {
22605                 return;
22606             }
22607             this.cwhite.push(tag);
22608             
22609         }, this);
22610         
22611         Roo.each(w, function(tag) {
22612             if (b.indexOf(tag) > -1) {
22613                 return;
22614             }
22615             if (this.cwhite.indexOf(tag) > -1) {
22616                 return;
22617             }
22618             this.cwhite.push(tag);
22619             
22620         }, this);
22621         
22622         
22623         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22624             if (w.indexOf(tag) > -1) {
22625                 return;
22626             }
22627             this.cblack.push(tag);
22628             
22629         }, this);
22630         
22631         Roo.each(b, function(tag) {
22632             if (w.indexOf(tag) > -1) {
22633                 return;
22634             }
22635             if (this.cblack.indexOf(tag) > -1) {
22636                 return;
22637             }
22638             this.cblack.push(tag);
22639             
22640         }, this);
22641     },
22642     
22643     setStylesheets : function(stylesheets)
22644     {
22645         if(typeof(stylesheets) == 'string'){
22646             Roo.get(this.iframe.contentDocument.head).createChild({
22647                 tag : 'link',
22648                 rel : 'stylesheet',
22649                 type : 'text/css',
22650                 href : stylesheets
22651             });
22652             
22653             return;
22654         }
22655         var _this = this;
22656      
22657         Roo.each(stylesheets, function(s) {
22658             if(!s.length){
22659                 return;
22660             }
22661             
22662             Roo.get(_this.iframe.contentDocument.head).createChild({
22663                 tag : 'link',
22664                 rel : 'stylesheet',
22665                 type : 'text/css',
22666                 href : s
22667             });
22668         });
22669
22670         
22671     },
22672     
22673     removeStylesheets : function()
22674     {
22675         var _this = this;
22676         
22677         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22678             s.remove();
22679         });
22680     }
22681     
22682     // hide stuff that is not compatible
22683     /**
22684      * @event blur
22685      * @hide
22686      */
22687     /**
22688      * @event change
22689      * @hide
22690      */
22691     /**
22692      * @event focus
22693      * @hide
22694      */
22695     /**
22696      * @event specialkey
22697      * @hide
22698      */
22699     /**
22700      * @cfg {String} fieldClass @hide
22701      */
22702     /**
22703      * @cfg {String} focusClass @hide
22704      */
22705     /**
22706      * @cfg {String} autoCreate @hide
22707      */
22708     /**
22709      * @cfg {String} inputType @hide
22710      */
22711     /**
22712      * @cfg {String} invalidClass @hide
22713      */
22714     /**
22715      * @cfg {String} invalidText @hide
22716      */
22717     /**
22718      * @cfg {String} msgFx @hide
22719      */
22720     /**
22721      * @cfg {String} validateOnBlur @hide
22722      */
22723 });
22724
22725 Roo.HtmlEditorCore.white = [
22726         'area', 'br', 'img', 'input', 'hr', 'wbr',
22727         
22728        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22729        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22730        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22731        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22732        'table',   'ul',         'xmp', 
22733        
22734        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22735       'thead',   'tr', 
22736      
22737       'dir', 'menu', 'ol', 'ul', 'dl',
22738        
22739       'embed',  'object'
22740 ];
22741
22742
22743 Roo.HtmlEditorCore.black = [
22744     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22745         'applet', // 
22746         'base',   'basefont', 'bgsound', 'blink',  'body', 
22747         'frame',  'frameset', 'head',    'html',   'ilayer', 
22748         'iframe', 'layer',  'link',     'meta',    'object',   
22749         'script', 'style' ,'title',  'xml' // clean later..
22750 ];
22751 Roo.HtmlEditorCore.clean = [
22752     'script', 'style', 'title', 'xml'
22753 ];
22754 Roo.HtmlEditorCore.remove = [
22755     'font'
22756 ];
22757 // attributes..
22758
22759 Roo.HtmlEditorCore.ablack = [
22760     'on'
22761 ];
22762     
22763 Roo.HtmlEditorCore.aclean = [ 
22764     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22765 ];
22766
22767 // protocols..
22768 Roo.HtmlEditorCore.pwhite= [
22769         'http',  'https',  'mailto'
22770 ];
22771
22772 // white listed style attributes.
22773 Roo.HtmlEditorCore.cwhite= [
22774       //  'text-align', /// default is to allow most things..
22775       
22776          
22777 //        'font-size'//??
22778 ];
22779
22780 // black listed style attributes.
22781 Roo.HtmlEditorCore.cblack= [
22782       //  'font-size' -- this can be set by the project 
22783 ];
22784
22785
22786 Roo.HtmlEditorCore.swapCodes   =[ 
22787     [    8211, "--" ], 
22788     [    8212, "--" ], 
22789     [    8216,  "'" ],  
22790     [    8217, "'" ],  
22791     [    8220, '"' ],  
22792     [    8221, '"' ],  
22793     [    8226, "*" ],  
22794     [    8230, "..." ]
22795 ]; 
22796
22797     /*
22798  * - LGPL
22799  *
22800  * HtmlEditor
22801  * 
22802  */
22803
22804 /**
22805  * @class Roo.bootstrap.HtmlEditor
22806  * @extends Roo.bootstrap.TextArea
22807  * Bootstrap HtmlEditor class
22808
22809  * @constructor
22810  * Create a new HtmlEditor
22811  * @param {Object} config The config object
22812  */
22813
22814 Roo.bootstrap.HtmlEditor = function(config){
22815     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22816     if (!this.toolbars) {
22817         this.toolbars = [];
22818     }
22819     
22820     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22821     this.addEvents({
22822             /**
22823              * @event initialize
22824              * Fires when the editor is fully initialized (including the iframe)
22825              * @param {HtmlEditor} this
22826              */
22827             initialize: true,
22828             /**
22829              * @event activate
22830              * Fires when the editor is first receives the focus. Any insertion must wait
22831              * until after this event.
22832              * @param {HtmlEditor} this
22833              */
22834             activate: true,
22835              /**
22836              * @event beforesync
22837              * Fires before the textarea is updated with content from the editor iframe. Return false
22838              * to cancel the sync.
22839              * @param {HtmlEditor} this
22840              * @param {String} html
22841              */
22842             beforesync: true,
22843              /**
22844              * @event beforepush
22845              * Fires before the iframe editor is updated with content from the textarea. Return false
22846              * to cancel the push.
22847              * @param {HtmlEditor} this
22848              * @param {String} html
22849              */
22850             beforepush: true,
22851              /**
22852              * @event sync
22853              * Fires when the textarea is updated with content from the editor iframe.
22854              * @param {HtmlEditor} this
22855              * @param {String} html
22856              */
22857             sync: true,
22858              /**
22859              * @event push
22860              * Fires when the iframe editor is updated with content from the textarea.
22861              * @param {HtmlEditor} this
22862              * @param {String} html
22863              */
22864             push: true,
22865              /**
22866              * @event editmodechange
22867              * Fires when the editor switches edit modes
22868              * @param {HtmlEditor} this
22869              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22870              */
22871             editmodechange: true,
22872             /**
22873              * @event editorevent
22874              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22875              * @param {HtmlEditor} this
22876              */
22877             editorevent: true,
22878             /**
22879              * @event firstfocus
22880              * Fires when on first focus - needed by toolbars..
22881              * @param {HtmlEditor} this
22882              */
22883             firstfocus: true,
22884             /**
22885              * @event autosave
22886              * Auto save the htmlEditor value as a file into Events
22887              * @param {HtmlEditor} this
22888              */
22889             autosave: true,
22890             /**
22891              * @event savedpreview
22892              * preview the saved version of htmlEditor
22893              * @param {HtmlEditor} this
22894              */
22895             savedpreview: true
22896         });
22897 };
22898
22899
22900 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22901     
22902     
22903       /**
22904      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22905      */
22906     toolbars : false,
22907     
22908      /**
22909     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22910     */
22911     btns : [],
22912    
22913      /**
22914      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22915      *                        Roo.resizable.
22916      */
22917     resizable : false,
22918      /**
22919      * @cfg {Number} height (in pixels)
22920      */   
22921     height: 300,
22922    /**
22923      * @cfg {Number} width (in pixels)
22924      */   
22925     width: false,
22926     
22927     /**
22928      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22929      * 
22930      */
22931     stylesheets: false,
22932     
22933     // id of frame..
22934     frameId: false,
22935     
22936     // private properties
22937     validationEvent : false,
22938     deferHeight: true,
22939     initialized : false,
22940     activated : false,
22941     
22942     onFocus : Roo.emptyFn,
22943     iframePad:3,
22944     hideMode:'offsets',
22945     
22946     tbContainer : false,
22947     
22948     toolbarContainer :function() {
22949         return this.wrap.select('.x-html-editor-tb',true).first();
22950     },
22951
22952     /**
22953      * Protected method that will not generally be called directly. It
22954      * is called when the editor creates its toolbar. Override this method if you need to
22955      * add custom toolbar buttons.
22956      * @param {HtmlEditor} editor
22957      */
22958     createToolbar : function(){
22959         Roo.log('renewing');
22960         Roo.log("create toolbars");
22961         
22962         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22963         this.toolbars[0].render(this.toolbarContainer());
22964         
22965         return;
22966         
22967 //        if (!editor.toolbars || !editor.toolbars.length) {
22968 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22969 //        }
22970 //        
22971 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22972 //            editor.toolbars[i] = Roo.factory(
22973 //                    typeof(editor.toolbars[i]) == 'string' ?
22974 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22975 //                Roo.bootstrap.HtmlEditor);
22976 //            editor.toolbars[i].init(editor);
22977 //        }
22978     },
22979
22980      
22981     // private
22982     onRender : function(ct, position)
22983     {
22984        // Roo.log("Call onRender: " + this.xtype);
22985         var _t = this;
22986         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22987       
22988         this.wrap = this.inputEl().wrap({
22989             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22990         });
22991         
22992         this.editorcore.onRender(ct, position);
22993          
22994         if (this.resizable) {
22995             this.resizeEl = new Roo.Resizable(this.wrap, {
22996                 pinned : true,
22997                 wrap: true,
22998                 dynamic : true,
22999                 minHeight : this.height,
23000                 height: this.height,
23001                 handles : this.resizable,
23002                 width: this.width,
23003                 listeners : {
23004                     resize : function(r, w, h) {
23005                         _t.onResize(w,h); // -something
23006                     }
23007                 }
23008             });
23009             
23010         }
23011         this.createToolbar(this);
23012        
23013         
23014         if(!this.width && this.resizable){
23015             this.setSize(this.wrap.getSize());
23016         }
23017         if (this.resizeEl) {
23018             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23019             // should trigger onReize..
23020         }
23021         
23022     },
23023
23024     // private
23025     onResize : function(w, h)
23026     {
23027         Roo.log('resize: ' +w + ',' + h );
23028         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23029         var ew = false;
23030         var eh = false;
23031         
23032         if(this.inputEl() ){
23033             if(typeof w == 'number'){
23034                 var aw = w - this.wrap.getFrameWidth('lr');
23035                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23036                 ew = aw;
23037             }
23038             if(typeof h == 'number'){
23039                  var tbh = -11;  // fixme it needs to tool bar size!
23040                 for (var i =0; i < this.toolbars.length;i++) {
23041                     // fixme - ask toolbars for heights?
23042                     tbh += this.toolbars[i].el.getHeight();
23043                     //if (this.toolbars[i].footer) {
23044                     //    tbh += this.toolbars[i].footer.el.getHeight();
23045                     //}
23046                 }
23047               
23048                 
23049                 
23050                 
23051                 
23052                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23053                 ah -= 5; // knock a few pixes off for look..
23054                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23055                 var eh = ah;
23056             }
23057         }
23058         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23059         this.editorcore.onResize(ew,eh);
23060         
23061     },
23062
23063     /**
23064      * Toggles the editor between standard and source edit mode.
23065      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23066      */
23067     toggleSourceEdit : function(sourceEditMode)
23068     {
23069         this.editorcore.toggleSourceEdit(sourceEditMode);
23070         
23071         if(this.editorcore.sourceEditMode){
23072             Roo.log('editor - showing textarea');
23073             
23074 //            Roo.log('in');
23075 //            Roo.log(this.syncValue());
23076             this.syncValue();
23077             this.inputEl().removeClass(['hide', 'x-hidden']);
23078             this.inputEl().dom.removeAttribute('tabIndex');
23079             this.inputEl().focus();
23080         }else{
23081             Roo.log('editor - hiding textarea');
23082 //            Roo.log('out')
23083 //            Roo.log(this.pushValue()); 
23084             this.pushValue();
23085             
23086             this.inputEl().addClass(['hide', 'x-hidden']);
23087             this.inputEl().dom.setAttribute('tabIndex', -1);
23088             //this.deferFocus();
23089         }
23090          
23091         if(this.resizable){
23092             this.setSize(this.wrap.getSize());
23093         }
23094         
23095         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23096     },
23097  
23098     // private (for BoxComponent)
23099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23100
23101     // private (for BoxComponent)
23102     getResizeEl : function(){
23103         return this.wrap;
23104     },
23105
23106     // private (for BoxComponent)
23107     getPositionEl : function(){
23108         return this.wrap;
23109     },
23110
23111     // private
23112     initEvents : function(){
23113         this.originalValue = this.getValue();
23114     },
23115
23116 //    /**
23117 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23118 //     * @method
23119 //     */
23120 //    markInvalid : Roo.emptyFn,
23121 //    /**
23122 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23123 //     * @method
23124 //     */
23125 //    clearInvalid : Roo.emptyFn,
23126
23127     setValue : function(v){
23128         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23129         this.editorcore.pushValue();
23130     },
23131
23132      
23133     // private
23134     deferFocus : function(){
23135         this.focus.defer(10, this);
23136     },
23137
23138     // doc'ed in Field
23139     focus : function(){
23140         this.editorcore.focus();
23141         
23142     },
23143       
23144
23145     // private
23146     onDestroy : function(){
23147         
23148         
23149         
23150         if(this.rendered){
23151             
23152             for (var i =0; i < this.toolbars.length;i++) {
23153                 // fixme - ask toolbars for heights?
23154                 this.toolbars[i].onDestroy();
23155             }
23156             
23157             this.wrap.dom.innerHTML = '';
23158             this.wrap.remove();
23159         }
23160     },
23161
23162     // private
23163     onFirstFocus : function(){
23164         //Roo.log("onFirstFocus");
23165         this.editorcore.onFirstFocus();
23166          for (var i =0; i < this.toolbars.length;i++) {
23167             this.toolbars[i].onFirstFocus();
23168         }
23169         
23170     },
23171     
23172     // private
23173     syncValue : function()
23174     {   
23175         this.editorcore.syncValue();
23176     },
23177     
23178     pushValue : function()
23179     {   
23180         this.editorcore.pushValue();
23181     }
23182      
23183     
23184     // hide stuff that is not compatible
23185     /**
23186      * @event blur
23187      * @hide
23188      */
23189     /**
23190      * @event change
23191      * @hide
23192      */
23193     /**
23194      * @event focus
23195      * @hide
23196      */
23197     /**
23198      * @event specialkey
23199      * @hide
23200      */
23201     /**
23202      * @cfg {String} fieldClass @hide
23203      */
23204     /**
23205      * @cfg {String} focusClass @hide
23206      */
23207     /**
23208      * @cfg {String} autoCreate @hide
23209      */
23210     /**
23211      * @cfg {String} inputType @hide
23212      */
23213     /**
23214      * @cfg {String} invalidClass @hide
23215      */
23216     /**
23217      * @cfg {String} invalidText @hide
23218      */
23219     /**
23220      * @cfg {String} msgFx @hide
23221      */
23222     /**
23223      * @cfg {String} validateOnBlur @hide
23224      */
23225 });
23226  
23227     
23228    
23229    
23230    
23231       
23232 Roo.namespace('Roo.bootstrap.htmleditor');
23233 /**
23234  * @class Roo.bootstrap.HtmlEditorToolbar1
23235  * Basic Toolbar
23236  * 
23237  * Usage:
23238  *
23239  new Roo.bootstrap.HtmlEditor({
23240     ....
23241     toolbars : [
23242         new Roo.bootstrap.HtmlEditorToolbar1({
23243             disable : { fonts: 1 , format: 1, ..., ... , ...],
23244             btns : [ .... ]
23245         })
23246     }
23247      
23248  * 
23249  * @cfg {Object} disable List of elements to disable..
23250  * @cfg {Array} btns List of additional buttons.
23251  * 
23252  * 
23253  * NEEDS Extra CSS? 
23254  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23255  */
23256  
23257 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23258 {
23259     
23260     Roo.apply(this, config);
23261     
23262     // default disabled, based on 'good practice'..
23263     this.disable = this.disable || {};
23264     Roo.applyIf(this.disable, {
23265         fontSize : true,
23266         colors : true,
23267         specialElements : true
23268     });
23269     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23270     
23271     this.editor = config.editor;
23272     this.editorcore = config.editor.editorcore;
23273     
23274     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23275     
23276     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23277     // dont call parent... till later.
23278 }
23279 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23280      
23281     bar : true,
23282     
23283     editor : false,
23284     editorcore : false,
23285     
23286     
23287     formats : [
23288         "p" ,  
23289         "h1","h2","h3","h4","h5","h6", 
23290         "pre", "code", 
23291         "abbr", "acronym", "address", "cite", "samp", "var",
23292         'div','span'
23293     ],
23294     
23295     onRender : function(ct, position)
23296     {
23297        // Roo.log("Call onRender: " + this.xtype);
23298         
23299        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23300        Roo.log(this.el);
23301        this.el.dom.style.marginBottom = '0';
23302        var _this = this;
23303        var editorcore = this.editorcore;
23304        var editor= this.editor;
23305        
23306        var children = [];
23307        var btn = function(id,cmd , toggle, handler, html){
23308        
23309             var  event = toggle ? 'toggle' : 'click';
23310        
23311             var a = {
23312                 size : 'sm',
23313                 xtype: 'Button',
23314                 xns: Roo.bootstrap,
23315                 glyphicon : id,
23316                 cmd : id || cmd,
23317                 enableToggle:toggle !== false,
23318                 html : html || '',
23319                 pressed : toggle ? false : null,
23320                 listeners : {}
23321             };
23322             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23323                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23324             };
23325             children.push(a);
23326             return a;
23327        }
23328        
23329     //    var cb_box = function...
23330         
23331         var style = {
23332                 xtype: 'Button',
23333                 size : 'sm',
23334                 xns: Roo.bootstrap,
23335                 glyphicon : 'font',
23336                 //html : 'submit'
23337                 menu : {
23338                     xtype: 'Menu',
23339                     xns: Roo.bootstrap,
23340                     items:  []
23341                 }
23342         };
23343         Roo.each(this.formats, function(f) {
23344             style.menu.items.push({
23345                 xtype :'MenuItem',
23346                 xns: Roo.bootstrap,
23347                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23348                 tagname : f,
23349                 listeners : {
23350                     click : function()
23351                     {
23352                         editorcore.insertTag(this.tagname);
23353                         editor.focus();
23354                     }
23355                 }
23356                 
23357             });
23358         });
23359         children.push(style);   
23360         
23361         btn('bold',false,true);
23362         btn('italic',false,true);
23363         btn('align-left', 'justifyleft',true);
23364         btn('align-center', 'justifycenter',true);
23365         btn('align-right' , 'justifyright',true);
23366         btn('link', false, false, function(btn) {
23367             //Roo.log("create link?");
23368             var url = prompt(this.createLinkText, this.defaultLinkValue);
23369             if(url && url != 'http:/'+'/'){
23370                 this.editorcore.relayCmd('createlink', url);
23371             }
23372         }),
23373         btn('list','insertunorderedlist',true);
23374         btn('pencil', false,true, function(btn){
23375                 Roo.log(this);
23376                 this.toggleSourceEdit(btn.pressed);
23377         });
23378         
23379         if (this.editor.btns.length > 0) {
23380             for (var i = 0; i<this.editor.btns.length; i++) {
23381                 children.push(this.editor.btns[i]);
23382             }
23383         }
23384         
23385         /*
23386         var cog = {
23387                 xtype: 'Button',
23388                 size : 'sm',
23389                 xns: Roo.bootstrap,
23390                 glyphicon : 'cog',
23391                 //html : 'submit'
23392                 menu : {
23393                     xtype: 'Menu',
23394                     xns: Roo.bootstrap,
23395                     items:  []
23396                 }
23397         };
23398         
23399         cog.menu.items.push({
23400             xtype :'MenuItem',
23401             xns: Roo.bootstrap,
23402             html : Clean styles,
23403             tagname : f,
23404             listeners : {
23405                 click : function()
23406                 {
23407                     editorcore.insertTag(this.tagname);
23408                     editor.focus();
23409                 }
23410             }
23411             
23412         });
23413        */
23414         
23415          
23416        this.xtype = 'NavSimplebar';
23417         
23418         for(var i=0;i< children.length;i++) {
23419             
23420             this.buttons.add(this.addxtypeChild(children[i]));
23421             
23422         }
23423         
23424         editor.on('editorevent', this.updateToolbar, this);
23425     },
23426     onBtnClick : function(id)
23427     {
23428        this.editorcore.relayCmd(id);
23429        this.editorcore.focus();
23430     },
23431     
23432     /**
23433      * Protected method that will not generally be called directly. It triggers
23434      * a toolbar update by reading the markup state of the current selection in the editor.
23435      */
23436     updateToolbar: function(){
23437
23438         if(!this.editorcore.activated){
23439             this.editor.onFirstFocus(); // is this neeed?
23440             return;
23441         }
23442
23443         var btns = this.buttons; 
23444         var doc = this.editorcore.doc;
23445         btns.get('bold').setActive(doc.queryCommandState('bold'));
23446         btns.get('italic').setActive(doc.queryCommandState('italic'));
23447         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23448         
23449         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23450         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23451         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23452         
23453         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23454         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23455          /*
23456         
23457         var ans = this.editorcore.getAllAncestors();
23458         if (this.formatCombo) {
23459             
23460             
23461             var store = this.formatCombo.store;
23462             this.formatCombo.setValue("");
23463             for (var i =0; i < ans.length;i++) {
23464                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23465                     // select it..
23466                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23467                     break;
23468                 }
23469             }
23470         }
23471         
23472         
23473         
23474         // hides menus... - so this cant be on a menu...
23475         Roo.bootstrap.MenuMgr.hideAll();
23476         */
23477         Roo.bootstrap.MenuMgr.hideAll();
23478         //this.editorsyncValue();
23479     },
23480     onFirstFocus: function() {
23481         this.buttons.each(function(item){
23482            item.enable();
23483         });
23484     },
23485     toggleSourceEdit : function(sourceEditMode){
23486         
23487           
23488         if(sourceEditMode){
23489             Roo.log("disabling buttons");
23490            this.buttons.each( function(item){
23491                 if(item.cmd != 'pencil'){
23492                     item.disable();
23493                 }
23494             });
23495           
23496         }else{
23497             Roo.log("enabling buttons");
23498             if(this.editorcore.initialized){
23499                 this.buttons.each( function(item){
23500                     item.enable();
23501                 });
23502             }
23503             
23504         }
23505         Roo.log("calling toggole on editor");
23506         // tell the editor that it's been pressed..
23507         this.editor.toggleSourceEdit(sourceEditMode);
23508        
23509     }
23510 });
23511
23512
23513
23514
23515
23516 /**
23517  * @class Roo.bootstrap.Table.AbstractSelectionModel
23518  * @extends Roo.util.Observable
23519  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23520  * implemented by descendant classes.  This class should not be directly instantiated.
23521  * @constructor
23522  */
23523 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23524     this.locked = false;
23525     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23526 };
23527
23528
23529 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23530     /** @ignore Called by the grid automatically. Do not call directly. */
23531     init : function(grid){
23532         this.grid = grid;
23533         this.initEvents();
23534     },
23535
23536     /**
23537      * Locks the selections.
23538      */
23539     lock : function(){
23540         this.locked = true;
23541     },
23542
23543     /**
23544      * Unlocks the selections.
23545      */
23546     unlock : function(){
23547         this.locked = false;
23548     },
23549
23550     /**
23551      * Returns true if the selections are locked.
23552      * @return {Boolean}
23553      */
23554     isLocked : function(){
23555         return this.locked;
23556     }
23557 });
23558 /**
23559  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23560  * @class Roo.bootstrap.Table.RowSelectionModel
23561  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23562  * It supports multiple selections and keyboard selection/navigation. 
23563  * @constructor
23564  * @param {Object} config
23565  */
23566
23567 Roo.bootstrap.Table.RowSelectionModel = function(config){
23568     Roo.apply(this, config);
23569     this.selections = new Roo.util.MixedCollection(false, function(o){
23570         return o.id;
23571     });
23572
23573     this.last = false;
23574     this.lastActive = false;
23575
23576     this.addEvents({
23577         /**
23578              * @event selectionchange
23579              * Fires when the selection changes
23580              * @param {SelectionModel} this
23581              */
23582             "selectionchange" : true,
23583         /**
23584              * @event afterselectionchange
23585              * Fires after the selection changes (eg. by key press or clicking)
23586              * @param {SelectionModel} this
23587              */
23588             "afterselectionchange" : true,
23589         /**
23590              * @event beforerowselect
23591              * Fires when a row is selected being selected, return false to cancel.
23592              * @param {SelectionModel} this
23593              * @param {Number} rowIndex The selected index
23594              * @param {Boolean} keepExisting False if other selections will be cleared
23595              */
23596             "beforerowselect" : true,
23597         /**
23598              * @event rowselect
23599              * Fires when a row is selected.
23600              * @param {SelectionModel} this
23601              * @param {Number} rowIndex The selected index
23602              * @param {Roo.data.Record} r The record
23603              */
23604             "rowselect" : true,
23605         /**
23606              * @event rowdeselect
23607              * Fires when a row is deselected.
23608              * @param {SelectionModel} this
23609              * @param {Number} rowIndex The selected index
23610              */
23611         "rowdeselect" : true
23612     });
23613     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23614     this.locked = false;
23615  };
23616
23617 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23618     /**
23619      * @cfg {Boolean} singleSelect
23620      * True to allow selection of only one row at a time (defaults to false)
23621      */
23622     singleSelect : false,
23623
23624     // private
23625     initEvents : function()
23626     {
23627
23628         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23629         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23630         //}else{ // allow click to work like normal
23631          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23632         //}
23633         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23634         this.grid.on("rowclick", this.handleMouseDown, this);
23635         
23636         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23637             "up" : function(e){
23638                 if(!e.shiftKey){
23639                     this.selectPrevious(e.shiftKey);
23640                 }else if(this.last !== false && this.lastActive !== false){
23641                     var last = this.last;
23642                     this.selectRange(this.last,  this.lastActive-1);
23643                     this.grid.getView().focusRow(this.lastActive);
23644                     if(last !== false){
23645                         this.last = last;
23646                     }
23647                 }else{
23648                     this.selectFirstRow();
23649                 }
23650                 this.fireEvent("afterselectionchange", this);
23651             },
23652             "down" : function(e){
23653                 if(!e.shiftKey){
23654                     this.selectNext(e.shiftKey);
23655                 }else if(this.last !== false && this.lastActive !== false){
23656                     var last = this.last;
23657                     this.selectRange(this.last,  this.lastActive+1);
23658                     this.grid.getView().focusRow(this.lastActive);
23659                     if(last !== false){
23660                         this.last = last;
23661                     }
23662                 }else{
23663                     this.selectFirstRow();
23664                 }
23665                 this.fireEvent("afterselectionchange", this);
23666             },
23667             scope: this
23668         });
23669         this.grid.store.on('load', function(){
23670             this.selections.clear();
23671         },this);
23672         /*
23673         var view = this.grid.view;
23674         view.on("refresh", this.onRefresh, this);
23675         view.on("rowupdated", this.onRowUpdated, this);
23676         view.on("rowremoved", this.onRemove, this);
23677         */
23678     },
23679
23680     // private
23681     onRefresh : function()
23682     {
23683         var ds = this.grid.store, i, v = this.grid.view;
23684         var s = this.selections;
23685         s.each(function(r){
23686             if((i = ds.indexOfId(r.id)) != -1){
23687                 v.onRowSelect(i);
23688             }else{
23689                 s.remove(r);
23690             }
23691         });
23692     },
23693
23694     // private
23695     onRemove : function(v, index, r){
23696         this.selections.remove(r);
23697     },
23698
23699     // private
23700     onRowUpdated : function(v, index, r){
23701         if(this.isSelected(r)){
23702             v.onRowSelect(index);
23703         }
23704     },
23705
23706     /**
23707      * Select records.
23708      * @param {Array} records The records to select
23709      * @param {Boolean} keepExisting (optional) True to keep existing selections
23710      */
23711     selectRecords : function(records, keepExisting)
23712     {
23713         if(!keepExisting){
23714             this.clearSelections();
23715         }
23716             var ds = this.grid.store;
23717         for(var i = 0, len = records.length; i < len; i++){
23718             this.selectRow(ds.indexOf(records[i]), true);
23719         }
23720     },
23721
23722     /**
23723      * Gets the number of selected rows.
23724      * @return {Number}
23725      */
23726     getCount : function(){
23727         return this.selections.length;
23728     },
23729
23730     /**
23731      * Selects the first row in the grid.
23732      */
23733     selectFirstRow : function(){
23734         this.selectRow(0);
23735     },
23736
23737     /**
23738      * Select the last row.
23739      * @param {Boolean} keepExisting (optional) True to keep existing selections
23740      */
23741     selectLastRow : function(keepExisting){
23742         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23743         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23744     },
23745
23746     /**
23747      * Selects the row immediately following the last selected row.
23748      * @param {Boolean} keepExisting (optional) True to keep existing selections
23749      */
23750     selectNext : function(keepExisting)
23751     {
23752             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23753             this.selectRow(this.last+1, keepExisting);
23754             this.grid.getView().focusRow(this.last);
23755         }
23756     },
23757
23758     /**
23759      * Selects the row that precedes the last selected row.
23760      * @param {Boolean} keepExisting (optional) True to keep existing selections
23761      */
23762     selectPrevious : function(keepExisting){
23763         if(this.last){
23764             this.selectRow(this.last-1, keepExisting);
23765             this.grid.getView().focusRow(this.last);
23766         }
23767     },
23768
23769     /**
23770      * Returns the selected records
23771      * @return {Array} Array of selected records
23772      */
23773     getSelections : function(){
23774         return [].concat(this.selections.items);
23775     },
23776
23777     /**
23778      * Returns the first selected record.
23779      * @return {Record}
23780      */
23781     getSelected : function(){
23782         return this.selections.itemAt(0);
23783     },
23784
23785
23786     /**
23787      * Clears all selections.
23788      */
23789     clearSelections : function(fast)
23790     {
23791         if(this.locked) {
23792             return;
23793         }
23794         if(fast !== true){
23795                 var ds = this.grid.store;
23796             var s = this.selections;
23797             s.each(function(r){
23798                 this.deselectRow(ds.indexOfId(r.id));
23799             }, this);
23800             s.clear();
23801         }else{
23802             this.selections.clear();
23803         }
23804         this.last = false;
23805     },
23806
23807
23808     /**
23809      * Selects all rows.
23810      */
23811     selectAll : function(){
23812         if(this.locked) {
23813             return;
23814         }
23815         this.selections.clear();
23816         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23817             this.selectRow(i, true);
23818         }
23819     },
23820
23821     /**
23822      * Returns True if there is a selection.
23823      * @return {Boolean}
23824      */
23825     hasSelection : function(){
23826         return this.selections.length > 0;
23827     },
23828
23829     /**
23830      * Returns True if the specified row is selected.
23831      * @param {Number/Record} record The record or index of the record to check
23832      * @return {Boolean}
23833      */
23834     isSelected : function(index){
23835             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23836         return (r && this.selections.key(r.id) ? true : false);
23837     },
23838
23839     /**
23840      * Returns True if the specified record id is selected.
23841      * @param {String} id The id of record to check
23842      * @return {Boolean}
23843      */
23844     isIdSelected : function(id){
23845         return (this.selections.key(id) ? true : false);
23846     },
23847
23848
23849     // private
23850     handleMouseDBClick : function(e, t){
23851         
23852     },
23853     // private
23854     handleMouseDown : function(e, t)
23855     {
23856             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23857         if(this.isLocked() || rowIndex < 0 ){
23858             return;
23859         };
23860         if(e.shiftKey && this.last !== false){
23861             var last = this.last;
23862             this.selectRange(last, rowIndex, e.ctrlKey);
23863             this.last = last; // reset the last
23864             t.focus();
23865     
23866         }else{
23867             var isSelected = this.isSelected(rowIndex);
23868             //Roo.log("select row:" + rowIndex);
23869             if(isSelected){
23870                 this.deselectRow(rowIndex);
23871             } else {
23872                         this.selectRow(rowIndex, true);
23873             }
23874     
23875             /*
23876                 if(e.button !== 0 && isSelected){
23877                 alert('rowIndex 2: ' + rowIndex);
23878                     view.focusRow(rowIndex);
23879                 }else if(e.ctrlKey && isSelected){
23880                     this.deselectRow(rowIndex);
23881                 }else if(!isSelected){
23882                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23883                     view.focusRow(rowIndex);
23884                 }
23885             */
23886         }
23887         this.fireEvent("afterselectionchange", this);
23888     },
23889     // private
23890     handleDragableRowClick :  function(grid, rowIndex, e) 
23891     {
23892         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23893             this.selectRow(rowIndex, false);
23894             grid.view.focusRow(rowIndex);
23895              this.fireEvent("afterselectionchange", this);
23896         }
23897     },
23898     
23899     /**
23900      * Selects multiple rows.
23901      * @param {Array} rows Array of the indexes of the row to select
23902      * @param {Boolean} keepExisting (optional) True to keep existing selections
23903      */
23904     selectRows : function(rows, keepExisting){
23905         if(!keepExisting){
23906             this.clearSelections();
23907         }
23908         for(var i = 0, len = rows.length; i < len; i++){
23909             this.selectRow(rows[i], true);
23910         }
23911     },
23912
23913     /**
23914      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23915      * @param {Number} startRow The index of the first row in the range
23916      * @param {Number} endRow The index of the last row in the range
23917      * @param {Boolean} keepExisting (optional) True to retain existing selections
23918      */
23919     selectRange : function(startRow, endRow, keepExisting){
23920         if(this.locked) {
23921             return;
23922         }
23923         if(!keepExisting){
23924             this.clearSelections();
23925         }
23926         if(startRow <= endRow){
23927             for(var i = startRow; i <= endRow; i++){
23928                 this.selectRow(i, true);
23929             }
23930         }else{
23931             for(var i = startRow; i >= endRow; i--){
23932                 this.selectRow(i, true);
23933             }
23934         }
23935     },
23936
23937     /**
23938      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23939      * @param {Number} startRow The index of the first row in the range
23940      * @param {Number} endRow The index of the last row in the range
23941      */
23942     deselectRange : function(startRow, endRow, preventViewNotify){
23943         if(this.locked) {
23944             return;
23945         }
23946         for(var i = startRow; i <= endRow; i++){
23947             this.deselectRow(i, preventViewNotify);
23948         }
23949     },
23950
23951     /**
23952      * Selects a row.
23953      * @param {Number} row The index of the row to select
23954      * @param {Boolean} keepExisting (optional) True to keep existing selections
23955      */
23956     selectRow : function(index, keepExisting, preventViewNotify)
23957     {
23958             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23959             return;
23960         }
23961         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23962             if(!keepExisting || this.singleSelect){
23963                 this.clearSelections();
23964             }
23965             
23966             var r = this.grid.store.getAt(index);
23967             //console.log('selectRow - record id :' + r.id);
23968             
23969             this.selections.add(r);
23970             this.last = this.lastActive = index;
23971             if(!preventViewNotify){
23972                 var proxy = new Roo.Element(
23973                                 this.grid.getRowDom(index)
23974                 );
23975                 proxy.addClass('bg-info info');
23976             }
23977             this.fireEvent("rowselect", this, index, r);
23978             this.fireEvent("selectionchange", this);
23979         }
23980     },
23981
23982     /**
23983      * Deselects a row.
23984      * @param {Number} row The index of the row to deselect
23985      */
23986     deselectRow : function(index, preventViewNotify)
23987     {
23988         if(this.locked) {
23989             return;
23990         }
23991         if(this.last == index){
23992             this.last = false;
23993         }
23994         if(this.lastActive == index){
23995             this.lastActive = false;
23996         }
23997         
23998         var r = this.grid.store.getAt(index);
23999         if (!r) {
24000             return;
24001         }
24002         
24003         this.selections.remove(r);
24004         //.console.log('deselectRow - record id :' + r.id);
24005         if(!preventViewNotify){
24006         
24007             var proxy = new Roo.Element(
24008                 this.grid.getRowDom(index)
24009             );
24010             proxy.removeClass('bg-info info');
24011         }
24012         this.fireEvent("rowdeselect", this, index);
24013         this.fireEvent("selectionchange", this);
24014     },
24015
24016     // private
24017     restoreLast : function(){
24018         if(this._last){
24019             this.last = this._last;
24020         }
24021     },
24022
24023     // private
24024     acceptsNav : function(row, col, cm){
24025         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24026     },
24027
24028     // private
24029     onEditorKey : function(field, e){
24030         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24031         if(k == e.TAB){
24032             e.stopEvent();
24033             ed.completeEdit();
24034             if(e.shiftKey){
24035                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24036             }else{
24037                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24038             }
24039         }else if(k == e.ENTER && !e.ctrlKey){
24040             e.stopEvent();
24041             ed.completeEdit();
24042             if(e.shiftKey){
24043                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24044             }else{
24045                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24046             }
24047         }else if(k == e.ESC){
24048             ed.cancelEdit();
24049         }
24050         if(newCell){
24051             g.startEditing(newCell[0], newCell[1]);
24052         }
24053     }
24054 });
24055 /*
24056  * Based on:
24057  * Ext JS Library 1.1.1
24058  * Copyright(c) 2006-2007, Ext JS, LLC.
24059  *
24060  * Originally Released Under LGPL - original licence link has changed is not relivant.
24061  *
24062  * Fork - LGPL
24063  * <script type="text/javascript">
24064  */
24065  
24066 /**
24067  * @class Roo.bootstrap.PagingToolbar
24068  * @extends Roo.bootstrap.NavSimplebar
24069  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24070  * @constructor
24071  * Create a new PagingToolbar
24072  * @param {Object} config The config object
24073  * @param {Roo.data.Store} store
24074  */
24075 Roo.bootstrap.PagingToolbar = function(config)
24076 {
24077     // old args format still supported... - xtype is prefered..
24078         // created from xtype...
24079     
24080     this.ds = config.dataSource;
24081     
24082     if (config.store && !this.ds) {
24083         this.store= Roo.factory(config.store, Roo.data);
24084         this.ds = this.store;
24085         this.ds.xmodule = this.xmodule || false;
24086     }
24087     
24088     this.toolbarItems = [];
24089     if (config.items) {
24090         this.toolbarItems = config.items;
24091     }
24092     
24093     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24094     
24095     this.cursor = 0;
24096     
24097     if (this.ds) { 
24098         this.bind(this.ds);
24099     }
24100     
24101     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24102     
24103 };
24104
24105 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24106     /**
24107      * @cfg {Roo.data.Store} dataSource
24108      * The underlying data store providing the paged data
24109      */
24110     /**
24111      * @cfg {String/HTMLElement/Element} container
24112      * container The id or element that will contain the toolbar
24113      */
24114     /**
24115      * @cfg {Boolean} displayInfo
24116      * True to display the displayMsg (defaults to false)
24117      */
24118     /**
24119      * @cfg {Number} pageSize
24120      * The number of records to display per page (defaults to 20)
24121      */
24122     pageSize: 20,
24123     /**
24124      * @cfg {String} displayMsg
24125      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24126      */
24127     displayMsg : 'Displaying {0} - {1} of {2}',
24128     /**
24129      * @cfg {String} emptyMsg
24130      * The message to display when no records are found (defaults to "No data to display")
24131      */
24132     emptyMsg : 'No data to display',
24133     /**
24134      * Customizable piece of the default paging text (defaults to "Page")
24135      * @type String
24136      */
24137     beforePageText : "Page",
24138     /**
24139      * Customizable piece of the default paging text (defaults to "of %0")
24140      * @type String
24141      */
24142     afterPageText : "of {0}",
24143     /**
24144      * Customizable piece of the default paging text (defaults to "First Page")
24145      * @type String
24146      */
24147     firstText : "First Page",
24148     /**
24149      * Customizable piece of the default paging text (defaults to "Previous Page")
24150      * @type String
24151      */
24152     prevText : "Previous Page",
24153     /**
24154      * Customizable piece of the default paging text (defaults to "Next Page")
24155      * @type String
24156      */
24157     nextText : "Next Page",
24158     /**
24159      * Customizable piece of the default paging text (defaults to "Last Page")
24160      * @type String
24161      */
24162     lastText : "Last Page",
24163     /**
24164      * Customizable piece of the default paging text (defaults to "Refresh")
24165      * @type String
24166      */
24167     refreshText : "Refresh",
24168
24169     buttons : false,
24170     // private
24171     onRender : function(ct, position) 
24172     {
24173         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24174         this.navgroup.parentId = this.id;
24175         this.navgroup.onRender(this.el, null);
24176         // add the buttons to the navgroup
24177         
24178         if(this.displayInfo){
24179             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24180             this.displayEl = this.el.select('.x-paging-info', true).first();
24181 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24182 //            this.displayEl = navel.el.select('span',true).first();
24183         }
24184         
24185         var _this = this;
24186         
24187         if(this.buttons){
24188             Roo.each(_this.buttons, function(e){ // this might need to use render????
24189                Roo.factory(e).onRender(_this.el, null);
24190             });
24191         }
24192             
24193         Roo.each(_this.toolbarItems, function(e) {
24194             _this.navgroup.addItem(e);
24195         });
24196         
24197         
24198         this.first = this.navgroup.addItem({
24199             tooltip: this.firstText,
24200             cls: "prev",
24201             icon : 'fa fa-backward',
24202             disabled: true,
24203             preventDefault: true,
24204             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24205         });
24206         
24207         this.prev =  this.navgroup.addItem({
24208             tooltip: this.prevText,
24209             cls: "prev",
24210             icon : 'fa fa-step-backward',
24211             disabled: true,
24212             preventDefault: true,
24213             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24214         });
24215     //this.addSeparator();
24216         
24217         
24218         var field = this.navgroup.addItem( {
24219             tagtype : 'span',
24220             cls : 'x-paging-position',
24221             
24222             html : this.beforePageText  +
24223                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24224                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24225          } ); //?? escaped?
24226         
24227         this.field = field.el.select('input', true).first();
24228         this.field.on("keydown", this.onPagingKeydown, this);
24229         this.field.on("focus", function(){this.dom.select();});
24230     
24231     
24232         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24233         //this.field.setHeight(18);
24234         //this.addSeparator();
24235         this.next = this.navgroup.addItem({
24236             tooltip: this.nextText,
24237             cls: "next",
24238             html : ' <i class="fa fa-step-forward">',
24239             disabled: true,
24240             preventDefault: true,
24241             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24242         });
24243         this.last = this.navgroup.addItem({
24244             tooltip: this.lastText,
24245             icon : 'fa fa-forward',
24246             cls: "next",
24247             disabled: true,
24248             preventDefault: true,
24249             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24250         });
24251     //this.addSeparator();
24252         this.loading = this.navgroup.addItem({
24253             tooltip: this.refreshText,
24254             icon: 'fa fa-refresh',
24255             preventDefault: true,
24256             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24257         });
24258         
24259     },
24260
24261     // private
24262     updateInfo : function(){
24263         if(this.displayEl){
24264             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24265             var msg = count == 0 ?
24266                 this.emptyMsg :
24267                 String.format(
24268                     this.displayMsg,
24269                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24270                 );
24271             this.displayEl.update(msg);
24272         }
24273     },
24274
24275     // private
24276     onLoad : function(ds, r, o)
24277     {
24278         this.cursor = o.params ? o.params.start : 0;
24279         var d = this.getPageData(),
24280             ap = d.activePage,
24281             ps = d.pages;
24282         
24283         
24284         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24285         this.field.dom.value = ap;
24286         this.first.setDisabled(ap == 1);
24287         this.prev.setDisabled(ap == 1);
24288         this.next.setDisabled(ap == ps);
24289         this.last.setDisabled(ap == ps);
24290         this.loading.enable();
24291         this.updateInfo();
24292     },
24293
24294     // private
24295     getPageData : function(){
24296         var total = this.ds.getTotalCount();
24297         return {
24298             total : total,
24299             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24300             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24301         };
24302     },
24303
24304     // private
24305     onLoadError : function(){
24306         this.loading.enable();
24307     },
24308
24309     // private
24310     onPagingKeydown : function(e){
24311         var k = e.getKey();
24312         var d = this.getPageData();
24313         if(k == e.RETURN){
24314             var v = this.field.dom.value, pageNum;
24315             if(!v || isNaN(pageNum = parseInt(v, 10))){
24316                 this.field.dom.value = d.activePage;
24317                 return;
24318             }
24319             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24320             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24321             e.stopEvent();
24322         }
24323         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))
24324         {
24325           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24326           this.field.dom.value = pageNum;
24327           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24328           e.stopEvent();
24329         }
24330         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24331         {
24332           var v = this.field.dom.value, pageNum; 
24333           var increment = (e.shiftKey) ? 10 : 1;
24334           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24335                 increment *= -1;
24336           }
24337           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24338             this.field.dom.value = d.activePage;
24339             return;
24340           }
24341           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24342           {
24343             this.field.dom.value = parseInt(v, 10) + increment;
24344             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24345             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24346           }
24347           e.stopEvent();
24348         }
24349     },
24350
24351     // private
24352     beforeLoad : function(){
24353         if(this.loading){
24354             this.loading.disable();
24355         }
24356     },
24357
24358     // private
24359     onClick : function(which){
24360         
24361         var ds = this.ds;
24362         if (!ds) {
24363             return;
24364         }
24365         
24366         switch(which){
24367             case "first":
24368                 ds.load({params:{start: 0, limit: this.pageSize}});
24369             break;
24370             case "prev":
24371                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24372             break;
24373             case "next":
24374                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24375             break;
24376             case "last":
24377                 var total = ds.getTotalCount();
24378                 var extra = total % this.pageSize;
24379                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24380                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24381             break;
24382             case "refresh":
24383                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24384             break;
24385         }
24386     },
24387
24388     /**
24389      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24390      * @param {Roo.data.Store} store The data store to unbind
24391      */
24392     unbind : function(ds){
24393         ds.un("beforeload", this.beforeLoad, this);
24394         ds.un("load", this.onLoad, this);
24395         ds.un("loadexception", this.onLoadError, this);
24396         ds.un("remove", this.updateInfo, this);
24397         ds.un("add", this.updateInfo, this);
24398         this.ds = undefined;
24399     },
24400
24401     /**
24402      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24403      * @param {Roo.data.Store} store The data store to bind
24404      */
24405     bind : function(ds){
24406         ds.on("beforeload", this.beforeLoad, this);
24407         ds.on("load", this.onLoad, this);
24408         ds.on("loadexception", this.onLoadError, this);
24409         ds.on("remove", this.updateInfo, this);
24410         ds.on("add", this.updateInfo, this);
24411         this.ds = ds;
24412     }
24413 });/*
24414  * - LGPL
24415  *
24416  * element
24417  * 
24418  */
24419
24420 /**
24421  * @class Roo.bootstrap.MessageBar
24422  * @extends Roo.bootstrap.Component
24423  * Bootstrap MessageBar class
24424  * @cfg {String} html contents of the MessageBar
24425  * @cfg {String} weight (info | success | warning | danger) default info
24426  * @cfg {String} beforeClass insert the bar before the given class
24427  * @cfg {Boolean} closable (true | false) default false
24428  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24429  * 
24430  * @constructor
24431  * Create a new Element
24432  * @param {Object} config The config object
24433  */
24434
24435 Roo.bootstrap.MessageBar = function(config){
24436     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24437 };
24438
24439 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24440     
24441     html: '',
24442     weight: 'info',
24443     closable: false,
24444     fixed: false,
24445     beforeClass: 'bootstrap-sticky-wrap',
24446     
24447     getAutoCreate : function(){
24448         
24449         var cfg = {
24450             tag: 'div',
24451             cls: 'alert alert-dismissable alert-' + this.weight,
24452             cn: [
24453                 {
24454                     tag: 'span',
24455                     cls: 'message',
24456                     html: this.html || ''
24457                 }
24458             ]
24459         };
24460         
24461         if(this.fixed){
24462             cfg.cls += ' alert-messages-fixed';
24463         }
24464         
24465         if(this.closable){
24466             cfg.cn.push({
24467                 tag: 'button',
24468                 cls: 'close',
24469                 html: 'x'
24470             });
24471         }
24472         
24473         return cfg;
24474     },
24475     
24476     onRender : function(ct, position)
24477     {
24478         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24479         
24480         if(!this.el){
24481             var cfg = Roo.apply({},  this.getAutoCreate());
24482             cfg.id = Roo.id();
24483             
24484             if (this.cls) {
24485                 cfg.cls += ' ' + this.cls;
24486             }
24487             if (this.style) {
24488                 cfg.style = this.style;
24489             }
24490             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24491             
24492             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24493         }
24494         
24495         this.el.select('>button.close').on('click', this.hide, this);
24496         
24497     },
24498     
24499     show : function()
24500     {
24501         if (!this.rendered) {
24502             this.render();
24503         }
24504         
24505         this.el.show();
24506         
24507         this.fireEvent('show', this);
24508         
24509     },
24510     
24511     hide : function()
24512     {
24513         if (!this.rendered) {
24514             this.render();
24515         }
24516         
24517         this.el.hide();
24518         
24519         this.fireEvent('hide', this);
24520     },
24521     
24522     update : function()
24523     {
24524 //        var e = this.el.dom.firstChild;
24525 //        
24526 //        if(this.closable){
24527 //            e = e.nextSibling;
24528 //        }
24529 //        
24530 //        e.data = this.html || '';
24531
24532         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24533     }
24534    
24535 });
24536
24537  
24538
24539      /*
24540  * - LGPL
24541  *
24542  * Graph
24543  * 
24544  */
24545
24546
24547 /**
24548  * @class Roo.bootstrap.Graph
24549  * @extends Roo.bootstrap.Component
24550  * Bootstrap Graph class
24551 > Prameters
24552  -sm {number} sm 4
24553  -md {number} md 5
24554  @cfg {String} graphtype  bar | vbar | pie
24555  @cfg {number} g_x coodinator | centre x (pie)
24556  @cfg {number} g_y coodinator | centre y (pie)
24557  @cfg {number} g_r radius (pie)
24558  @cfg {number} g_height height of the chart (respected by all elements in the set)
24559  @cfg {number} g_width width of the chart (respected by all elements in the set)
24560  @cfg {Object} title The title of the chart
24561     
24562  -{Array}  values
24563  -opts (object) options for the chart 
24564      o {
24565      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24566      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24567      o vgutter (number)
24568      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.
24569      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24570      o to
24571      o stretch (boolean)
24572      o }
24573  -opts (object) options for the pie
24574      o{
24575      o cut
24576      o startAngle (number)
24577      o endAngle (number)
24578      } 
24579  *
24580  * @constructor
24581  * Create a new Input
24582  * @param {Object} config The config object
24583  */
24584
24585 Roo.bootstrap.Graph = function(config){
24586     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24587     
24588     this.addEvents({
24589         // img events
24590         /**
24591          * @event click
24592          * The img click event for the img.
24593          * @param {Roo.EventObject} e
24594          */
24595         "click" : true
24596     });
24597 };
24598
24599 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24600     
24601     sm: 4,
24602     md: 5,
24603     graphtype: 'bar',
24604     g_height: 250,
24605     g_width: 400,
24606     g_x: 50,
24607     g_y: 50,
24608     g_r: 30,
24609     opts:{
24610         //g_colors: this.colors,
24611         g_type: 'soft',
24612         g_gutter: '20%'
24613
24614     },
24615     title : false,
24616
24617     getAutoCreate : function(){
24618         
24619         var cfg = {
24620             tag: 'div',
24621             html : null
24622         };
24623         
24624         
24625         return  cfg;
24626     },
24627
24628     onRender : function(ct,position){
24629         
24630         
24631         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24632         
24633         if (typeof(Raphael) == 'undefined') {
24634             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24635             return;
24636         }
24637         
24638         this.raphael = Raphael(this.el.dom);
24639         
24640                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24641                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24642                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24643                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24644                 /*
24645                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24646                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24647                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24648                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24649                 
24650                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24651                 r.barchart(330, 10, 300, 220, data1);
24652                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24653                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24654                 */
24655                 
24656                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24657                 // r.barchart(30, 30, 560, 250,  xdata, {
24658                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24659                 //     axis : "0 0 1 1",
24660                 //     axisxlabels :  xdata
24661                 //     //yvalues : cols,
24662                    
24663                 // });
24664 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24665 //        
24666 //        this.load(null,xdata,{
24667 //                axis : "0 0 1 1",
24668 //                axisxlabels :  xdata
24669 //                });
24670
24671     },
24672
24673     load : function(graphtype,xdata,opts)
24674     {
24675         this.raphael.clear();
24676         if(!graphtype) {
24677             graphtype = this.graphtype;
24678         }
24679         if(!opts){
24680             opts = this.opts;
24681         }
24682         var r = this.raphael,
24683             fin = function () {
24684                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24685             },
24686             fout = function () {
24687                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24688             },
24689             pfin = function() {
24690                 this.sector.stop();
24691                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24692
24693                 if (this.label) {
24694                     this.label[0].stop();
24695                     this.label[0].attr({ r: 7.5 });
24696                     this.label[1].attr({ "font-weight": 800 });
24697                 }
24698             },
24699             pfout = function() {
24700                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24701
24702                 if (this.label) {
24703                     this.label[0].animate({ r: 5 }, 500, "bounce");
24704                     this.label[1].attr({ "font-weight": 400 });
24705                 }
24706             };
24707
24708         switch(graphtype){
24709             case 'bar':
24710                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24711                 break;
24712             case 'hbar':
24713                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24714                 break;
24715             case 'pie':
24716 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24717 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24718 //            
24719                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24720                 
24721                 break;
24722
24723         }
24724         
24725         if(this.title){
24726             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24727         }
24728         
24729     },
24730     
24731     setTitle: function(o)
24732     {
24733         this.title = o;
24734     },
24735     
24736     initEvents: function() {
24737         
24738         if(!this.href){
24739             this.el.on('click', this.onClick, this);
24740         }
24741     },
24742     
24743     onClick : function(e)
24744     {
24745         Roo.log('img onclick');
24746         this.fireEvent('click', this, e);
24747     }
24748    
24749 });
24750
24751  
24752 /*
24753  * - LGPL
24754  *
24755  * numberBox
24756  * 
24757  */
24758 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24759
24760 /**
24761  * @class Roo.bootstrap.dash.NumberBox
24762  * @extends Roo.bootstrap.Component
24763  * Bootstrap NumberBox class
24764  * @cfg {String} headline Box headline
24765  * @cfg {String} content Box content
24766  * @cfg {String} icon Box icon
24767  * @cfg {String} footer Footer text
24768  * @cfg {String} fhref Footer href
24769  * 
24770  * @constructor
24771  * Create a new NumberBox
24772  * @param {Object} config The config object
24773  */
24774
24775
24776 Roo.bootstrap.dash.NumberBox = function(config){
24777     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24778     
24779 };
24780
24781 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24782     
24783     headline : '',
24784     content : '',
24785     icon : '',
24786     footer : '',
24787     fhref : '',
24788     ficon : '',
24789     
24790     getAutoCreate : function(){
24791         
24792         var cfg = {
24793             tag : 'div',
24794             cls : 'small-box ',
24795             cn : [
24796                 {
24797                     tag : 'div',
24798                     cls : 'inner',
24799                     cn :[
24800                         {
24801                             tag : 'h3',
24802                             cls : 'roo-headline',
24803                             html : this.headline
24804                         },
24805                         {
24806                             tag : 'p',
24807                             cls : 'roo-content',
24808                             html : this.content
24809                         }
24810                     ]
24811                 }
24812             ]
24813         };
24814         
24815         if(this.icon){
24816             cfg.cn.push({
24817                 tag : 'div',
24818                 cls : 'icon',
24819                 cn :[
24820                     {
24821                         tag : 'i',
24822                         cls : 'ion ' + this.icon
24823                     }
24824                 ]
24825             });
24826         }
24827         
24828         if(this.footer){
24829             var footer = {
24830                 tag : 'a',
24831                 cls : 'small-box-footer',
24832                 href : this.fhref || '#',
24833                 html : this.footer
24834             };
24835             
24836             cfg.cn.push(footer);
24837             
24838         }
24839         
24840         return  cfg;
24841     },
24842
24843     onRender : function(ct,position){
24844         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24845
24846
24847        
24848                 
24849     },
24850
24851     setHeadline: function (value)
24852     {
24853         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24854     },
24855     
24856     setFooter: function (value, href)
24857     {
24858         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24859         
24860         if(href){
24861             this.el.select('a.small-box-footer',true).first().attr('href', href);
24862         }
24863         
24864     },
24865
24866     setContent: function (value)
24867     {
24868         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24869     },
24870
24871     initEvents: function() 
24872     {   
24873         
24874     }
24875     
24876 });
24877
24878  
24879 /*
24880  * - LGPL
24881  *
24882  * TabBox
24883  * 
24884  */
24885 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24886
24887 /**
24888  * @class Roo.bootstrap.dash.TabBox
24889  * @extends Roo.bootstrap.Component
24890  * Bootstrap TabBox class
24891  * @cfg {String} title Title of the TabBox
24892  * @cfg {String} icon Icon of the TabBox
24893  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24894  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24895  * 
24896  * @constructor
24897  * Create a new TabBox
24898  * @param {Object} config The config object
24899  */
24900
24901
24902 Roo.bootstrap.dash.TabBox = function(config){
24903     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24904     this.addEvents({
24905         // raw events
24906         /**
24907          * @event addpane
24908          * When a pane is added
24909          * @param {Roo.bootstrap.dash.TabPane} pane
24910          */
24911         "addpane" : true,
24912         /**
24913          * @event activatepane
24914          * When a pane is activated
24915          * @param {Roo.bootstrap.dash.TabPane} pane
24916          */
24917         "activatepane" : true
24918         
24919          
24920     });
24921     
24922     this.panes = [];
24923 };
24924
24925 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24926
24927     title : '',
24928     icon : false,
24929     showtabs : true,
24930     tabScrollable : false,
24931     
24932     getChildContainer : function()
24933     {
24934         return this.el.select('.tab-content', true).first();
24935     },
24936     
24937     getAutoCreate : function(){
24938         
24939         var header = {
24940             tag: 'li',
24941             cls: 'pull-left header',
24942             html: this.title,
24943             cn : []
24944         };
24945         
24946         if(this.icon){
24947             header.cn.push({
24948                 tag: 'i',
24949                 cls: 'fa ' + this.icon
24950             });
24951         }
24952         
24953         var h = {
24954             tag: 'ul',
24955             cls: 'nav nav-tabs pull-right',
24956             cn: [
24957                 header
24958             ]
24959         };
24960         
24961         if(this.tabScrollable){
24962             h = {
24963                 tag: 'div',
24964                 cls: 'tab-header',
24965                 cn: [
24966                     {
24967                         tag: 'ul',
24968                         cls: 'nav nav-tabs pull-right',
24969                         cn: [
24970                             header
24971                         ]
24972                     }
24973                 ]
24974             };
24975         }
24976         
24977         var cfg = {
24978             tag: 'div',
24979             cls: 'nav-tabs-custom',
24980             cn: [
24981                 h,
24982                 {
24983                     tag: 'div',
24984                     cls: 'tab-content no-padding',
24985                     cn: []
24986                 }
24987             ]
24988         };
24989
24990         return  cfg;
24991     },
24992     initEvents : function()
24993     {
24994         //Roo.log('add add pane handler');
24995         this.on('addpane', this.onAddPane, this);
24996     },
24997      /**
24998      * Updates the box title
24999      * @param {String} html to set the title to.
25000      */
25001     setTitle : function(value)
25002     {
25003         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25004     },
25005     onAddPane : function(pane)
25006     {
25007         this.panes.push(pane);
25008         //Roo.log('addpane');
25009         //Roo.log(pane);
25010         // tabs are rendere left to right..
25011         if(!this.showtabs){
25012             return;
25013         }
25014         
25015         var ctr = this.el.select('.nav-tabs', true).first();
25016          
25017          
25018         var existing = ctr.select('.nav-tab',true);
25019         var qty = existing.getCount();;
25020         
25021         
25022         var tab = ctr.createChild({
25023             tag : 'li',
25024             cls : 'nav-tab' + (qty ? '' : ' active'),
25025             cn : [
25026                 {
25027                     tag : 'a',
25028                     href:'#',
25029                     html : pane.title
25030                 }
25031             ]
25032         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25033         pane.tab = tab;
25034         
25035         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25036         if (!qty) {
25037             pane.el.addClass('active');
25038         }
25039         
25040                 
25041     },
25042     onTabClick : function(ev,un,ob,pane)
25043     {
25044         //Roo.log('tab - prev default');
25045         ev.preventDefault();
25046         
25047         
25048         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25049         pane.tab.addClass('active');
25050         //Roo.log(pane.title);
25051         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25052         // technically we should have a deactivate event.. but maybe add later.
25053         // and it should not de-activate the selected tab...
25054         this.fireEvent('activatepane', pane);
25055         pane.el.addClass('active');
25056         pane.fireEvent('activate');
25057         
25058         
25059     },
25060     
25061     getActivePane : function()
25062     {
25063         var r = false;
25064         Roo.each(this.panes, function(p) {
25065             if(p.el.hasClass('active')){
25066                 r = p;
25067                 return false;
25068             }
25069             
25070             return;
25071         });
25072         
25073         return r;
25074     }
25075     
25076     
25077 });
25078
25079  
25080 /*
25081  * - LGPL
25082  *
25083  * Tab pane
25084  * 
25085  */
25086 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25087 /**
25088  * @class Roo.bootstrap.TabPane
25089  * @extends Roo.bootstrap.Component
25090  * Bootstrap TabPane class
25091  * @cfg {Boolean} active (false | true) Default false
25092  * @cfg {String} title title of panel
25093
25094  * 
25095  * @constructor
25096  * Create a new TabPane
25097  * @param {Object} config The config object
25098  */
25099
25100 Roo.bootstrap.dash.TabPane = function(config){
25101     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25102     
25103     this.addEvents({
25104         // raw events
25105         /**
25106          * @event activate
25107          * When a pane is activated
25108          * @param {Roo.bootstrap.dash.TabPane} pane
25109          */
25110         "activate" : true
25111          
25112     });
25113 };
25114
25115 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25116     
25117     active : false,
25118     title : '',
25119     
25120     // the tabBox that this is attached to.
25121     tab : false,
25122      
25123     getAutoCreate : function() 
25124     {
25125         var cfg = {
25126             tag: 'div',
25127             cls: 'tab-pane'
25128         };
25129         
25130         if(this.active){
25131             cfg.cls += ' active';
25132         }
25133         
25134         return cfg;
25135     },
25136     initEvents  : function()
25137     {
25138         //Roo.log('trigger add pane handler');
25139         this.parent().fireEvent('addpane', this)
25140     },
25141     
25142      /**
25143      * Updates the tab title 
25144      * @param {String} html to set the title to.
25145      */
25146     setTitle: function(str)
25147     {
25148         if (!this.tab) {
25149             return;
25150         }
25151         this.title = str;
25152         this.tab.select('a', true).first().dom.innerHTML = str;
25153         
25154     }
25155     
25156     
25157     
25158 });
25159
25160  
25161
25162
25163  /*
25164  * - LGPL
25165  *
25166  * menu
25167  * 
25168  */
25169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25170
25171 /**
25172  * @class Roo.bootstrap.menu.Menu
25173  * @extends Roo.bootstrap.Component
25174  * Bootstrap Menu class - container for Menu
25175  * @cfg {String} html Text of the menu
25176  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25177  * @cfg {String} icon Font awesome icon
25178  * @cfg {String} pos Menu align to (top | bottom) default bottom
25179  * 
25180  * 
25181  * @constructor
25182  * Create a new Menu
25183  * @param {Object} config The config object
25184  */
25185
25186
25187 Roo.bootstrap.menu.Menu = function(config){
25188     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25189     
25190     this.addEvents({
25191         /**
25192          * @event beforeshow
25193          * Fires before this menu is displayed
25194          * @param {Roo.bootstrap.menu.Menu} this
25195          */
25196         beforeshow : true,
25197         /**
25198          * @event beforehide
25199          * Fires before this menu is hidden
25200          * @param {Roo.bootstrap.menu.Menu} this
25201          */
25202         beforehide : true,
25203         /**
25204          * @event show
25205          * Fires after this menu is displayed
25206          * @param {Roo.bootstrap.menu.Menu} this
25207          */
25208         show : true,
25209         /**
25210          * @event hide
25211          * Fires after this menu is hidden
25212          * @param {Roo.bootstrap.menu.Menu} this
25213          */
25214         hide : true,
25215         /**
25216          * @event click
25217          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25218          * @param {Roo.bootstrap.menu.Menu} this
25219          * @param {Roo.EventObject} e
25220          */
25221         click : true
25222     });
25223     
25224 };
25225
25226 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25227     
25228     submenu : false,
25229     html : '',
25230     weight : 'default',
25231     icon : false,
25232     pos : 'bottom',
25233     
25234     
25235     getChildContainer : function() {
25236         if(this.isSubMenu){
25237             return this.el;
25238         }
25239         
25240         return this.el.select('ul.dropdown-menu', true).first();  
25241     },
25242     
25243     getAutoCreate : function()
25244     {
25245         var text = [
25246             {
25247                 tag : 'span',
25248                 cls : 'roo-menu-text',
25249                 html : this.html
25250             }
25251         ];
25252         
25253         if(this.icon){
25254             text.unshift({
25255                 tag : 'i',
25256                 cls : 'fa ' + this.icon
25257             })
25258         }
25259         
25260         
25261         var cfg = {
25262             tag : 'div',
25263             cls : 'btn-group',
25264             cn : [
25265                 {
25266                     tag : 'button',
25267                     cls : 'dropdown-button btn btn-' + this.weight,
25268                     cn : text
25269                 },
25270                 {
25271                     tag : 'button',
25272                     cls : 'dropdown-toggle btn btn-' + this.weight,
25273                     cn : [
25274                         {
25275                             tag : 'span',
25276                             cls : 'caret'
25277                         }
25278                     ]
25279                 },
25280                 {
25281                     tag : 'ul',
25282                     cls : 'dropdown-menu'
25283                 }
25284             ]
25285             
25286         };
25287         
25288         if(this.pos == 'top'){
25289             cfg.cls += ' dropup';
25290         }
25291         
25292         if(this.isSubMenu){
25293             cfg = {
25294                 tag : 'ul',
25295                 cls : 'dropdown-menu'
25296             }
25297         }
25298         
25299         return cfg;
25300     },
25301     
25302     onRender : function(ct, position)
25303     {
25304         this.isSubMenu = ct.hasClass('dropdown-submenu');
25305         
25306         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25307     },
25308     
25309     initEvents : function() 
25310     {
25311         if(this.isSubMenu){
25312             return;
25313         }
25314         
25315         this.hidden = true;
25316         
25317         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25318         this.triggerEl.on('click', this.onTriggerPress, this);
25319         
25320         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25321         this.buttonEl.on('click', this.onClick, this);
25322         
25323     },
25324     
25325     list : function()
25326     {
25327         if(this.isSubMenu){
25328             return this.el;
25329         }
25330         
25331         return this.el.select('ul.dropdown-menu', true).first();
25332     },
25333     
25334     onClick : function(e)
25335     {
25336         this.fireEvent("click", this, e);
25337     },
25338     
25339     onTriggerPress  : function(e)
25340     {   
25341         if (this.isVisible()) {
25342             this.hide();
25343         } else {
25344             this.show();
25345         }
25346     },
25347     
25348     isVisible : function(){
25349         return !this.hidden;
25350     },
25351     
25352     show : function()
25353     {
25354         this.fireEvent("beforeshow", this);
25355         
25356         this.hidden = false;
25357         this.el.addClass('open');
25358         
25359         Roo.get(document).on("mouseup", this.onMouseUp, this);
25360         
25361         this.fireEvent("show", this);
25362         
25363         
25364     },
25365     
25366     hide : function()
25367     {
25368         this.fireEvent("beforehide", this);
25369         
25370         this.hidden = true;
25371         this.el.removeClass('open');
25372         
25373         Roo.get(document).un("mouseup", this.onMouseUp);
25374         
25375         this.fireEvent("hide", this);
25376     },
25377     
25378     onMouseUp : function()
25379     {
25380         this.hide();
25381     }
25382     
25383 });
25384
25385  
25386  /*
25387  * - LGPL
25388  *
25389  * menu item
25390  * 
25391  */
25392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25393
25394 /**
25395  * @class Roo.bootstrap.menu.Item
25396  * @extends Roo.bootstrap.Component
25397  * Bootstrap MenuItem class
25398  * @cfg {Boolean} submenu (true | false) default false
25399  * @cfg {String} html text of the item
25400  * @cfg {String} href the link
25401  * @cfg {Boolean} disable (true | false) default false
25402  * @cfg {Boolean} preventDefault (true | false) default true
25403  * @cfg {String} icon Font awesome icon
25404  * @cfg {String} pos Submenu align to (left | right) default right 
25405  * 
25406  * 
25407  * @constructor
25408  * Create a new Item
25409  * @param {Object} config The config object
25410  */
25411
25412
25413 Roo.bootstrap.menu.Item = function(config){
25414     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25415     this.addEvents({
25416         /**
25417          * @event mouseover
25418          * Fires when the mouse is hovering over this menu
25419          * @param {Roo.bootstrap.menu.Item} this
25420          * @param {Roo.EventObject} e
25421          */
25422         mouseover : true,
25423         /**
25424          * @event mouseout
25425          * Fires when the mouse exits this menu
25426          * @param {Roo.bootstrap.menu.Item} this
25427          * @param {Roo.EventObject} e
25428          */
25429         mouseout : true,
25430         // raw events
25431         /**
25432          * @event click
25433          * The raw click event for the entire grid.
25434          * @param {Roo.EventObject} e
25435          */
25436         click : true
25437     });
25438 };
25439
25440 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25441     
25442     submenu : false,
25443     href : '',
25444     html : '',
25445     preventDefault: true,
25446     disable : false,
25447     icon : false,
25448     pos : 'right',
25449     
25450     getAutoCreate : function()
25451     {
25452         var text = [
25453             {
25454                 tag : 'span',
25455                 cls : 'roo-menu-item-text',
25456                 html : this.html
25457             }
25458         ];
25459         
25460         if(this.icon){
25461             text.unshift({
25462                 tag : 'i',
25463                 cls : 'fa ' + this.icon
25464             })
25465         }
25466         
25467         var cfg = {
25468             tag : 'li',
25469             cn : [
25470                 {
25471                     tag : 'a',
25472                     href : this.href || '#',
25473                     cn : text
25474                 }
25475             ]
25476         };
25477         
25478         if(this.disable){
25479             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25480         }
25481         
25482         if(this.submenu){
25483             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25484             
25485             if(this.pos == 'left'){
25486                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25487             }
25488         }
25489         
25490         return cfg;
25491     },
25492     
25493     initEvents : function() 
25494     {
25495         this.el.on('mouseover', this.onMouseOver, this);
25496         this.el.on('mouseout', this.onMouseOut, this);
25497         
25498         this.el.select('a', true).first().on('click', this.onClick, this);
25499         
25500     },
25501     
25502     onClick : function(e)
25503     {
25504         if(this.preventDefault){
25505             e.preventDefault();
25506         }
25507         
25508         this.fireEvent("click", this, e);
25509     },
25510     
25511     onMouseOver : function(e)
25512     {
25513         if(this.submenu && this.pos == 'left'){
25514             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25515         }
25516         
25517         this.fireEvent("mouseover", this, e);
25518     },
25519     
25520     onMouseOut : function(e)
25521     {
25522         this.fireEvent("mouseout", this, e);
25523     }
25524 });
25525
25526  
25527
25528  /*
25529  * - LGPL
25530  *
25531  * menu separator
25532  * 
25533  */
25534 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25535
25536 /**
25537  * @class Roo.bootstrap.menu.Separator
25538  * @extends Roo.bootstrap.Component
25539  * Bootstrap Separator class
25540  * 
25541  * @constructor
25542  * Create a new Separator
25543  * @param {Object} config The config object
25544  */
25545
25546
25547 Roo.bootstrap.menu.Separator = function(config){
25548     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25549 };
25550
25551 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25552     
25553     getAutoCreate : function(){
25554         var cfg = {
25555             tag : 'li',
25556             cls: 'divider'
25557         };
25558         
25559         return cfg;
25560     }
25561    
25562 });
25563
25564  
25565
25566  /*
25567  * - LGPL
25568  *
25569  * Tooltip
25570  * 
25571  */
25572
25573 /**
25574  * @class Roo.bootstrap.Tooltip
25575  * Bootstrap Tooltip class
25576  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25577  * to determine which dom element triggers the tooltip.
25578  * 
25579  * It needs to add support for additional attributes like tooltip-position
25580  * 
25581  * @constructor
25582  * Create a new Toolti
25583  * @param {Object} config The config object
25584  */
25585
25586 Roo.bootstrap.Tooltip = function(config){
25587     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25588     
25589     this.alignment = Roo.bootstrap.Tooltip.alignment;
25590     
25591     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25592         this.alignment = config.alignment;
25593     }
25594     
25595 };
25596
25597 Roo.apply(Roo.bootstrap.Tooltip, {
25598     /**
25599      * @function init initialize tooltip monitoring.
25600      * @static
25601      */
25602     currentEl : false,
25603     currentTip : false,
25604     currentRegion : false,
25605     
25606     //  init : delay?
25607     
25608     init : function()
25609     {
25610         Roo.get(document).on('mouseover', this.enter ,this);
25611         Roo.get(document).on('mouseout', this.leave, this);
25612          
25613         
25614         this.currentTip = new Roo.bootstrap.Tooltip();
25615     },
25616     
25617     enter : function(ev)
25618     {
25619         var dom = ev.getTarget();
25620         
25621         //Roo.log(['enter',dom]);
25622         var el = Roo.fly(dom);
25623         if (this.currentEl) {
25624             //Roo.log(dom);
25625             //Roo.log(this.currentEl);
25626             //Roo.log(this.currentEl.contains(dom));
25627             if (this.currentEl == el) {
25628                 return;
25629             }
25630             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25631                 return;
25632             }
25633
25634         }
25635         
25636         if (this.currentTip.el) {
25637             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25638         }    
25639         //Roo.log(ev);
25640         
25641         if(!el || el.dom == document){
25642             return;
25643         }
25644         
25645         var bindEl = el;
25646         
25647         // you can not look for children, as if el is the body.. then everythign is the child..
25648         if (!el.attr('tooltip')) { //
25649             if (!el.select("[tooltip]").elements.length) {
25650                 return;
25651             }
25652             // is the mouse over this child...?
25653             bindEl = el.select("[tooltip]").first();
25654             var xy = ev.getXY();
25655             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25656                 //Roo.log("not in region.");
25657                 return;
25658             }
25659             //Roo.log("child element over..");
25660             
25661         }
25662         this.currentEl = bindEl;
25663         this.currentTip.bind(bindEl);
25664         this.currentRegion = Roo.lib.Region.getRegion(dom);
25665         this.currentTip.enter();
25666         
25667     },
25668     leave : function(ev)
25669     {
25670         var dom = ev.getTarget();
25671         //Roo.log(['leave',dom]);
25672         if (!this.currentEl) {
25673             return;
25674         }
25675         
25676         
25677         if (dom != this.currentEl.dom) {
25678             return;
25679         }
25680         var xy = ev.getXY();
25681         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25682             return;
25683         }
25684         // only activate leave if mouse cursor is outside... bounding box..
25685         
25686         
25687         
25688         
25689         if (this.currentTip) {
25690             this.currentTip.leave();
25691         }
25692         //Roo.log('clear currentEl');
25693         this.currentEl = false;
25694         
25695         
25696     },
25697     alignment : {
25698         'left' : ['r-l', [-2,0], 'right'],
25699         'right' : ['l-r', [2,0], 'left'],
25700         'bottom' : ['t-b', [0,2], 'top'],
25701         'top' : [ 'b-t', [0,-2], 'bottom']
25702     }
25703     
25704 });
25705
25706
25707 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25708     
25709     
25710     bindEl : false,
25711     
25712     delay : null, // can be { show : 300 , hide: 500}
25713     
25714     timeout : null,
25715     
25716     hoverState : null, //???
25717     
25718     placement : 'bottom', 
25719     
25720     alignment : false,
25721     
25722     getAutoCreate : function(){
25723     
25724         var cfg = {
25725            cls : 'tooltip',
25726            role : 'tooltip',
25727            cn : [
25728                 {
25729                     cls : 'tooltip-arrow'
25730                 },
25731                 {
25732                     cls : 'tooltip-inner'
25733                 }
25734            ]
25735         };
25736         
25737         return cfg;
25738     },
25739     bind : function(el)
25740     {
25741         this.bindEl = el;
25742     },
25743       
25744     
25745     enter : function () {
25746        
25747         if (this.timeout != null) {
25748             clearTimeout(this.timeout);
25749         }
25750         
25751         this.hoverState = 'in';
25752          //Roo.log("enter - show");
25753         if (!this.delay || !this.delay.show) {
25754             this.show();
25755             return;
25756         }
25757         var _t = this;
25758         this.timeout = setTimeout(function () {
25759             if (_t.hoverState == 'in') {
25760                 _t.show();
25761             }
25762         }, this.delay.show);
25763     },
25764     leave : function()
25765     {
25766         clearTimeout(this.timeout);
25767     
25768         this.hoverState = 'out';
25769          if (!this.delay || !this.delay.hide) {
25770             this.hide();
25771             return;
25772         }
25773        
25774         var _t = this;
25775         this.timeout = setTimeout(function () {
25776             //Roo.log("leave - timeout");
25777             
25778             if (_t.hoverState == 'out') {
25779                 _t.hide();
25780                 Roo.bootstrap.Tooltip.currentEl = false;
25781             }
25782         }, delay);
25783     },
25784     
25785     show : function (msg)
25786     {
25787         if (!this.el) {
25788             this.render(document.body);
25789         }
25790         // set content.
25791         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25792         
25793         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25794         
25795         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25796         
25797         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25798         
25799         var placement = typeof this.placement == 'function' ?
25800             this.placement.call(this, this.el, on_el) :
25801             this.placement;
25802             
25803         var autoToken = /\s?auto?\s?/i;
25804         var autoPlace = autoToken.test(placement);
25805         if (autoPlace) {
25806             placement = placement.replace(autoToken, '') || 'top';
25807         }
25808         
25809         //this.el.detach()
25810         //this.el.setXY([0,0]);
25811         this.el.show();
25812         //this.el.dom.style.display='block';
25813         
25814         //this.el.appendTo(on_el);
25815         
25816         var p = this.getPosition();
25817         var box = this.el.getBox();
25818         
25819         if (autoPlace) {
25820             // fixme..
25821         }
25822         
25823         var align = this.alignment[placement];
25824         
25825         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25826         
25827         if(placement == 'top' || placement == 'bottom'){
25828             if(xy[0] < 0){
25829                 placement = 'right';
25830             }
25831             
25832             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25833                 placement = 'left';
25834             }
25835             
25836             var scroll = Roo.select('body', true).first().getScroll();
25837             
25838             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25839                 placement = 'top';
25840             }
25841             
25842         }
25843         
25844         this.el.alignTo(this.bindEl, align[0],align[1]);
25845         //var arrow = this.el.select('.arrow',true).first();
25846         //arrow.set(align[2], 
25847         
25848         this.el.addClass(placement);
25849         
25850         this.el.addClass('in fade');
25851         
25852         this.hoverState = null;
25853         
25854         if (this.el.hasClass('fade')) {
25855             // fade it?
25856         }
25857         
25858     },
25859     hide : function()
25860     {
25861          
25862         if (!this.el) {
25863             return;
25864         }
25865         //this.el.setXY([0,0]);
25866         this.el.removeClass('in');
25867         //this.el.hide();
25868         
25869     }
25870     
25871 });
25872  
25873
25874  /*
25875  * - LGPL
25876  *
25877  * Location Picker
25878  * 
25879  */
25880
25881 /**
25882  * @class Roo.bootstrap.LocationPicker
25883  * @extends Roo.bootstrap.Component
25884  * Bootstrap LocationPicker class
25885  * @cfg {Number} latitude Position when init default 0
25886  * @cfg {Number} longitude Position when init default 0
25887  * @cfg {Number} zoom default 15
25888  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25889  * @cfg {Boolean} mapTypeControl default false
25890  * @cfg {Boolean} disableDoubleClickZoom default false
25891  * @cfg {Boolean} scrollwheel default true
25892  * @cfg {Boolean} streetViewControl default false
25893  * @cfg {Number} radius default 0
25894  * @cfg {String} locationName
25895  * @cfg {Boolean} draggable default true
25896  * @cfg {Boolean} enableAutocomplete default false
25897  * @cfg {Boolean} enableReverseGeocode default true
25898  * @cfg {String} markerTitle
25899  * 
25900  * @constructor
25901  * Create a new LocationPicker
25902  * @param {Object} config The config object
25903  */
25904
25905
25906 Roo.bootstrap.LocationPicker = function(config){
25907     
25908     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25909     
25910     this.addEvents({
25911         /**
25912          * @event initial
25913          * Fires when the picker initialized.
25914          * @param {Roo.bootstrap.LocationPicker} this
25915          * @param {Google Location} location
25916          */
25917         initial : true,
25918         /**
25919          * @event positionchanged
25920          * Fires when the picker position changed.
25921          * @param {Roo.bootstrap.LocationPicker} this
25922          * @param {Google Location} location
25923          */
25924         positionchanged : true,
25925         /**
25926          * @event resize
25927          * Fires when the map resize.
25928          * @param {Roo.bootstrap.LocationPicker} this
25929          */
25930         resize : true,
25931         /**
25932          * @event show
25933          * Fires when the map show.
25934          * @param {Roo.bootstrap.LocationPicker} this
25935          */
25936         show : true,
25937         /**
25938          * @event hide
25939          * Fires when the map hide.
25940          * @param {Roo.bootstrap.LocationPicker} this
25941          */
25942         hide : true,
25943         /**
25944          * @event mapClick
25945          * Fires when click the map.
25946          * @param {Roo.bootstrap.LocationPicker} this
25947          * @param {Map event} e
25948          */
25949         mapClick : true,
25950         /**
25951          * @event mapRightClick
25952          * Fires when right click the map.
25953          * @param {Roo.bootstrap.LocationPicker} this
25954          * @param {Map event} e
25955          */
25956         mapRightClick : true,
25957         /**
25958          * @event markerClick
25959          * Fires when click the marker.
25960          * @param {Roo.bootstrap.LocationPicker} this
25961          * @param {Map event} e
25962          */
25963         markerClick : true,
25964         /**
25965          * @event markerRightClick
25966          * Fires when right click the marker.
25967          * @param {Roo.bootstrap.LocationPicker} this
25968          * @param {Map event} e
25969          */
25970         markerRightClick : true,
25971         /**
25972          * @event OverlayViewDraw
25973          * Fires when OverlayView Draw
25974          * @param {Roo.bootstrap.LocationPicker} this
25975          */
25976         OverlayViewDraw : true,
25977         /**
25978          * @event OverlayViewOnAdd
25979          * Fires when OverlayView Draw
25980          * @param {Roo.bootstrap.LocationPicker} this
25981          */
25982         OverlayViewOnAdd : true,
25983         /**
25984          * @event OverlayViewOnRemove
25985          * Fires when OverlayView Draw
25986          * @param {Roo.bootstrap.LocationPicker} this
25987          */
25988         OverlayViewOnRemove : true,
25989         /**
25990          * @event OverlayViewShow
25991          * Fires when OverlayView Draw
25992          * @param {Roo.bootstrap.LocationPicker} this
25993          * @param {Pixel} cpx
25994          */
25995         OverlayViewShow : true,
25996         /**
25997          * @event OverlayViewHide
25998          * Fires when OverlayView Draw
25999          * @param {Roo.bootstrap.LocationPicker} this
26000          */
26001         OverlayViewHide : true,
26002         /**
26003          * @event loadexception
26004          * Fires when load google lib failed.
26005          * @param {Roo.bootstrap.LocationPicker} this
26006          */
26007         loadexception : true
26008     });
26009         
26010 };
26011
26012 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26013     
26014     gMapContext: false,
26015     
26016     latitude: 0,
26017     longitude: 0,
26018     zoom: 15,
26019     mapTypeId: false,
26020     mapTypeControl: false,
26021     disableDoubleClickZoom: false,
26022     scrollwheel: true,
26023     streetViewControl: false,
26024     radius: 0,
26025     locationName: '',
26026     draggable: true,
26027     enableAutocomplete: false,
26028     enableReverseGeocode: true,
26029     markerTitle: '',
26030     
26031     getAutoCreate: function()
26032     {
26033
26034         var cfg = {
26035             tag: 'div',
26036             cls: 'roo-location-picker'
26037         };
26038         
26039         return cfg
26040     },
26041     
26042     initEvents: function(ct, position)
26043     {       
26044         if(!this.el.getWidth() || this.isApplied()){
26045             return;
26046         }
26047         
26048         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26049         
26050         this.initial();
26051     },
26052     
26053     initial: function()
26054     {
26055         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26056             this.fireEvent('loadexception', this);
26057             return;
26058         }
26059         
26060         if(!this.mapTypeId){
26061             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26062         }
26063         
26064         this.gMapContext = this.GMapContext();
26065         
26066         this.initOverlayView();
26067         
26068         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26069         
26070         var _this = this;
26071                 
26072         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26073             _this.setPosition(_this.gMapContext.marker.position);
26074         });
26075         
26076         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26077             _this.fireEvent('mapClick', this, event);
26078             
26079         });
26080
26081         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26082             _this.fireEvent('mapRightClick', this, event);
26083             
26084         });
26085         
26086         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26087             _this.fireEvent('markerClick', this, event);
26088             
26089         });
26090
26091         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26092             _this.fireEvent('markerRightClick', this, event);
26093             
26094         });
26095         
26096         this.setPosition(this.gMapContext.location);
26097         
26098         this.fireEvent('initial', this, this.gMapContext.location);
26099     },
26100     
26101     initOverlayView: function()
26102     {
26103         var _this = this;
26104         
26105         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26106             
26107             draw: function()
26108             {
26109                 _this.fireEvent('OverlayViewDraw', _this);
26110             },
26111             
26112             onAdd: function()
26113             {
26114                 _this.fireEvent('OverlayViewOnAdd', _this);
26115             },
26116             
26117             onRemove: function()
26118             {
26119                 _this.fireEvent('OverlayViewOnRemove', _this);
26120             },
26121             
26122             show: function(cpx)
26123             {
26124                 _this.fireEvent('OverlayViewShow', _this, cpx);
26125             },
26126             
26127             hide: function()
26128             {
26129                 _this.fireEvent('OverlayViewHide', _this);
26130             }
26131             
26132         });
26133     },
26134     
26135     fromLatLngToContainerPixel: function(event)
26136     {
26137         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26138     },
26139     
26140     isApplied: function() 
26141     {
26142         return this.getGmapContext() == false ? false : true;
26143     },
26144     
26145     getGmapContext: function() 
26146     {
26147         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26148     },
26149     
26150     GMapContext: function() 
26151     {
26152         var position = new google.maps.LatLng(this.latitude, this.longitude);
26153         
26154         var _map = new google.maps.Map(this.el.dom, {
26155             center: position,
26156             zoom: this.zoom,
26157             mapTypeId: this.mapTypeId,
26158             mapTypeControl: this.mapTypeControl,
26159             disableDoubleClickZoom: this.disableDoubleClickZoom,
26160             scrollwheel: this.scrollwheel,
26161             streetViewControl: this.streetViewControl,
26162             locationName: this.locationName,
26163             draggable: this.draggable,
26164             enableAutocomplete: this.enableAutocomplete,
26165             enableReverseGeocode: this.enableReverseGeocode
26166         });
26167         
26168         var _marker = new google.maps.Marker({
26169             position: position,
26170             map: _map,
26171             title: this.markerTitle,
26172             draggable: this.draggable
26173         });
26174         
26175         return {
26176             map: _map,
26177             marker: _marker,
26178             circle: null,
26179             location: position,
26180             radius: this.radius,
26181             locationName: this.locationName,
26182             addressComponents: {
26183                 formatted_address: null,
26184                 addressLine1: null,
26185                 addressLine2: null,
26186                 streetName: null,
26187                 streetNumber: null,
26188                 city: null,
26189                 district: null,
26190                 state: null,
26191                 stateOrProvince: null
26192             },
26193             settings: this,
26194             domContainer: this.el.dom,
26195             geodecoder: new google.maps.Geocoder()
26196         };
26197     },
26198     
26199     drawCircle: function(center, radius, options) 
26200     {
26201         if (this.gMapContext.circle != null) {
26202             this.gMapContext.circle.setMap(null);
26203         }
26204         if (radius > 0) {
26205             radius *= 1;
26206             options = Roo.apply({}, options, {
26207                 strokeColor: "#0000FF",
26208                 strokeOpacity: .35,
26209                 strokeWeight: 2,
26210                 fillColor: "#0000FF",
26211                 fillOpacity: .2
26212             });
26213             
26214             options.map = this.gMapContext.map;
26215             options.radius = radius;
26216             options.center = center;
26217             this.gMapContext.circle = new google.maps.Circle(options);
26218             return this.gMapContext.circle;
26219         }
26220         
26221         return null;
26222     },
26223     
26224     setPosition: function(location) 
26225     {
26226         this.gMapContext.location = location;
26227         this.gMapContext.marker.setPosition(location);
26228         this.gMapContext.map.panTo(location);
26229         this.drawCircle(location, this.gMapContext.radius, {});
26230         
26231         var _this = this;
26232         
26233         if (this.gMapContext.settings.enableReverseGeocode) {
26234             this.gMapContext.geodecoder.geocode({
26235                 latLng: this.gMapContext.location
26236             }, function(results, status) {
26237                 
26238                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26239                     _this.gMapContext.locationName = results[0].formatted_address;
26240                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26241                     
26242                     _this.fireEvent('positionchanged', this, location);
26243                 }
26244             });
26245             
26246             return;
26247         }
26248         
26249         this.fireEvent('positionchanged', this, location);
26250     },
26251     
26252     resize: function()
26253     {
26254         google.maps.event.trigger(this.gMapContext.map, "resize");
26255         
26256         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26257         
26258         this.fireEvent('resize', this);
26259     },
26260     
26261     setPositionByLatLng: function(latitude, longitude)
26262     {
26263         this.setPosition(new google.maps.LatLng(latitude, longitude));
26264     },
26265     
26266     getCurrentPosition: function() 
26267     {
26268         return {
26269             latitude: this.gMapContext.location.lat(),
26270             longitude: this.gMapContext.location.lng()
26271         };
26272     },
26273     
26274     getAddressName: function() 
26275     {
26276         return this.gMapContext.locationName;
26277     },
26278     
26279     getAddressComponents: function() 
26280     {
26281         return this.gMapContext.addressComponents;
26282     },
26283     
26284     address_component_from_google_geocode: function(address_components) 
26285     {
26286         var result = {};
26287         
26288         for (var i = 0; i < address_components.length; i++) {
26289             var component = address_components[i];
26290             if (component.types.indexOf("postal_code") >= 0) {
26291                 result.postalCode = component.short_name;
26292             } else if (component.types.indexOf("street_number") >= 0) {
26293                 result.streetNumber = component.short_name;
26294             } else if (component.types.indexOf("route") >= 0) {
26295                 result.streetName = component.short_name;
26296             } else if (component.types.indexOf("neighborhood") >= 0) {
26297                 result.city = component.short_name;
26298             } else if (component.types.indexOf("locality") >= 0) {
26299                 result.city = component.short_name;
26300             } else if (component.types.indexOf("sublocality") >= 0) {
26301                 result.district = component.short_name;
26302             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26303                 result.stateOrProvince = component.short_name;
26304             } else if (component.types.indexOf("country") >= 0) {
26305                 result.country = component.short_name;
26306             }
26307         }
26308         
26309         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26310         result.addressLine2 = "";
26311         return result;
26312     },
26313     
26314     setZoomLevel: function(zoom)
26315     {
26316         this.gMapContext.map.setZoom(zoom);
26317     },
26318     
26319     show: function()
26320     {
26321         if(!this.el){
26322             return;
26323         }
26324         
26325         this.el.show();
26326         
26327         this.resize();
26328         
26329         this.fireEvent('show', this);
26330     },
26331     
26332     hide: function()
26333     {
26334         if(!this.el){
26335             return;
26336         }
26337         
26338         this.el.hide();
26339         
26340         this.fireEvent('hide', this);
26341     }
26342     
26343 });
26344
26345 Roo.apply(Roo.bootstrap.LocationPicker, {
26346     
26347     OverlayView : function(map, options)
26348     {
26349         options = options || {};
26350         
26351         this.setMap(map);
26352     }
26353     
26354     
26355 });/*
26356  * - LGPL
26357  *
26358  * Alert
26359  * 
26360  */
26361
26362 /**
26363  * @class Roo.bootstrap.Alert
26364  * @extends Roo.bootstrap.Component
26365  * Bootstrap Alert class
26366  * @cfg {String} title The title of alert
26367  * @cfg {String} html The content of alert
26368  * @cfg {String} weight (  success | info | warning | danger )
26369  * @cfg {String} faicon font-awesomeicon
26370  * 
26371  * @constructor
26372  * Create a new alert
26373  * @param {Object} config The config object
26374  */
26375
26376
26377 Roo.bootstrap.Alert = function(config){
26378     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26379     
26380 };
26381
26382 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26383     
26384     title: '',
26385     html: '',
26386     weight: false,
26387     faicon: false,
26388     
26389     getAutoCreate : function()
26390     {
26391         
26392         var cfg = {
26393             tag : 'div',
26394             cls : 'alert',
26395             cn : [
26396                 {
26397                     tag : 'i',
26398                     cls : 'roo-alert-icon'
26399                     
26400                 },
26401                 {
26402                     tag : 'b',
26403                     cls : 'roo-alert-title',
26404                     html : this.title
26405                 },
26406                 {
26407                     tag : 'span',
26408                     cls : 'roo-alert-text',
26409                     html : this.html
26410                 }
26411             ]
26412         };
26413         
26414         if(this.faicon){
26415             cfg.cn[0].cls += ' fa ' + this.faicon;
26416         }
26417         
26418         if(this.weight){
26419             cfg.cls += ' alert-' + this.weight;
26420         }
26421         
26422         return cfg;
26423     },
26424     
26425     initEvents: function() 
26426     {
26427         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26428     },
26429     
26430     setTitle : function(str)
26431     {
26432         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26433     },
26434     
26435     setText : function(str)
26436     {
26437         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26438     },
26439     
26440     setWeight : function(weight)
26441     {
26442         if(this.weight){
26443             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26444         }
26445         
26446         this.weight = weight;
26447         
26448         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26449     },
26450     
26451     setIcon : function(icon)
26452     {
26453         if(this.faicon){
26454             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26455         }
26456         
26457         this.faicon = icon;
26458         
26459         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26460     },
26461     
26462     hide: function() 
26463     {
26464         this.el.hide();   
26465     },
26466     
26467     show: function() 
26468     {  
26469         this.el.show();   
26470     }
26471     
26472 });
26473
26474  
26475 /*
26476 * Licence: LGPL
26477 */
26478
26479 /**
26480  * @class Roo.bootstrap.UploadCropbox
26481  * @extends Roo.bootstrap.Component
26482  * Bootstrap UploadCropbox class
26483  * @cfg {String} emptyText show when image has been loaded
26484  * @cfg {String} rotateNotify show when image too small to rotate
26485  * @cfg {Number} errorTimeout default 3000
26486  * @cfg {Number} minWidth default 300
26487  * @cfg {Number} minHeight default 300
26488  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26489  * @cfg {Boolean} isDocument (true|false) default false
26490  * @cfg {String} url action url
26491  * @cfg {String} paramName default 'imageUpload'
26492  * @cfg {String} method default POST
26493  * @cfg {Boolean} loadMask (true|false) default true
26494  * @cfg {Boolean} loadingText default 'Loading...'
26495  * 
26496  * @constructor
26497  * Create a new UploadCropbox
26498  * @param {Object} config The config object
26499  */
26500
26501 Roo.bootstrap.UploadCropbox = function(config){
26502     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26503     
26504     this.addEvents({
26505         /**
26506          * @event beforeselectfile
26507          * Fire before select file
26508          * @param {Roo.bootstrap.UploadCropbox} this
26509          */
26510         "beforeselectfile" : true,
26511         /**
26512          * @event initial
26513          * Fire after initEvent
26514          * @param {Roo.bootstrap.UploadCropbox} this
26515          */
26516         "initial" : true,
26517         /**
26518          * @event crop
26519          * Fire after initEvent
26520          * @param {Roo.bootstrap.UploadCropbox} this
26521          * @param {String} data
26522          */
26523         "crop" : true,
26524         /**
26525          * @event prepare
26526          * Fire when preparing the file data
26527          * @param {Roo.bootstrap.UploadCropbox} this
26528          * @param {Object} file
26529          */
26530         "prepare" : true,
26531         /**
26532          * @event exception
26533          * Fire when get exception
26534          * @param {Roo.bootstrap.UploadCropbox} this
26535          * @param {XMLHttpRequest} xhr
26536          */
26537         "exception" : true,
26538         /**
26539          * @event beforeloadcanvas
26540          * Fire before load the canvas
26541          * @param {Roo.bootstrap.UploadCropbox} this
26542          * @param {String} src
26543          */
26544         "beforeloadcanvas" : true,
26545         /**
26546          * @event trash
26547          * Fire when trash image
26548          * @param {Roo.bootstrap.UploadCropbox} this
26549          */
26550         "trash" : true,
26551         /**
26552          * @event download
26553          * Fire when download the image
26554          * @param {Roo.bootstrap.UploadCropbox} this
26555          */
26556         "download" : true,
26557         /**
26558          * @event footerbuttonclick
26559          * Fire when footerbuttonclick
26560          * @param {Roo.bootstrap.UploadCropbox} this
26561          * @param {String} type
26562          */
26563         "footerbuttonclick" : true,
26564         /**
26565          * @event resize
26566          * Fire when resize
26567          * @param {Roo.bootstrap.UploadCropbox} this
26568          */
26569         "resize" : true,
26570         /**
26571          * @event rotate
26572          * Fire when rotate the image
26573          * @param {Roo.bootstrap.UploadCropbox} this
26574          * @param {String} pos
26575          */
26576         "rotate" : true,
26577         /**
26578          * @event inspect
26579          * Fire when inspect the file
26580          * @param {Roo.bootstrap.UploadCropbox} this
26581          * @param {Object} file
26582          */
26583         "inspect" : true,
26584         /**
26585          * @event upload
26586          * Fire when xhr upload the file
26587          * @param {Roo.bootstrap.UploadCropbox} this
26588          * @param {Object} data
26589          */
26590         "upload" : true,
26591         /**
26592          * @event arrange
26593          * Fire when arrange the file data
26594          * @param {Roo.bootstrap.UploadCropbox} this
26595          * @param {Object} formData
26596          */
26597         "arrange" : true
26598     });
26599     
26600     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26601 };
26602
26603 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26604     
26605     emptyText : 'Click to upload image',
26606     rotateNotify : 'Image is too small to rotate',
26607     errorTimeout : 3000,
26608     scale : 0,
26609     baseScale : 1,
26610     rotate : 0,
26611     dragable : false,
26612     pinching : false,
26613     mouseX : 0,
26614     mouseY : 0,
26615     cropData : false,
26616     minWidth : 300,
26617     minHeight : 300,
26618     file : false,
26619     exif : {},
26620     baseRotate : 1,
26621     cropType : 'image/jpeg',
26622     buttons : false,
26623     canvasLoaded : false,
26624     isDocument : false,
26625     method : 'POST',
26626     paramName : 'imageUpload',
26627     loadMask : true,
26628     loadingText : 'Loading...',
26629     maskEl : false,
26630     
26631     getAutoCreate : function()
26632     {
26633         var cfg = {
26634             tag : 'div',
26635             cls : 'roo-upload-cropbox',
26636             cn : [
26637                 {
26638                     tag : 'input',
26639                     cls : 'roo-upload-cropbox-selector',
26640                     type : 'file'
26641                 },
26642                 {
26643                     tag : 'div',
26644                     cls : 'roo-upload-cropbox-body',
26645                     style : 'cursor:pointer',
26646                     cn : [
26647                         {
26648                             tag : 'div',
26649                             cls : 'roo-upload-cropbox-preview'
26650                         },
26651                         {
26652                             tag : 'div',
26653                             cls : 'roo-upload-cropbox-thumb'
26654                         },
26655                         {
26656                             tag : 'div',
26657                             cls : 'roo-upload-cropbox-empty-notify',
26658                             html : this.emptyText
26659                         },
26660                         {
26661                             tag : 'div',
26662                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26663                             html : this.rotateNotify
26664                         }
26665                     ]
26666                 },
26667                 {
26668                     tag : 'div',
26669                     cls : 'roo-upload-cropbox-footer',
26670                     cn : {
26671                         tag : 'div',
26672                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26673                         cn : []
26674                     }
26675                 }
26676             ]
26677         };
26678         
26679         return cfg;
26680     },
26681     
26682     onRender : function(ct, position)
26683     {
26684         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26685         
26686         if (this.buttons.length) {
26687             
26688             Roo.each(this.buttons, function(bb) {
26689                 
26690                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26691                 
26692                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26693                 
26694             }, this);
26695         }
26696         
26697         if(this.loadMask){
26698             this.maskEl = this.el;
26699         }
26700     },
26701     
26702     initEvents : function()
26703     {
26704         this.urlAPI = (window.createObjectURL && window) || 
26705                                 (window.URL && URL.revokeObjectURL && URL) || 
26706                                 (window.webkitURL && webkitURL);
26707                         
26708         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26709         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26710         
26711         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26712         this.selectorEl.hide();
26713         
26714         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26715         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26716         
26717         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26718         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26719         this.thumbEl.hide();
26720         
26721         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26722         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26723         
26724         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26725         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26726         this.errorEl.hide();
26727         
26728         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26729         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26730         this.footerEl.hide();
26731         
26732         this.setThumbBoxSize();
26733         
26734         this.bind();
26735         
26736         this.resize();
26737         
26738         this.fireEvent('initial', this);
26739     },
26740
26741     bind : function()
26742     {
26743         var _this = this;
26744         
26745         window.addEventListener("resize", function() { _this.resize(); } );
26746         
26747         this.bodyEl.on('click', this.beforeSelectFile, this);
26748         
26749         if(Roo.isTouch){
26750             this.bodyEl.on('touchstart', this.onTouchStart, this);
26751             this.bodyEl.on('touchmove', this.onTouchMove, this);
26752             this.bodyEl.on('touchend', this.onTouchEnd, this);
26753         }
26754         
26755         if(!Roo.isTouch){
26756             this.bodyEl.on('mousedown', this.onMouseDown, this);
26757             this.bodyEl.on('mousemove', this.onMouseMove, this);
26758             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26759             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26760             Roo.get(document).on('mouseup', this.onMouseUp, this);
26761         }
26762         
26763         this.selectorEl.on('change', this.onFileSelected, this);
26764     },
26765     
26766     reset : function()
26767     {    
26768         this.scale = 0;
26769         this.baseScale = 1;
26770         this.rotate = 0;
26771         this.baseRotate = 1;
26772         this.dragable = false;
26773         this.pinching = false;
26774         this.mouseX = 0;
26775         this.mouseY = 0;
26776         this.cropData = false;
26777         this.notifyEl.dom.innerHTML = this.emptyText;
26778         
26779         this.selectorEl.dom.value = '';
26780         
26781     },
26782     
26783     resize : function()
26784     {
26785         if(this.fireEvent('resize', this) != false){
26786             this.setThumbBoxPosition();
26787             this.setCanvasPosition();
26788         }
26789     },
26790     
26791     onFooterButtonClick : function(e, el, o, type)
26792     {
26793         switch (type) {
26794             case 'rotate-left' :
26795                 this.onRotateLeft(e);
26796                 break;
26797             case 'rotate-right' :
26798                 this.onRotateRight(e);
26799                 break;
26800             case 'picture' :
26801                 this.beforeSelectFile(e);
26802                 break;
26803             case 'trash' :
26804                 this.trash(e);
26805                 break;
26806             case 'crop' :
26807                 this.crop(e);
26808                 break;
26809             case 'download' :
26810                 this.download(e);
26811                 break;
26812             default :
26813                 break;
26814         }
26815         
26816         this.fireEvent('footerbuttonclick', this, type);
26817     },
26818     
26819     beforeSelectFile : function(e)
26820     {
26821         e.preventDefault();
26822         
26823         if(this.fireEvent('beforeselectfile', this) != false){
26824             this.selectorEl.dom.click();
26825         }
26826     },
26827     
26828     onFileSelected : function(e)
26829     {
26830         e.preventDefault();
26831         
26832         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26833             return;
26834         }
26835         
26836         var file = this.selectorEl.dom.files[0];
26837         
26838         if(this.fireEvent('inspect', this, file) != false){
26839             this.prepare(file);
26840         }
26841         
26842     },
26843     
26844     trash : function(e)
26845     {
26846         this.fireEvent('trash', this);
26847     },
26848     
26849     download : function(e)
26850     {
26851         this.fireEvent('download', this);
26852     },
26853     
26854     loadCanvas : function(src)
26855     {   
26856         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26857             
26858             this.reset();
26859             
26860             this.imageEl = document.createElement('img');
26861             
26862             var _this = this;
26863             
26864             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26865             
26866             this.imageEl.src = src;
26867         }
26868     },
26869     
26870     onLoadCanvas : function()
26871     {   
26872         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26873         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26874         
26875         this.bodyEl.un('click', this.beforeSelectFile, this);
26876         
26877         this.notifyEl.hide();
26878         this.thumbEl.show();
26879         this.footerEl.show();
26880         
26881         this.baseRotateLevel();
26882         
26883         if(this.isDocument){
26884             this.setThumbBoxSize();
26885         }
26886         
26887         this.setThumbBoxPosition();
26888         
26889         this.baseScaleLevel();
26890         
26891         this.draw();
26892         
26893         this.resize();
26894         
26895         this.canvasLoaded = true;
26896         
26897         if(this.loadMask){
26898             this.maskEl.unmask();
26899         }
26900         
26901     },
26902     
26903     setCanvasPosition : function()
26904     {   
26905         if(!this.canvasEl){
26906             return;
26907         }
26908         
26909         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26910         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26911         
26912         this.previewEl.setLeft(pw);
26913         this.previewEl.setTop(ph);
26914         
26915     },
26916     
26917     onMouseDown : function(e)
26918     {   
26919         e.stopEvent();
26920         
26921         this.dragable = true;
26922         this.pinching = false;
26923         
26924         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26925             this.dragable = false;
26926             return;
26927         }
26928         
26929         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26930         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26931         
26932     },
26933     
26934     onMouseMove : function(e)
26935     {   
26936         e.stopEvent();
26937         
26938         if(!this.canvasLoaded){
26939             return;
26940         }
26941         
26942         if (!this.dragable){
26943             return;
26944         }
26945         
26946         var minX = Math.ceil(this.thumbEl.getLeft(true));
26947         var minY = Math.ceil(this.thumbEl.getTop(true));
26948         
26949         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26950         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26951         
26952         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26953         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26954         
26955         x = x - this.mouseX;
26956         y = y - this.mouseY;
26957         
26958         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26959         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26960         
26961         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26962         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26963         
26964         this.previewEl.setLeft(bgX);
26965         this.previewEl.setTop(bgY);
26966         
26967         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26968         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26969     },
26970     
26971     onMouseUp : function(e)
26972     {   
26973         e.stopEvent();
26974         
26975         this.dragable = false;
26976     },
26977     
26978     onMouseWheel : function(e)
26979     {   
26980         e.stopEvent();
26981         
26982         this.startScale = this.scale;
26983         
26984         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26985         
26986         if(!this.zoomable()){
26987             this.scale = this.startScale;
26988             return;
26989         }
26990         
26991         this.draw();
26992         
26993         return;
26994     },
26995     
26996     zoomable : function()
26997     {
26998         var minScale = this.thumbEl.getWidth() / this.minWidth;
26999         
27000         if(this.minWidth < this.minHeight){
27001             minScale = this.thumbEl.getHeight() / this.minHeight;
27002         }
27003         
27004         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27005         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27006         
27007         if(
27008                 this.isDocument &&
27009                 (this.rotate == 0 || this.rotate == 180) && 
27010                 (
27011                     width > this.imageEl.OriginWidth || 
27012                     height > this.imageEl.OriginHeight ||
27013                     (width < this.minWidth && height < this.minHeight)
27014                 )
27015         ){
27016             return false;
27017         }
27018         
27019         if(
27020                 this.isDocument &&
27021                 (this.rotate == 90 || this.rotate == 270) && 
27022                 (
27023                     width > this.imageEl.OriginWidth || 
27024                     height > this.imageEl.OriginHeight ||
27025                     (width < this.minHeight && height < this.minWidth)
27026                 )
27027         ){
27028             return false;
27029         }
27030         
27031         if(
27032                 !this.isDocument &&
27033                 (this.rotate == 0 || this.rotate == 180) && 
27034                 (
27035                     width < this.minWidth || 
27036                     width > this.imageEl.OriginWidth || 
27037                     height < this.minHeight || 
27038                     height > this.imageEl.OriginHeight
27039                 )
27040         ){
27041             return false;
27042         }
27043         
27044         if(
27045                 !this.isDocument &&
27046                 (this.rotate == 90 || this.rotate == 270) && 
27047                 (
27048                     width < this.minHeight || 
27049                     width > this.imageEl.OriginWidth || 
27050                     height < this.minWidth || 
27051                     height > this.imageEl.OriginHeight
27052                 )
27053         ){
27054             return false;
27055         }
27056         
27057         return true;
27058         
27059     },
27060     
27061     onRotateLeft : function(e)
27062     {   
27063         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27064             
27065             var minScale = this.thumbEl.getWidth() / this.minWidth;
27066             
27067             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27068             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27069             
27070             this.startScale = this.scale;
27071             
27072             while (this.getScaleLevel() < minScale){
27073             
27074                 this.scale = this.scale + 1;
27075                 
27076                 if(!this.zoomable()){
27077                     break;
27078                 }
27079                 
27080                 if(
27081                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27082                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27083                 ){
27084                     continue;
27085                 }
27086                 
27087                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27088
27089                 this.draw();
27090                 
27091                 return;
27092             }
27093             
27094             this.scale = this.startScale;
27095             
27096             this.onRotateFail();
27097             
27098             return false;
27099         }
27100         
27101         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27102
27103         if(this.isDocument){
27104             this.setThumbBoxSize();
27105             this.setThumbBoxPosition();
27106             this.setCanvasPosition();
27107         }
27108         
27109         this.draw();
27110         
27111         this.fireEvent('rotate', this, 'left');
27112         
27113     },
27114     
27115     onRotateRight : function(e)
27116     {
27117         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27118             
27119             var minScale = this.thumbEl.getWidth() / this.minWidth;
27120         
27121             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27122             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27123             
27124             this.startScale = this.scale;
27125             
27126             while (this.getScaleLevel() < minScale){
27127             
27128                 this.scale = this.scale + 1;
27129                 
27130                 if(!this.zoomable()){
27131                     break;
27132                 }
27133                 
27134                 if(
27135                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27136                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27137                 ){
27138                     continue;
27139                 }
27140                 
27141                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27142
27143                 this.draw();
27144                 
27145                 return;
27146             }
27147             
27148             this.scale = this.startScale;
27149             
27150             this.onRotateFail();
27151             
27152             return false;
27153         }
27154         
27155         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27156
27157         if(this.isDocument){
27158             this.setThumbBoxSize();
27159             this.setThumbBoxPosition();
27160             this.setCanvasPosition();
27161         }
27162         
27163         this.draw();
27164         
27165         this.fireEvent('rotate', this, 'right');
27166     },
27167     
27168     onRotateFail : function()
27169     {
27170         this.errorEl.show(true);
27171         
27172         var _this = this;
27173         
27174         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27175     },
27176     
27177     draw : function()
27178     {
27179         this.previewEl.dom.innerHTML = '';
27180         
27181         var canvasEl = document.createElement("canvas");
27182         
27183         var contextEl = canvasEl.getContext("2d");
27184         
27185         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27186         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27187         var center = this.imageEl.OriginWidth / 2;
27188         
27189         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27190             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27191             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27192             center = this.imageEl.OriginHeight / 2;
27193         }
27194         
27195         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27196         
27197         contextEl.translate(center, center);
27198         contextEl.rotate(this.rotate * Math.PI / 180);
27199
27200         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27201         
27202         this.canvasEl = document.createElement("canvas");
27203         
27204         this.contextEl = this.canvasEl.getContext("2d");
27205         
27206         switch (this.rotate) {
27207             case 0 :
27208                 
27209                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27210                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27211                 
27212                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27213                 
27214                 break;
27215             case 90 : 
27216                 
27217                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27218                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27219                 
27220                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27221                     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);
27222                     break;
27223                 }
27224                 
27225                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27226                 
27227                 break;
27228             case 180 :
27229                 
27230                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27231                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27232                 
27233                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27234                     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);
27235                     break;
27236                 }
27237                 
27238                 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);
27239                 
27240                 break;
27241             case 270 :
27242                 
27243                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27244                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27245         
27246                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27247                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27248                     break;
27249                 }
27250                 
27251                 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);
27252                 
27253                 break;
27254             default : 
27255                 break;
27256         }
27257         
27258         this.previewEl.appendChild(this.canvasEl);
27259         
27260         this.setCanvasPosition();
27261     },
27262     
27263     crop : function()
27264     {
27265         if(!this.canvasLoaded){
27266             return;
27267         }
27268         
27269         var imageCanvas = document.createElement("canvas");
27270         
27271         var imageContext = imageCanvas.getContext("2d");
27272         
27273         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27274         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27275         
27276         var center = imageCanvas.width / 2;
27277         
27278         imageContext.translate(center, center);
27279         
27280         imageContext.rotate(this.rotate * Math.PI / 180);
27281         
27282         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27283         
27284         var canvas = document.createElement("canvas");
27285         
27286         var context = canvas.getContext("2d");
27287                 
27288         canvas.width = this.minWidth;
27289         canvas.height = this.minHeight;
27290
27291         switch (this.rotate) {
27292             case 0 :
27293                 
27294                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27295                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27296                 
27297                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27298                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27299                 
27300                 var targetWidth = this.minWidth - 2 * x;
27301                 var targetHeight = this.minHeight - 2 * y;
27302                 
27303                 var scale = 1;
27304                 
27305                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27306                     scale = targetWidth / width;
27307                 }
27308                 
27309                 if(x > 0 && y == 0){
27310                     scale = targetHeight / height;
27311                 }
27312                 
27313                 if(x > 0 && y > 0){
27314                     scale = targetWidth / width;
27315                     
27316                     if(width < height){
27317                         scale = targetHeight / height;
27318                     }
27319                 }
27320                 
27321                 context.scale(scale, scale);
27322                 
27323                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27324                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27325
27326                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27327                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27328
27329                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27330                 
27331                 break;
27332             case 90 : 
27333                 
27334                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27335                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27336                 
27337                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27338                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27339                 
27340                 var targetWidth = this.minWidth - 2 * x;
27341                 var targetHeight = this.minHeight - 2 * y;
27342                 
27343                 var scale = 1;
27344                 
27345                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27346                     scale = targetWidth / width;
27347                 }
27348                 
27349                 if(x > 0 && y == 0){
27350                     scale = targetHeight / height;
27351                 }
27352                 
27353                 if(x > 0 && y > 0){
27354                     scale = targetWidth / width;
27355                     
27356                     if(width < height){
27357                         scale = targetHeight / height;
27358                     }
27359                 }
27360                 
27361                 context.scale(scale, scale);
27362                 
27363                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27364                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27365
27366                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27367                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27368                 
27369                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27370                 
27371                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27372                 
27373                 break;
27374             case 180 :
27375                 
27376                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27377                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27378                 
27379                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27380                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27381                 
27382                 var targetWidth = this.minWidth - 2 * x;
27383                 var targetHeight = this.minHeight - 2 * y;
27384                 
27385                 var scale = 1;
27386                 
27387                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27388                     scale = targetWidth / width;
27389                 }
27390                 
27391                 if(x > 0 && y == 0){
27392                     scale = targetHeight / height;
27393                 }
27394                 
27395                 if(x > 0 && y > 0){
27396                     scale = targetWidth / width;
27397                     
27398                     if(width < height){
27399                         scale = targetHeight / height;
27400                     }
27401                 }
27402                 
27403                 context.scale(scale, scale);
27404                 
27405                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27406                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27407
27408                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27409                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27410
27411                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27412                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27413                 
27414                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27415                 
27416                 break;
27417             case 270 :
27418                 
27419                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27420                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27421                 
27422                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27423                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27424                 
27425                 var targetWidth = this.minWidth - 2 * x;
27426                 var targetHeight = this.minHeight - 2 * y;
27427                 
27428                 var scale = 1;
27429                 
27430                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27431                     scale = targetWidth / width;
27432                 }
27433                 
27434                 if(x > 0 && y == 0){
27435                     scale = targetHeight / height;
27436                 }
27437                 
27438                 if(x > 0 && y > 0){
27439                     scale = targetWidth / width;
27440                     
27441                     if(width < height){
27442                         scale = targetHeight / height;
27443                     }
27444                 }
27445                 
27446                 context.scale(scale, scale);
27447                 
27448                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27449                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27450
27451                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27452                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27453                 
27454                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27455                 
27456                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27457                 
27458                 break;
27459             default : 
27460                 break;
27461         }
27462         
27463         this.cropData = canvas.toDataURL(this.cropType);
27464         
27465         if(this.fireEvent('crop', this, this.cropData) !== false){
27466             this.process(this.file, this.cropData);
27467         }
27468         
27469         return;
27470         
27471     },
27472     
27473     setThumbBoxSize : function()
27474     {
27475         var width, height;
27476         
27477         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27478             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27479             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27480             
27481             this.minWidth = width;
27482             this.minHeight = height;
27483             
27484             if(this.rotate == 90 || this.rotate == 270){
27485                 this.minWidth = height;
27486                 this.minHeight = width;
27487             }
27488         }
27489         
27490         height = 300;
27491         width = Math.ceil(this.minWidth * height / this.minHeight);
27492         
27493         if(this.minWidth > this.minHeight){
27494             width = 300;
27495             height = Math.ceil(this.minHeight * width / this.minWidth);
27496         }
27497         
27498         this.thumbEl.setStyle({
27499             width : width + 'px',
27500             height : height + 'px'
27501         });
27502
27503         return;
27504             
27505     },
27506     
27507     setThumbBoxPosition : function()
27508     {
27509         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27510         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27511         
27512         this.thumbEl.setLeft(x);
27513         this.thumbEl.setTop(y);
27514         
27515     },
27516     
27517     baseRotateLevel : function()
27518     {
27519         this.baseRotate = 1;
27520         
27521         if(
27522                 typeof(this.exif) != 'undefined' &&
27523                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27524                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27525         ){
27526             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27527         }
27528         
27529         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27530         
27531     },
27532     
27533     baseScaleLevel : function()
27534     {
27535         var width, height;
27536         
27537         if(this.isDocument){
27538             
27539             if(this.baseRotate == 6 || this.baseRotate == 8){
27540             
27541                 height = this.thumbEl.getHeight();
27542                 this.baseScale = height / this.imageEl.OriginWidth;
27543
27544                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27545                     width = this.thumbEl.getWidth();
27546                     this.baseScale = width / this.imageEl.OriginHeight;
27547                 }
27548
27549                 return;
27550             }
27551
27552             height = this.thumbEl.getHeight();
27553             this.baseScale = height / this.imageEl.OriginHeight;
27554
27555             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27556                 width = this.thumbEl.getWidth();
27557                 this.baseScale = width / this.imageEl.OriginWidth;
27558             }
27559
27560             return;
27561         }
27562         
27563         if(this.baseRotate == 6 || this.baseRotate == 8){
27564             
27565             width = this.thumbEl.getHeight();
27566             this.baseScale = width / this.imageEl.OriginHeight;
27567             
27568             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27569                 height = this.thumbEl.getWidth();
27570                 this.baseScale = height / this.imageEl.OriginHeight;
27571             }
27572             
27573             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27574                 height = this.thumbEl.getWidth();
27575                 this.baseScale = height / this.imageEl.OriginHeight;
27576                 
27577                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27578                     width = this.thumbEl.getHeight();
27579                     this.baseScale = width / this.imageEl.OriginWidth;
27580                 }
27581             }
27582             
27583             return;
27584         }
27585         
27586         width = this.thumbEl.getWidth();
27587         this.baseScale = width / this.imageEl.OriginWidth;
27588         
27589         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27590             height = this.thumbEl.getHeight();
27591             this.baseScale = height / this.imageEl.OriginHeight;
27592         }
27593         
27594         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27595             
27596             height = this.thumbEl.getHeight();
27597             this.baseScale = height / this.imageEl.OriginHeight;
27598             
27599             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27600                 width = this.thumbEl.getWidth();
27601                 this.baseScale = width / this.imageEl.OriginWidth;
27602             }
27603             
27604         }
27605         
27606         return;
27607     },
27608     
27609     getScaleLevel : function()
27610     {
27611         return this.baseScale * Math.pow(1.1, this.scale);
27612     },
27613     
27614     onTouchStart : function(e)
27615     {
27616         if(!this.canvasLoaded){
27617             this.beforeSelectFile(e);
27618             return;
27619         }
27620         
27621         var touches = e.browserEvent.touches;
27622         
27623         if(!touches){
27624             return;
27625         }
27626         
27627         if(touches.length == 1){
27628             this.onMouseDown(e);
27629             return;
27630         }
27631         
27632         if(touches.length != 2){
27633             return;
27634         }
27635         
27636         var coords = [];
27637         
27638         for(var i = 0, finger; finger = touches[i]; i++){
27639             coords.push(finger.pageX, finger.pageY);
27640         }
27641         
27642         var x = Math.pow(coords[0] - coords[2], 2);
27643         var y = Math.pow(coords[1] - coords[3], 2);
27644         
27645         this.startDistance = Math.sqrt(x + y);
27646         
27647         this.startScale = this.scale;
27648         
27649         this.pinching = true;
27650         this.dragable = false;
27651         
27652     },
27653     
27654     onTouchMove : function(e)
27655     {
27656         if(!this.pinching && !this.dragable){
27657             return;
27658         }
27659         
27660         var touches = e.browserEvent.touches;
27661         
27662         if(!touches){
27663             return;
27664         }
27665         
27666         if(this.dragable){
27667             this.onMouseMove(e);
27668             return;
27669         }
27670         
27671         var coords = [];
27672         
27673         for(var i = 0, finger; finger = touches[i]; i++){
27674             coords.push(finger.pageX, finger.pageY);
27675         }
27676         
27677         var x = Math.pow(coords[0] - coords[2], 2);
27678         var y = Math.pow(coords[1] - coords[3], 2);
27679         
27680         this.endDistance = Math.sqrt(x + y);
27681         
27682         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27683         
27684         if(!this.zoomable()){
27685             this.scale = this.startScale;
27686             return;
27687         }
27688         
27689         this.draw();
27690         
27691     },
27692     
27693     onTouchEnd : function(e)
27694     {
27695         this.pinching = false;
27696         this.dragable = false;
27697         
27698     },
27699     
27700     process : function(file, crop)
27701     {
27702         if(this.loadMask){
27703             this.maskEl.mask(this.loadingText);
27704         }
27705         
27706         this.xhr = new XMLHttpRequest();
27707         
27708         file.xhr = this.xhr;
27709
27710         this.xhr.open(this.method, this.url, true);
27711         
27712         var headers = {
27713             "Accept": "application/json",
27714             "Cache-Control": "no-cache",
27715             "X-Requested-With": "XMLHttpRequest"
27716         };
27717         
27718         for (var headerName in headers) {
27719             var headerValue = headers[headerName];
27720             if (headerValue) {
27721                 this.xhr.setRequestHeader(headerName, headerValue);
27722             }
27723         }
27724         
27725         var _this = this;
27726         
27727         this.xhr.onload = function()
27728         {
27729             _this.xhrOnLoad(_this.xhr);
27730         }
27731         
27732         this.xhr.onerror = function()
27733         {
27734             _this.xhrOnError(_this.xhr);
27735         }
27736         
27737         var formData = new FormData();
27738
27739         formData.append('returnHTML', 'NO');
27740         
27741         if(crop){
27742             formData.append('crop', crop);
27743         }
27744         
27745         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27746             formData.append(this.paramName, file, file.name);
27747         }
27748         
27749         if(typeof(file.filename) != 'undefined'){
27750             formData.append('filename', file.filename);
27751         }
27752         
27753         if(typeof(file.mimetype) != 'undefined'){
27754             formData.append('mimetype', file.mimetype);
27755         }
27756         
27757         if(this.fireEvent('arrange', this, formData) != false){
27758             this.xhr.send(formData);
27759         };
27760     },
27761     
27762     xhrOnLoad : function(xhr)
27763     {
27764         if(this.loadMask){
27765             this.maskEl.unmask();
27766         }
27767         
27768         if (xhr.readyState !== 4) {
27769             this.fireEvent('exception', this, xhr);
27770             return;
27771         }
27772
27773         var response = Roo.decode(xhr.responseText);
27774         
27775         if(!response.success){
27776             this.fireEvent('exception', this, xhr);
27777             return;
27778         }
27779         
27780         var response = Roo.decode(xhr.responseText);
27781         
27782         this.fireEvent('upload', this, response);
27783         
27784     },
27785     
27786     xhrOnError : function()
27787     {
27788         if(this.loadMask){
27789             this.maskEl.unmask();
27790         }
27791         
27792         Roo.log('xhr on error');
27793         
27794         var response = Roo.decode(xhr.responseText);
27795           
27796         Roo.log(response);
27797         
27798     },
27799     
27800     prepare : function(file)
27801     {   
27802         if(this.loadMask){
27803             this.maskEl.mask(this.loadingText);
27804         }
27805         
27806         this.file = false;
27807         this.exif = {};
27808         
27809         if(typeof(file) === 'string'){
27810             this.loadCanvas(file);
27811             return;
27812         }
27813         
27814         if(!file || !this.urlAPI){
27815             return;
27816         }
27817         
27818         this.file = file;
27819         this.cropType = file.type;
27820         
27821         var _this = this;
27822         
27823         if(this.fireEvent('prepare', this, this.file) != false){
27824             
27825             var reader = new FileReader();
27826             
27827             reader.onload = function (e) {
27828                 if (e.target.error) {
27829                     Roo.log(e.target.error);
27830                     return;
27831                 }
27832                 
27833                 var buffer = e.target.result,
27834                     dataView = new DataView(buffer),
27835                     offset = 2,
27836                     maxOffset = dataView.byteLength - 4,
27837                     markerBytes,
27838                     markerLength;
27839                 
27840                 if (dataView.getUint16(0) === 0xffd8) {
27841                     while (offset < maxOffset) {
27842                         markerBytes = dataView.getUint16(offset);
27843                         
27844                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27845                             markerLength = dataView.getUint16(offset + 2) + 2;
27846                             if (offset + markerLength > dataView.byteLength) {
27847                                 Roo.log('Invalid meta data: Invalid segment size.');
27848                                 break;
27849                             }
27850                             
27851                             if(markerBytes == 0xffe1){
27852                                 _this.parseExifData(
27853                                     dataView,
27854                                     offset,
27855                                     markerLength
27856                                 );
27857                             }
27858                             
27859                             offset += markerLength;
27860                             
27861                             continue;
27862                         }
27863                         
27864                         break;
27865                     }
27866                     
27867                 }
27868                 
27869                 var url = _this.urlAPI.createObjectURL(_this.file);
27870                 
27871                 _this.loadCanvas(url);
27872                 
27873                 return;
27874             }
27875             
27876             reader.readAsArrayBuffer(this.file);
27877             
27878         }
27879         
27880     },
27881     
27882     parseExifData : function(dataView, offset, length)
27883     {
27884         var tiffOffset = offset + 10,
27885             littleEndian,
27886             dirOffset;
27887     
27888         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27889             // No Exif data, might be XMP data instead
27890             return;
27891         }
27892         
27893         // Check for the ASCII code for "Exif" (0x45786966):
27894         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27895             // No Exif data, might be XMP data instead
27896             return;
27897         }
27898         if (tiffOffset + 8 > dataView.byteLength) {
27899             Roo.log('Invalid Exif data: Invalid segment size.');
27900             return;
27901         }
27902         // Check for the two null bytes:
27903         if (dataView.getUint16(offset + 8) !== 0x0000) {
27904             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27905             return;
27906         }
27907         // Check the byte alignment:
27908         switch (dataView.getUint16(tiffOffset)) {
27909         case 0x4949:
27910             littleEndian = true;
27911             break;
27912         case 0x4D4D:
27913             littleEndian = false;
27914             break;
27915         default:
27916             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27917             return;
27918         }
27919         // Check for the TIFF tag marker (0x002A):
27920         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27921             Roo.log('Invalid Exif data: Missing TIFF marker.');
27922             return;
27923         }
27924         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27925         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27926         
27927         this.parseExifTags(
27928             dataView,
27929             tiffOffset,
27930             tiffOffset + dirOffset,
27931             littleEndian
27932         );
27933     },
27934     
27935     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27936     {
27937         var tagsNumber,
27938             dirEndOffset,
27939             i;
27940         if (dirOffset + 6 > dataView.byteLength) {
27941             Roo.log('Invalid Exif data: Invalid directory offset.');
27942             return;
27943         }
27944         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27945         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27946         if (dirEndOffset + 4 > dataView.byteLength) {
27947             Roo.log('Invalid Exif data: Invalid directory size.');
27948             return;
27949         }
27950         for (i = 0; i < tagsNumber; i += 1) {
27951             this.parseExifTag(
27952                 dataView,
27953                 tiffOffset,
27954                 dirOffset + 2 + 12 * i, // tag offset
27955                 littleEndian
27956             );
27957         }
27958         // Return the offset to the next directory:
27959         return dataView.getUint32(dirEndOffset, littleEndian);
27960     },
27961     
27962     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27963     {
27964         var tag = dataView.getUint16(offset, littleEndian);
27965         
27966         this.exif[tag] = this.getExifValue(
27967             dataView,
27968             tiffOffset,
27969             offset,
27970             dataView.getUint16(offset + 2, littleEndian), // tag type
27971             dataView.getUint32(offset + 4, littleEndian), // tag length
27972             littleEndian
27973         );
27974     },
27975     
27976     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27977     {
27978         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27979             tagSize,
27980             dataOffset,
27981             values,
27982             i,
27983             str,
27984             c;
27985     
27986         if (!tagType) {
27987             Roo.log('Invalid Exif data: Invalid tag type.');
27988             return;
27989         }
27990         
27991         tagSize = tagType.size * length;
27992         // Determine if the value is contained in the dataOffset bytes,
27993         // or if the value at the dataOffset is a pointer to the actual data:
27994         dataOffset = tagSize > 4 ?
27995                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27996         if (dataOffset + tagSize > dataView.byteLength) {
27997             Roo.log('Invalid Exif data: Invalid data offset.');
27998             return;
27999         }
28000         if (length === 1) {
28001             return tagType.getValue(dataView, dataOffset, littleEndian);
28002         }
28003         values = [];
28004         for (i = 0; i < length; i += 1) {
28005             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28006         }
28007         
28008         if (tagType.ascii) {
28009             str = '';
28010             // Concatenate the chars:
28011             for (i = 0; i < values.length; i += 1) {
28012                 c = values[i];
28013                 // Ignore the terminating NULL byte(s):
28014                 if (c === '\u0000') {
28015                     break;
28016                 }
28017                 str += c;
28018             }
28019             return str;
28020         }
28021         return values;
28022     }
28023     
28024 });
28025
28026 Roo.apply(Roo.bootstrap.UploadCropbox, {
28027     tags : {
28028         'Orientation': 0x0112
28029     },
28030     
28031     Orientation: {
28032             1: 0, //'top-left',
28033 //            2: 'top-right',
28034             3: 180, //'bottom-right',
28035 //            4: 'bottom-left',
28036 //            5: 'left-top',
28037             6: 90, //'right-top',
28038 //            7: 'right-bottom',
28039             8: 270 //'left-bottom'
28040     },
28041     
28042     exifTagTypes : {
28043         // byte, 8-bit unsigned int:
28044         1: {
28045             getValue: function (dataView, dataOffset) {
28046                 return dataView.getUint8(dataOffset);
28047             },
28048             size: 1
28049         },
28050         // ascii, 8-bit byte:
28051         2: {
28052             getValue: function (dataView, dataOffset) {
28053                 return String.fromCharCode(dataView.getUint8(dataOffset));
28054             },
28055             size: 1,
28056             ascii: true
28057         },
28058         // short, 16 bit int:
28059         3: {
28060             getValue: function (dataView, dataOffset, littleEndian) {
28061                 return dataView.getUint16(dataOffset, littleEndian);
28062             },
28063             size: 2
28064         },
28065         // long, 32 bit int:
28066         4: {
28067             getValue: function (dataView, dataOffset, littleEndian) {
28068                 return dataView.getUint32(dataOffset, littleEndian);
28069             },
28070             size: 4
28071         },
28072         // rational = two long values, first is numerator, second is denominator:
28073         5: {
28074             getValue: function (dataView, dataOffset, littleEndian) {
28075                 return dataView.getUint32(dataOffset, littleEndian) /
28076                     dataView.getUint32(dataOffset + 4, littleEndian);
28077             },
28078             size: 8
28079         },
28080         // slong, 32 bit signed int:
28081         9: {
28082             getValue: function (dataView, dataOffset, littleEndian) {
28083                 return dataView.getInt32(dataOffset, littleEndian);
28084             },
28085             size: 4
28086         },
28087         // srational, two slongs, first is numerator, second is denominator:
28088         10: {
28089             getValue: function (dataView, dataOffset, littleEndian) {
28090                 return dataView.getInt32(dataOffset, littleEndian) /
28091                     dataView.getInt32(dataOffset + 4, littleEndian);
28092             },
28093             size: 8
28094         }
28095     },
28096     
28097     footer : {
28098         STANDARD : [
28099             {
28100                 tag : 'div',
28101                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28102                 action : 'rotate-left',
28103                 cn : [
28104                     {
28105                         tag : 'button',
28106                         cls : 'btn btn-default',
28107                         html : '<i class="fa fa-undo"></i>'
28108                     }
28109                 ]
28110             },
28111             {
28112                 tag : 'div',
28113                 cls : 'btn-group roo-upload-cropbox-picture',
28114                 action : 'picture',
28115                 cn : [
28116                     {
28117                         tag : 'button',
28118                         cls : 'btn btn-default',
28119                         html : '<i class="fa fa-picture-o"></i>'
28120                     }
28121                 ]
28122             },
28123             {
28124                 tag : 'div',
28125                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28126                 action : 'rotate-right',
28127                 cn : [
28128                     {
28129                         tag : 'button',
28130                         cls : 'btn btn-default',
28131                         html : '<i class="fa fa-repeat"></i>'
28132                     }
28133                 ]
28134             }
28135         ],
28136         DOCUMENT : [
28137             {
28138                 tag : 'div',
28139                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28140                 action : 'rotate-left',
28141                 cn : [
28142                     {
28143                         tag : 'button',
28144                         cls : 'btn btn-default',
28145                         html : '<i class="fa fa-undo"></i>'
28146                     }
28147                 ]
28148             },
28149             {
28150                 tag : 'div',
28151                 cls : 'btn-group roo-upload-cropbox-download',
28152                 action : 'download',
28153                 cn : [
28154                     {
28155                         tag : 'button',
28156                         cls : 'btn btn-default',
28157                         html : '<i class="fa fa-download"></i>'
28158                     }
28159                 ]
28160             },
28161             {
28162                 tag : 'div',
28163                 cls : 'btn-group roo-upload-cropbox-crop',
28164                 action : 'crop',
28165                 cn : [
28166                     {
28167                         tag : 'button',
28168                         cls : 'btn btn-default',
28169                         html : '<i class="fa fa-crop"></i>'
28170                     }
28171                 ]
28172             },
28173             {
28174                 tag : 'div',
28175                 cls : 'btn-group roo-upload-cropbox-trash',
28176                 action : 'trash',
28177                 cn : [
28178                     {
28179                         tag : 'button',
28180                         cls : 'btn btn-default',
28181                         html : '<i class="fa fa-trash"></i>'
28182                     }
28183                 ]
28184             },
28185             {
28186                 tag : 'div',
28187                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28188                 action : 'rotate-right',
28189                 cn : [
28190                     {
28191                         tag : 'button',
28192                         cls : 'btn btn-default',
28193                         html : '<i class="fa fa-repeat"></i>'
28194                     }
28195                 ]
28196             }
28197         ],
28198         ROTATOR : [
28199             {
28200                 tag : 'div',
28201                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28202                 action : 'rotate-left',
28203                 cn : [
28204                     {
28205                         tag : 'button',
28206                         cls : 'btn btn-default',
28207                         html : '<i class="fa fa-undo"></i>'
28208                     }
28209                 ]
28210             },
28211             {
28212                 tag : 'div',
28213                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28214                 action : 'rotate-right',
28215                 cn : [
28216                     {
28217                         tag : 'button',
28218                         cls : 'btn btn-default',
28219                         html : '<i class="fa fa-repeat"></i>'
28220                     }
28221                 ]
28222             }
28223         ]
28224     }
28225 });
28226
28227 /*
28228 * Licence: LGPL
28229 */
28230
28231 /**
28232  * @class Roo.bootstrap.DocumentManager
28233  * @extends Roo.bootstrap.Component
28234  * Bootstrap DocumentManager class
28235  * @cfg {String} paramName default 'imageUpload'
28236  * @cfg {String} toolTipName default 'filename'
28237  * @cfg {String} method default POST
28238  * @cfg {String} url action url
28239  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28240  * @cfg {Boolean} multiple multiple upload default true
28241  * @cfg {Number} thumbSize default 300
28242  * @cfg {String} fieldLabel
28243  * @cfg {Number} labelWidth default 4
28244  * @cfg {String} labelAlign (left|top) default left
28245  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28246 * @cfg {Number} labellg set the width of label (1-12)
28247  * @cfg {Number} labelmd set the width of label (1-12)
28248  * @cfg {Number} labelsm set the width of label (1-12)
28249  * @cfg {Number} labelxs set the width of label (1-12)
28250  * 
28251  * @constructor
28252  * Create a new DocumentManager
28253  * @param {Object} config The config object
28254  */
28255
28256 Roo.bootstrap.DocumentManager = function(config){
28257     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28258     
28259     this.files = [];
28260     this.delegates = [];
28261     
28262     this.addEvents({
28263         /**
28264          * @event initial
28265          * Fire when initial the DocumentManager
28266          * @param {Roo.bootstrap.DocumentManager} this
28267          */
28268         "initial" : true,
28269         /**
28270          * @event inspect
28271          * inspect selected file
28272          * @param {Roo.bootstrap.DocumentManager} this
28273          * @param {File} file
28274          */
28275         "inspect" : true,
28276         /**
28277          * @event exception
28278          * Fire when xhr load exception
28279          * @param {Roo.bootstrap.DocumentManager} this
28280          * @param {XMLHttpRequest} xhr
28281          */
28282         "exception" : true,
28283         /**
28284          * @event afterupload
28285          * Fire when xhr load exception
28286          * @param {Roo.bootstrap.DocumentManager} this
28287          * @param {XMLHttpRequest} xhr
28288          */
28289         "afterupload" : true,
28290         /**
28291          * @event prepare
28292          * prepare the form data
28293          * @param {Roo.bootstrap.DocumentManager} this
28294          * @param {Object} formData
28295          */
28296         "prepare" : true,
28297         /**
28298          * @event remove
28299          * Fire when remove the file
28300          * @param {Roo.bootstrap.DocumentManager} this
28301          * @param {Object} file
28302          */
28303         "remove" : true,
28304         /**
28305          * @event refresh
28306          * Fire after refresh the file
28307          * @param {Roo.bootstrap.DocumentManager} this
28308          */
28309         "refresh" : true,
28310         /**
28311          * @event click
28312          * Fire after click the image
28313          * @param {Roo.bootstrap.DocumentManager} this
28314          * @param {Object} file
28315          */
28316         "click" : true,
28317         /**
28318          * @event edit
28319          * Fire when upload a image and editable set to true
28320          * @param {Roo.bootstrap.DocumentManager} this
28321          * @param {Object} file
28322          */
28323         "edit" : true,
28324         /**
28325          * @event beforeselectfile
28326          * Fire before select file
28327          * @param {Roo.bootstrap.DocumentManager} this
28328          */
28329         "beforeselectfile" : true,
28330         /**
28331          * @event process
28332          * Fire before process file
28333          * @param {Roo.bootstrap.DocumentManager} this
28334          * @param {Object} file
28335          */
28336         "process" : true
28337         
28338     });
28339 };
28340
28341 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28342     
28343     boxes : 0,
28344     inputName : '',
28345     thumbSize : 300,
28346     multiple : true,
28347     files : false,
28348     method : 'POST',
28349     url : '',
28350     paramName : 'imageUpload',
28351     toolTipName : 'filename',
28352     fieldLabel : '',
28353     labelWidth : 4,
28354     labelAlign : 'left',
28355     editable : true,
28356     delegates : false,
28357     xhr : false, 
28358     
28359     labellg : 0,
28360     labelmd : 0,
28361     labelsm : 0,
28362     labelxs : 0,
28363     
28364     getAutoCreate : function()
28365     {   
28366         var managerWidget = {
28367             tag : 'div',
28368             cls : 'roo-document-manager',
28369             cn : [
28370                 {
28371                     tag : 'input',
28372                     cls : 'roo-document-manager-selector',
28373                     type : 'file'
28374                 },
28375                 {
28376                     tag : 'div',
28377                     cls : 'roo-document-manager-uploader',
28378                     cn : [
28379                         {
28380                             tag : 'div',
28381                             cls : 'roo-document-manager-upload-btn',
28382                             html : '<i class="fa fa-plus"></i>'
28383                         }
28384                     ]
28385                     
28386                 }
28387             ]
28388         };
28389         
28390         var content = [
28391             {
28392                 tag : 'div',
28393                 cls : 'column col-md-12',
28394                 cn : managerWidget
28395             }
28396         ];
28397         
28398         if(this.fieldLabel.length){
28399             
28400             content = [
28401                 {
28402                     tag : 'div',
28403                     cls : 'column col-md-12',
28404                     html : this.fieldLabel
28405                 },
28406                 {
28407                     tag : 'div',
28408                     cls : 'column col-md-12',
28409                     cn : managerWidget
28410                 }
28411             ];
28412
28413             if(this.labelAlign == 'left'){
28414                 content = [
28415                     {
28416                         tag : 'div',
28417                         cls : 'column',
28418                         html : this.fieldLabel
28419                     },
28420                     {
28421                         tag : 'div',
28422                         cls : 'column',
28423                         cn : managerWidget
28424                     }
28425                 ];
28426                 
28427                 if(this.labelWidth > 12){
28428                     content[0].style = "width: " + this.labelWidth + 'px';
28429                 }
28430
28431                 if(this.labelWidth < 13 && this.labelmd == 0){
28432                     this.labelmd = this.labelWidth;
28433                 }
28434
28435                 if(this.labellg > 0){
28436                     content[0].cls += ' col-lg-' + this.labellg;
28437                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28438                 }
28439
28440                 if(this.labelmd > 0){
28441                     content[0].cls += ' col-md-' + this.labelmd;
28442                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28443                 }
28444
28445                 if(this.labelsm > 0){
28446                     content[0].cls += ' col-sm-' + this.labelsm;
28447                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28448                 }
28449
28450                 if(this.labelxs > 0){
28451                     content[0].cls += ' col-xs-' + this.labelxs;
28452                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28453                 }
28454                 
28455             }
28456         }
28457         
28458         var cfg = {
28459             tag : 'div',
28460             cls : 'row clearfix',
28461             cn : content
28462         };
28463         
28464         return cfg;
28465         
28466     },
28467     
28468     initEvents : function()
28469     {
28470         this.managerEl = this.el.select('.roo-document-manager', true).first();
28471         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28472         
28473         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28474         this.selectorEl.hide();
28475         
28476         if(this.multiple){
28477             this.selectorEl.attr('multiple', 'multiple');
28478         }
28479         
28480         this.selectorEl.on('change', this.onFileSelected, this);
28481         
28482         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28483         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28484         
28485         this.uploader.on('click', this.onUploaderClick, this);
28486         
28487         this.renderProgressDialog();
28488         
28489         var _this = this;
28490         
28491         window.addEventListener("resize", function() { _this.refresh(); } );
28492         
28493         this.fireEvent('initial', this);
28494     },
28495     
28496     renderProgressDialog : function()
28497     {
28498         var _this = this;
28499         
28500         this.progressDialog = new Roo.bootstrap.Modal({
28501             cls : 'roo-document-manager-progress-dialog',
28502             allow_close : false,
28503             title : '',
28504             buttons : [
28505                 {
28506                     name  :'cancel',
28507                     weight : 'danger',
28508                     html : 'Cancel'
28509                 }
28510             ], 
28511             listeners : { 
28512                 btnclick : function() {
28513                     _this.uploadCancel();
28514                     this.hide();
28515                 }
28516             }
28517         });
28518          
28519         this.progressDialog.render(Roo.get(document.body));
28520          
28521         this.progress = new Roo.bootstrap.Progress({
28522             cls : 'roo-document-manager-progress',
28523             active : true,
28524             striped : true
28525         });
28526         
28527         this.progress.render(this.progressDialog.getChildContainer());
28528         
28529         this.progressBar = new Roo.bootstrap.ProgressBar({
28530             cls : 'roo-document-manager-progress-bar',
28531             aria_valuenow : 0,
28532             aria_valuemin : 0,
28533             aria_valuemax : 12,
28534             panel : 'success'
28535         });
28536         
28537         this.progressBar.render(this.progress.getChildContainer());
28538     },
28539     
28540     onUploaderClick : function(e)
28541     {
28542         e.preventDefault();
28543      
28544         if(this.fireEvent('beforeselectfile', this) != false){
28545             this.selectorEl.dom.click();
28546         }
28547         
28548     },
28549     
28550     onFileSelected : function(e)
28551     {
28552         e.preventDefault();
28553         
28554         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28555             return;
28556         }
28557         
28558         Roo.each(this.selectorEl.dom.files, function(file){
28559             if(this.fireEvent('inspect', this, file) != false){
28560                 this.files.push(file);
28561             }
28562         }, this);
28563         
28564         this.queue();
28565         
28566     },
28567     
28568     queue : function()
28569     {
28570         this.selectorEl.dom.value = '';
28571         
28572         if(!this.files.length){
28573             return;
28574         }
28575         
28576         if(this.boxes > 0 && this.files.length > this.boxes){
28577             this.files = this.files.slice(0, this.boxes);
28578         }
28579         
28580         this.uploader.show();
28581         
28582         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28583             this.uploader.hide();
28584         }
28585         
28586         var _this = this;
28587         
28588         var files = [];
28589         
28590         var docs = [];
28591         
28592         Roo.each(this.files, function(file){
28593             
28594             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28595                 var f = this.renderPreview(file);
28596                 files.push(f);
28597                 return;
28598             }
28599             
28600             if(file.type.indexOf('image') != -1){
28601                 this.delegates.push(
28602                     (function(){
28603                         _this.process(file);
28604                     }).createDelegate(this)
28605                 );
28606         
28607                 return;
28608             }
28609             
28610             docs.push(
28611                 (function(){
28612                     _this.process(file);
28613                 }).createDelegate(this)
28614             );
28615             
28616         }, this);
28617         
28618         this.files = files;
28619         
28620         this.delegates = this.delegates.concat(docs);
28621         
28622         if(!this.delegates.length){
28623             this.refresh();
28624             return;
28625         }
28626         
28627         this.progressBar.aria_valuemax = this.delegates.length;
28628         
28629         this.arrange();
28630         
28631         return;
28632     },
28633     
28634     arrange : function()
28635     {
28636         if(!this.delegates.length){
28637             this.progressDialog.hide();
28638             this.refresh();
28639             return;
28640         }
28641         
28642         var delegate = this.delegates.shift();
28643         
28644         this.progressDialog.show();
28645         
28646         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28647         
28648         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28649         
28650         delegate();
28651     },
28652     
28653     refresh : function()
28654     {
28655         this.uploader.show();
28656         
28657         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28658             this.uploader.hide();
28659         }
28660         
28661         Roo.isTouch ? this.closable(false) : this.closable(true);
28662         
28663         this.fireEvent('refresh', this);
28664     },
28665     
28666     onRemove : function(e, el, o)
28667     {
28668         e.preventDefault();
28669         
28670         this.fireEvent('remove', this, o);
28671         
28672     },
28673     
28674     remove : function(o)
28675     {
28676         var files = [];
28677         
28678         Roo.each(this.files, function(file){
28679             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28680                 files.push(file);
28681                 return;
28682             }
28683
28684             o.target.remove();
28685
28686         }, this);
28687         
28688         this.files = files;
28689         
28690         this.refresh();
28691     },
28692     
28693     clear : function()
28694     {
28695         Roo.each(this.files, function(file){
28696             if(!file.target){
28697                 return;
28698             }
28699             
28700             file.target.remove();
28701
28702         }, this);
28703         
28704         this.files = [];
28705         
28706         this.refresh();
28707     },
28708     
28709     onClick : function(e, el, o)
28710     {
28711         e.preventDefault();
28712         
28713         this.fireEvent('click', this, o);
28714         
28715     },
28716     
28717     closable : function(closable)
28718     {
28719         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28720             
28721             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28722             
28723             if(closable){
28724                 el.show();
28725                 return;
28726             }
28727             
28728             el.hide();
28729             
28730         }, this);
28731     },
28732     
28733     xhrOnLoad : function(xhr)
28734     {
28735         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28736             el.remove();
28737         }, this);
28738         
28739         if (xhr.readyState !== 4) {
28740             this.arrange();
28741             this.fireEvent('exception', this, xhr);
28742             return;
28743         }
28744
28745         var response = Roo.decode(xhr.responseText);
28746         
28747         if(!response.success){
28748             this.arrange();
28749             this.fireEvent('exception', this, xhr);
28750             return;
28751         }
28752         
28753         var file = this.renderPreview(response.data);
28754         
28755         this.files.push(file);
28756         
28757         this.arrange();
28758         
28759         this.fireEvent('afterupload', this, xhr);
28760         
28761     },
28762     
28763     xhrOnError : function(xhr)
28764     {
28765         Roo.log('xhr on error');
28766         
28767         var response = Roo.decode(xhr.responseText);
28768           
28769         Roo.log(response);
28770         
28771         this.arrange();
28772     },
28773     
28774     process : function(file)
28775     {
28776         if(this.fireEvent('process', this, file) !== false){
28777             if(this.editable && file.type.indexOf('image') != -1){
28778                 this.fireEvent('edit', this, file);
28779                 return;
28780             }
28781
28782             this.uploadStart(file, false);
28783
28784             return;
28785         }
28786         
28787     },
28788     
28789     uploadStart : function(file, crop)
28790     {
28791         this.xhr = new XMLHttpRequest();
28792         
28793         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28794             this.arrange();
28795             return;
28796         }
28797         
28798         file.xhr = this.xhr;
28799             
28800         this.managerEl.createChild({
28801             tag : 'div',
28802             cls : 'roo-document-manager-loading',
28803             cn : [
28804                 {
28805                     tag : 'div',
28806                     tooltip : file.name,
28807                     cls : 'roo-document-manager-thumb',
28808                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28809                 }
28810             ]
28811
28812         });
28813
28814         this.xhr.open(this.method, this.url, true);
28815         
28816         var headers = {
28817             "Accept": "application/json",
28818             "Cache-Control": "no-cache",
28819             "X-Requested-With": "XMLHttpRequest"
28820         };
28821         
28822         for (var headerName in headers) {
28823             var headerValue = headers[headerName];
28824             if (headerValue) {
28825                 this.xhr.setRequestHeader(headerName, headerValue);
28826             }
28827         }
28828         
28829         var _this = this;
28830         
28831         this.xhr.onload = function()
28832         {
28833             _this.xhrOnLoad(_this.xhr);
28834         }
28835         
28836         this.xhr.onerror = function()
28837         {
28838             _this.xhrOnError(_this.xhr);
28839         }
28840         
28841         var formData = new FormData();
28842
28843         formData.append('returnHTML', 'NO');
28844         
28845         if(crop){
28846             formData.append('crop', crop);
28847         }
28848         
28849         formData.append(this.paramName, file, file.name);
28850         
28851         var options = {
28852             file : file, 
28853             manually : false
28854         };
28855         
28856         if(this.fireEvent('prepare', this, formData, options) != false){
28857             
28858             if(options.manually){
28859                 return;
28860             }
28861             
28862             this.xhr.send(formData);
28863             return;
28864         };
28865         
28866         this.uploadCancel();
28867     },
28868     
28869     uploadCancel : function()
28870     {
28871         if (this.xhr) {
28872             this.xhr.abort();
28873         }
28874         
28875         this.delegates = [];
28876         
28877         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28878             el.remove();
28879         }, this);
28880         
28881         this.arrange();
28882     },
28883     
28884     renderPreview : function(file)
28885     {
28886         if(typeof(file.target) != 'undefined' && file.target){
28887             return file;
28888         }
28889         
28890         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28891         
28892         var previewEl = this.managerEl.createChild({
28893             tag : 'div',
28894             cls : 'roo-document-manager-preview',
28895             cn : [
28896                 {
28897                     tag : 'div',
28898                     tooltip : file[this.toolTipName],
28899                     cls : 'roo-document-manager-thumb',
28900                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28901                 },
28902                 {
28903                     tag : 'button',
28904                     cls : 'close',
28905                     html : '<i class="fa fa-times-circle"></i>'
28906                 }
28907             ]
28908         });
28909
28910         var close = previewEl.select('button.close', true).first();
28911
28912         close.on('click', this.onRemove, this, file);
28913
28914         file.target = previewEl;
28915
28916         var image = previewEl.select('img', true).first();
28917         
28918         var _this = this;
28919         
28920         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28921         
28922         image.on('click', this.onClick, this, file);
28923         
28924         return file;
28925         
28926     },
28927     
28928     onPreviewLoad : function(file, image)
28929     {
28930         if(typeof(file.target) == 'undefined' || !file.target){
28931             return;
28932         }
28933         
28934         var width = image.dom.naturalWidth || image.dom.width;
28935         var height = image.dom.naturalHeight || image.dom.height;
28936         
28937         if(width > height){
28938             file.target.addClass('wide');
28939             return;
28940         }
28941         
28942         file.target.addClass('tall');
28943         return;
28944         
28945     },
28946     
28947     uploadFromSource : function(file, crop)
28948     {
28949         this.xhr = new XMLHttpRequest();
28950         
28951         this.managerEl.createChild({
28952             tag : 'div',
28953             cls : 'roo-document-manager-loading',
28954             cn : [
28955                 {
28956                     tag : 'div',
28957                     tooltip : file.name,
28958                     cls : 'roo-document-manager-thumb',
28959                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28960                 }
28961             ]
28962
28963         });
28964
28965         this.xhr.open(this.method, this.url, true);
28966         
28967         var headers = {
28968             "Accept": "application/json",
28969             "Cache-Control": "no-cache",
28970             "X-Requested-With": "XMLHttpRequest"
28971         };
28972         
28973         for (var headerName in headers) {
28974             var headerValue = headers[headerName];
28975             if (headerValue) {
28976                 this.xhr.setRequestHeader(headerName, headerValue);
28977             }
28978         }
28979         
28980         var _this = this;
28981         
28982         this.xhr.onload = function()
28983         {
28984             _this.xhrOnLoad(_this.xhr);
28985         }
28986         
28987         this.xhr.onerror = function()
28988         {
28989             _this.xhrOnError(_this.xhr);
28990         }
28991         
28992         var formData = new FormData();
28993
28994         formData.append('returnHTML', 'NO');
28995         
28996         formData.append('crop', crop);
28997         
28998         if(typeof(file.filename) != 'undefined'){
28999             formData.append('filename', file.filename);
29000         }
29001         
29002         if(typeof(file.mimetype) != 'undefined'){
29003             formData.append('mimetype', file.mimetype);
29004         }
29005         
29006         Roo.log(formData);
29007         
29008         if(this.fireEvent('prepare', this, formData) != false){
29009             this.xhr.send(formData);
29010         };
29011     }
29012 });
29013
29014 /*
29015 * Licence: LGPL
29016 */
29017
29018 /**
29019  * @class Roo.bootstrap.DocumentViewer
29020  * @extends Roo.bootstrap.Component
29021  * Bootstrap DocumentViewer class
29022  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29023  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29024  * 
29025  * @constructor
29026  * Create a new DocumentViewer
29027  * @param {Object} config The config object
29028  */
29029
29030 Roo.bootstrap.DocumentViewer = function(config){
29031     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29032     
29033     this.addEvents({
29034         /**
29035          * @event initial
29036          * Fire after initEvent
29037          * @param {Roo.bootstrap.DocumentViewer} this
29038          */
29039         "initial" : true,
29040         /**
29041          * @event click
29042          * Fire after click
29043          * @param {Roo.bootstrap.DocumentViewer} this
29044          */
29045         "click" : true,
29046         /**
29047          * @event download
29048          * Fire after download button
29049          * @param {Roo.bootstrap.DocumentViewer} this
29050          */
29051         "download" : true,
29052         /**
29053          * @event trash
29054          * Fire after trash button
29055          * @param {Roo.bootstrap.DocumentViewer} this
29056          */
29057         "trash" : true
29058         
29059     });
29060 };
29061
29062 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29063     
29064     showDownload : true,
29065     
29066     showTrash : true,
29067     
29068     getAutoCreate : function()
29069     {
29070         var cfg = {
29071             tag : 'div',
29072             cls : 'roo-document-viewer',
29073             cn : [
29074                 {
29075                     tag : 'div',
29076                     cls : 'roo-document-viewer-body',
29077                     cn : [
29078                         {
29079                             tag : 'div',
29080                             cls : 'roo-document-viewer-thumb',
29081                             cn : [
29082                                 {
29083                                     tag : 'img',
29084                                     cls : 'roo-document-viewer-image'
29085                                 }
29086                             ]
29087                         }
29088                     ]
29089                 },
29090                 {
29091                     tag : 'div',
29092                     cls : 'roo-document-viewer-footer',
29093                     cn : {
29094                         tag : 'div',
29095                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29096                         cn : [
29097                             {
29098                                 tag : 'div',
29099                                 cls : 'btn-group roo-document-viewer-download',
29100                                 cn : [
29101                                     {
29102                                         tag : 'button',
29103                                         cls : 'btn btn-default',
29104                                         html : '<i class="fa fa-download"></i>'
29105                                     }
29106                                 ]
29107                             },
29108                             {
29109                                 tag : 'div',
29110                                 cls : 'btn-group roo-document-viewer-trash',
29111                                 cn : [
29112                                     {
29113                                         tag : 'button',
29114                                         cls : 'btn btn-default',
29115                                         html : '<i class="fa fa-trash"></i>'
29116                                     }
29117                                 ]
29118                             }
29119                         ]
29120                     }
29121                 }
29122             ]
29123         };
29124         
29125         return cfg;
29126     },
29127     
29128     initEvents : function()
29129     {
29130         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29131         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29132         
29133         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29134         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29135         
29136         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29137         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29138         
29139         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29140         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29141         
29142         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29143         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29144         
29145         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29146         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29147         
29148         this.bodyEl.on('click', this.onClick, this);
29149         this.downloadBtn.on('click', this.onDownload, this);
29150         this.trashBtn.on('click', this.onTrash, this);
29151         
29152         this.downloadBtn.hide();
29153         this.trashBtn.hide();
29154         
29155         if(this.showDownload){
29156             this.downloadBtn.show();
29157         }
29158         
29159         if(this.showTrash){
29160             this.trashBtn.show();
29161         }
29162         
29163         if(!this.showDownload && !this.showTrash) {
29164             this.footerEl.hide();
29165         }
29166         
29167     },
29168     
29169     initial : function()
29170     {
29171         this.fireEvent('initial', this);
29172         
29173     },
29174     
29175     onClick : function(e)
29176     {
29177         e.preventDefault();
29178         
29179         this.fireEvent('click', this);
29180     },
29181     
29182     onDownload : function(e)
29183     {
29184         e.preventDefault();
29185         
29186         this.fireEvent('download', this);
29187     },
29188     
29189     onTrash : function(e)
29190     {
29191         e.preventDefault();
29192         
29193         this.fireEvent('trash', this);
29194     }
29195     
29196 });
29197 /*
29198  * - LGPL
29199  *
29200  * nav progress bar
29201  * 
29202  */
29203
29204 /**
29205  * @class Roo.bootstrap.NavProgressBar
29206  * @extends Roo.bootstrap.Component
29207  * Bootstrap NavProgressBar class
29208  * 
29209  * @constructor
29210  * Create a new nav progress bar
29211  * @param {Object} config The config object
29212  */
29213
29214 Roo.bootstrap.NavProgressBar = function(config){
29215     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29216
29217     this.bullets = this.bullets || [];
29218    
29219 //    Roo.bootstrap.NavProgressBar.register(this);
29220      this.addEvents({
29221         /**
29222              * @event changed
29223              * Fires when the active item changes
29224              * @param {Roo.bootstrap.NavProgressBar} this
29225              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29226              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29227          */
29228         'changed': true
29229      });
29230     
29231 };
29232
29233 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29234     
29235     bullets : [],
29236     barItems : [],
29237     
29238     getAutoCreate : function()
29239     {
29240         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29241         
29242         cfg = {
29243             tag : 'div',
29244             cls : 'roo-navigation-bar-group',
29245             cn : [
29246                 {
29247                     tag : 'div',
29248                     cls : 'roo-navigation-top-bar'
29249                 },
29250                 {
29251                     tag : 'div',
29252                     cls : 'roo-navigation-bullets-bar',
29253                     cn : [
29254                         {
29255                             tag : 'ul',
29256                             cls : 'roo-navigation-bar'
29257                         }
29258                     ]
29259                 },
29260                 
29261                 {
29262                     tag : 'div',
29263                     cls : 'roo-navigation-bottom-bar'
29264                 }
29265             ]
29266             
29267         };
29268         
29269         return cfg;
29270         
29271     },
29272     
29273     initEvents: function() 
29274     {
29275         
29276     },
29277     
29278     onRender : function(ct, position) 
29279     {
29280         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29281         
29282         if(this.bullets.length){
29283             Roo.each(this.bullets, function(b){
29284                this.addItem(b);
29285             }, this);
29286         }
29287         
29288         this.format();
29289         
29290     },
29291     
29292     addItem : function(cfg)
29293     {
29294         var item = new Roo.bootstrap.NavProgressItem(cfg);
29295         
29296         item.parentId = this.id;
29297         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29298         
29299         if(cfg.html){
29300             var top = new Roo.bootstrap.Element({
29301                 tag : 'div',
29302                 cls : 'roo-navigation-bar-text'
29303             });
29304             
29305             var bottom = new Roo.bootstrap.Element({
29306                 tag : 'div',
29307                 cls : 'roo-navigation-bar-text'
29308             });
29309             
29310             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29311             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29312             
29313             var topText = new Roo.bootstrap.Element({
29314                 tag : 'span',
29315                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29316             });
29317             
29318             var bottomText = new Roo.bootstrap.Element({
29319                 tag : 'span',
29320                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29321             });
29322             
29323             topText.onRender(top.el, null);
29324             bottomText.onRender(bottom.el, null);
29325             
29326             item.topEl = top;
29327             item.bottomEl = bottom;
29328         }
29329         
29330         this.barItems.push(item);
29331         
29332         return item;
29333     },
29334     
29335     getActive : function()
29336     {
29337         var active = false;
29338         
29339         Roo.each(this.barItems, function(v){
29340             
29341             if (!v.isActive()) {
29342                 return;
29343             }
29344             
29345             active = v;
29346             return false;
29347             
29348         });
29349         
29350         return active;
29351     },
29352     
29353     setActiveItem : function(item)
29354     {
29355         var prev = false;
29356         
29357         Roo.each(this.barItems, function(v){
29358             if (v.rid == item.rid) {
29359                 return ;
29360             }
29361             
29362             if (v.isActive()) {
29363                 v.setActive(false);
29364                 prev = v;
29365             }
29366         });
29367
29368         item.setActive(true);
29369         
29370         this.fireEvent('changed', this, item, prev);
29371     },
29372     
29373     getBarItem: function(rid)
29374     {
29375         var ret = false;
29376         
29377         Roo.each(this.barItems, function(e) {
29378             if (e.rid != rid) {
29379                 return;
29380             }
29381             
29382             ret =  e;
29383             return false;
29384         });
29385         
29386         return ret;
29387     },
29388     
29389     indexOfItem : function(item)
29390     {
29391         var index = false;
29392         
29393         Roo.each(this.barItems, function(v, i){
29394             
29395             if (v.rid != item.rid) {
29396                 return;
29397             }
29398             
29399             index = i;
29400             return false
29401         });
29402         
29403         return index;
29404     },
29405     
29406     setActiveNext : function()
29407     {
29408         var i = this.indexOfItem(this.getActive());
29409         
29410         if (i > this.barItems.length) {
29411             return;
29412         }
29413         
29414         this.setActiveItem(this.barItems[i+1]);
29415     },
29416     
29417     setActivePrev : function()
29418     {
29419         var i = this.indexOfItem(this.getActive());
29420         
29421         if (i  < 1) {
29422             return;
29423         }
29424         
29425         this.setActiveItem(this.barItems[i-1]);
29426     },
29427     
29428     format : function()
29429     {
29430         if(!this.barItems.length){
29431             return;
29432         }
29433      
29434         var width = 100 / this.barItems.length;
29435         
29436         Roo.each(this.barItems, function(i){
29437             i.el.setStyle('width', width + '%');
29438             i.topEl.el.setStyle('width', width + '%');
29439             i.bottomEl.el.setStyle('width', width + '%');
29440         }, this);
29441         
29442     }
29443     
29444 });
29445 /*
29446  * - LGPL
29447  *
29448  * Nav Progress Item
29449  * 
29450  */
29451
29452 /**
29453  * @class Roo.bootstrap.NavProgressItem
29454  * @extends Roo.bootstrap.Component
29455  * Bootstrap NavProgressItem class
29456  * @cfg {String} rid the reference id
29457  * @cfg {Boolean} active (true|false) Is item active default false
29458  * @cfg {Boolean} disabled (true|false) Is item active default false
29459  * @cfg {String} html
29460  * @cfg {String} position (top|bottom) text position default bottom
29461  * @cfg {String} icon show icon instead of number
29462  * 
29463  * @constructor
29464  * Create a new NavProgressItem
29465  * @param {Object} config The config object
29466  */
29467 Roo.bootstrap.NavProgressItem = function(config){
29468     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29469     this.addEvents({
29470         // raw events
29471         /**
29472          * @event click
29473          * The raw click event for the entire grid.
29474          * @param {Roo.bootstrap.NavProgressItem} this
29475          * @param {Roo.EventObject} e
29476          */
29477         "click" : true
29478     });
29479    
29480 };
29481
29482 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29483     
29484     rid : '',
29485     active : false,
29486     disabled : false,
29487     html : '',
29488     position : 'bottom',
29489     icon : false,
29490     
29491     getAutoCreate : function()
29492     {
29493         var iconCls = 'roo-navigation-bar-item-icon';
29494         
29495         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29496         
29497         var cfg = {
29498             tag: 'li',
29499             cls: 'roo-navigation-bar-item',
29500             cn : [
29501                 {
29502                     tag : 'i',
29503                     cls : iconCls
29504                 }
29505             ]
29506         };
29507         
29508         if(this.active){
29509             cfg.cls += ' active';
29510         }
29511         if(this.disabled){
29512             cfg.cls += ' disabled';
29513         }
29514         
29515         return cfg;
29516     },
29517     
29518     disable : function()
29519     {
29520         this.setDisabled(true);
29521     },
29522     
29523     enable : function()
29524     {
29525         this.setDisabled(false);
29526     },
29527     
29528     initEvents: function() 
29529     {
29530         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29531         
29532         this.iconEl.on('click', this.onClick, this);
29533     },
29534     
29535     onClick : function(e)
29536     {
29537         e.preventDefault();
29538         
29539         if(this.disabled){
29540             return;
29541         }
29542         
29543         if(this.fireEvent('click', this, e) === false){
29544             return;
29545         };
29546         
29547         this.parent().setActiveItem(this);
29548     },
29549     
29550     isActive: function () 
29551     {
29552         return this.active;
29553     },
29554     
29555     setActive : function(state)
29556     {
29557         if(this.active == state){
29558             return;
29559         }
29560         
29561         this.active = state;
29562         
29563         if (state) {
29564             this.el.addClass('active');
29565             return;
29566         }
29567         
29568         this.el.removeClass('active');
29569         
29570         return;
29571     },
29572     
29573     setDisabled : function(state)
29574     {
29575         if(this.disabled == state){
29576             return;
29577         }
29578         
29579         this.disabled = state;
29580         
29581         if (state) {
29582             this.el.addClass('disabled');
29583             return;
29584         }
29585         
29586         this.el.removeClass('disabled');
29587     },
29588     
29589     tooltipEl : function()
29590     {
29591         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29592     }
29593 });
29594  
29595
29596  /*
29597  * - LGPL
29598  *
29599  * FieldLabel
29600  * 
29601  */
29602
29603 /**
29604  * @class Roo.bootstrap.FieldLabel
29605  * @extends Roo.bootstrap.Component
29606  * Bootstrap FieldLabel class
29607  * @cfg {String} html contents of the element
29608  * @cfg {String} tag tag of the element default label
29609  * @cfg {String} cls class of the element
29610  * @cfg {String} target label target 
29611  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29612  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29613  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29614  * @cfg {String} iconTooltip default "This field is required"
29615  * 
29616  * @constructor
29617  * Create a new FieldLabel
29618  * @param {Object} config The config object
29619  */
29620
29621 Roo.bootstrap.FieldLabel = function(config){
29622     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29623     
29624     this.addEvents({
29625             /**
29626              * @event invalid
29627              * Fires after the field has been marked as invalid.
29628              * @param {Roo.form.FieldLabel} this
29629              * @param {String} msg The validation message
29630              */
29631             invalid : true,
29632             /**
29633              * @event valid
29634              * Fires after the field has been validated with no errors.
29635              * @param {Roo.form.FieldLabel} this
29636              */
29637             valid : true
29638         });
29639 };
29640
29641 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29642     
29643     tag: 'label',
29644     cls: '',
29645     html: '',
29646     target: '',
29647     allowBlank : true,
29648     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29649     validClass : 'text-success fa fa-lg fa-check',
29650     iconTooltip : 'This field is required',
29651     
29652     getAutoCreate : function(){
29653         
29654         var cfg = {
29655             tag : this.tag,
29656             cls : 'roo-bootstrap-field-label ' + this.cls,
29657             for : this.target,
29658             cn : [
29659                 {
29660                     tag : 'i',
29661                     cls : '',
29662                     tooltip : this.iconTooltip
29663                 },
29664                 {
29665                     tag : 'span',
29666                     html : this.html
29667                 }
29668             ] 
29669         };
29670         
29671         return cfg;
29672     },
29673     
29674     initEvents: function() 
29675     {
29676         Roo.bootstrap.Element.superclass.initEvents.call(this);
29677         
29678         this.iconEl = this.el.select('i', true).first();
29679         
29680         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29681         
29682         Roo.bootstrap.FieldLabel.register(this);
29683     },
29684     
29685     /**
29686      * Mark this field as valid
29687      */
29688     markValid : function()
29689     {
29690         this.iconEl.show();
29691         
29692         this.iconEl.removeClass(this.invalidClass);
29693         
29694         this.iconEl.addClass(this.validClass);
29695         
29696         this.fireEvent('valid', this);
29697     },
29698     
29699     /**
29700      * Mark this field as invalid
29701      * @param {String} msg The validation message
29702      */
29703     markInvalid : function(msg)
29704     {
29705         this.iconEl.show();
29706         
29707         this.iconEl.removeClass(this.validClass);
29708         
29709         this.iconEl.addClass(this.invalidClass);
29710         
29711         this.fireEvent('invalid', this, msg);
29712     }
29713     
29714    
29715 });
29716
29717 Roo.apply(Roo.bootstrap.FieldLabel, {
29718     
29719     groups: {},
29720     
29721      /**
29722     * register a FieldLabel Group
29723     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29724     */
29725     register : function(label)
29726     {
29727         if(this.groups.hasOwnProperty(label.target)){
29728             return;
29729         }
29730      
29731         this.groups[label.target] = label;
29732         
29733     },
29734     /**
29735     * fetch a FieldLabel Group based on the target
29736     * @param {string} target
29737     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29738     */
29739     get: function(target) {
29740         if (typeof(this.groups[target]) == 'undefined') {
29741             return false;
29742         }
29743         
29744         return this.groups[target] ;
29745     }
29746 });
29747
29748  
29749
29750  /*
29751  * - LGPL
29752  *
29753  * page DateSplitField.
29754  * 
29755  */
29756
29757
29758 /**
29759  * @class Roo.bootstrap.DateSplitField
29760  * @extends Roo.bootstrap.Component
29761  * Bootstrap DateSplitField class
29762  * @cfg {string} fieldLabel - the label associated
29763  * @cfg {Number} labelWidth set the width of label (0-12)
29764  * @cfg {String} labelAlign (top|left)
29765  * @cfg {Boolean} dayAllowBlank (true|false) default false
29766  * @cfg {Boolean} monthAllowBlank (true|false) default false
29767  * @cfg {Boolean} yearAllowBlank (true|false) default false
29768  * @cfg {string} dayPlaceholder 
29769  * @cfg {string} monthPlaceholder
29770  * @cfg {string} yearPlaceholder
29771  * @cfg {string} dayFormat default 'd'
29772  * @cfg {string} monthFormat default 'm'
29773  * @cfg {string} yearFormat default 'Y'
29774  * @cfg {Number} labellg set the width of label (1-12)
29775  * @cfg {Number} labelmd set the width of label (1-12)
29776  * @cfg {Number} labelsm set the width of label (1-12)
29777  * @cfg {Number} labelxs set the width of label (1-12)
29778
29779  *     
29780  * @constructor
29781  * Create a new DateSplitField
29782  * @param {Object} config The config object
29783  */
29784
29785 Roo.bootstrap.DateSplitField = function(config){
29786     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29787     
29788     this.addEvents({
29789         // raw events
29790          /**
29791          * @event years
29792          * getting the data of years
29793          * @param {Roo.bootstrap.DateSplitField} this
29794          * @param {Object} years
29795          */
29796         "years" : true,
29797         /**
29798          * @event days
29799          * getting the data of days
29800          * @param {Roo.bootstrap.DateSplitField} this
29801          * @param {Object} days
29802          */
29803         "days" : true,
29804         /**
29805          * @event invalid
29806          * Fires after the field has been marked as invalid.
29807          * @param {Roo.form.Field} this
29808          * @param {String} msg The validation message
29809          */
29810         invalid : true,
29811        /**
29812          * @event valid
29813          * Fires after the field has been validated with no errors.
29814          * @param {Roo.form.Field} this
29815          */
29816         valid : true
29817     });
29818 };
29819
29820 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29821     
29822     fieldLabel : '',
29823     labelAlign : 'top',
29824     labelWidth : 3,
29825     dayAllowBlank : false,
29826     monthAllowBlank : false,
29827     yearAllowBlank : false,
29828     dayPlaceholder : '',
29829     monthPlaceholder : '',
29830     yearPlaceholder : '',
29831     dayFormat : 'd',
29832     monthFormat : 'm',
29833     yearFormat : 'Y',
29834     isFormField : true,
29835     labellg : 0,
29836     labelmd : 0,
29837     labelsm : 0,
29838     labelxs : 0,
29839     
29840     getAutoCreate : function()
29841     {
29842         var cfg = {
29843             tag : 'div',
29844             cls : 'row roo-date-split-field-group',
29845             cn : [
29846                 {
29847                     tag : 'input',
29848                     type : 'hidden',
29849                     cls : 'form-hidden-field roo-date-split-field-group-value',
29850                     name : this.name
29851                 }
29852             ]
29853         };
29854         
29855         var labelCls = 'col-md-12';
29856         var contentCls = 'col-md-4';
29857         
29858         if(this.fieldLabel){
29859             
29860             var label = {
29861                 tag : 'div',
29862                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29863                 cn : [
29864                     {
29865                         tag : 'label',
29866                         html : this.fieldLabel
29867                     }
29868                 ]
29869             };
29870             
29871             if(this.labelAlign == 'left'){
29872             
29873                 if(this.labelWidth > 12){
29874                     label.style = "width: " + this.labelWidth + 'px';
29875                 }
29876
29877                 if(this.labelWidth < 13 && this.labelmd == 0){
29878                     this.labelmd = this.labelWidth;
29879                 }
29880
29881                 if(this.labellg > 0){
29882                     labelCls = ' col-lg-' + this.labellg;
29883                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29884                 }
29885
29886                 if(this.labelmd > 0){
29887                     labelCls = ' col-md-' + this.labelmd;
29888                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29889                 }
29890
29891                 if(this.labelsm > 0){
29892                     labelCls = ' col-sm-' + this.labelsm;
29893                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29894                 }
29895
29896                 if(this.labelxs > 0){
29897                     labelCls = ' col-xs-' + this.labelxs;
29898                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29899                 }
29900             }
29901             
29902             label.cls += ' ' + labelCls;
29903             
29904             cfg.cn.push(label);
29905         }
29906         
29907         Roo.each(['day', 'month', 'year'], function(t){
29908             cfg.cn.push({
29909                 tag : 'div',
29910                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29911             });
29912         }, this);
29913         
29914         return cfg;
29915     },
29916     
29917     inputEl: function ()
29918     {
29919         return this.el.select('.roo-date-split-field-group-value', true).first();
29920     },
29921     
29922     onRender : function(ct, position) 
29923     {
29924         var _this = this;
29925         
29926         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29927         
29928         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29929         
29930         this.dayField = new Roo.bootstrap.ComboBox({
29931             allowBlank : this.dayAllowBlank,
29932             alwaysQuery : true,
29933             displayField : 'value',
29934             editable : false,
29935             fieldLabel : '',
29936             forceSelection : true,
29937             mode : 'local',
29938             placeholder : this.dayPlaceholder,
29939             selectOnFocus : true,
29940             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29941             triggerAction : 'all',
29942             typeAhead : true,
29943             valueField : 'value',
29944             store : new Roo.data.SimpleStore({
29945                 data : (function() {    
29946                     var days = [];
29947                     _this.fireEvent('days', _this, days);
29948                     return days;
29949                 })(),
29950                 fields : [ 'value' ]
29951             }),
29952             listeners : {
29953                 select : function (_self, record, index)
29954                 {
29955                     _this.setValue(_this.getValue());
29956                 }
29957             }
29958         });
29959
29960         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29961         
29962         this.monthField = new Roo.bootstrap.MonthField({
29963             after : '<i class=\"fa fa-calendar\"></i>',
29964             allowBlank : this.monthAllowBlank,
29965             placeholder : this.monthPlaceholder,
29966             readOnly : true,
29967             listeners : {
29968                 render : function (_self)
29969                 {
29970                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29971                         e.preventDefault();
29972                         _self.focus();
29973                     });
29974                 },
29975                 select : function (_self, oldvalue, newvalue)
29976                 {
29977                     _this.setValue(_this.getValue());
29978                 }
29979             }
29980         });
29981         
29982         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29983         
29984         this.yearField = new Roo.bootstrap.ComboBox({
29985             allowBlank : this.yearAllowBlank,
29986             alwaysQuery : true,
29987             displayField : 'value',
29988             editable : false,
29989             fieldLabel : '',
29990             forceSelection : true,
29991             mode : 'local',
29992             placeholder : this.yearPlaceholder,
29993             selectOnFocus : true,
29994             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29995             triggerAction : 'all',
29996             typeAhead : true,
29997             valueField : 'value',
29998             store : new Roo.data.SimpleStore({
29999                 data : (function() {
30000                     var years = [];
30001                     _this.fireEvent('years', _this, years);
30002                     return years;
30003                 })(),
30004                 fields : [ 'value' ]
30005             }),
30006             listeners : {
30007                 select : function (_self, record, index)
30008                 {
30009                     _this.setValue(_this.getValue());
30010                 }
30011             }
30012         });
30013
30014         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30015     },
30016     
30017     setValue : function(v, format)
30018     {
30019         this.inputEl.dom.value = v;
30020         
30021         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30022         
30023         var d = Date.parseDate(v, f);
30024         
30025         if(!d){
30026             this.validate();
30027             return;
30028         }
30029         
30030         this.setDay(d.format(this.dayFormat));
30031         this.setMonth(d.format(this.monthFormat));
30032         this.setYear(d.format(this.yearFormat));
30033         
30034         this.validate();
30035         
30036         return;
30037     },
30038     
30039     setDay : function(v)
30040     {
30041         this.dayField.setValue(v);
30042         this.inputEl.dom.value = this.getValue();
30043         this.validate();
30044         return;
30045     },
30046     
30047     setMonth : function(v)
30048     {
30049         this.monthField.setValue(v, true);
30050         this.inputEl.dom.value = this.getValue();
30051         this.validate();
30052         return;
30053     },
30054     
30055     setYear : function(v)
30056     {
30057         this.yearField.setValue(v);
30058         this.inputEl.dom.value = this.getValue();
30059         this.validate();
30060         return;
30061     },
30062     
30063     getDay : function()
30064     {
30065         return this.dayField.getValue();
30066     },
30067     
30068     getMonth : function()
30069     {
30070         return this.monthField.getValue();
30071     },
30072     
30073     getYear : function()
30074     {
30075         return this.yearField.getValue();
30076     },
30077     
30078     getValue : function()
30079     {
30080         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30081         
30082         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30083         
30084         return date;
30085     },
30086     
30087     reset : function()
30088     {
30089         this.setDay('');
30090         this.setMonth('');
30091         this.setYear('');
30092         this.inputEl.dom.value = '';
30093         this.validate();
30094         return;
30095     },
30096     
30097     validate : function()
30098     {
30099         var d = this.dayField.validate();
30100         var m = this.monthField.validate();
30101         var y = this.yearField.validate();
30102         
30103         var valid = true;
30104         
30105         if(
30106                 (!this.dayAllowBlank && !d) ||
30107                 (!this.monthAllowBlank && !m) ||
30108                 (!this.yearAllowBlank && !y)
30109         ){
30110             valid = false;
30111         }
30112         
30113         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30114             return valid;
30115         }
30116         
30117         if(valid){
30118             this.markValid();
30119             return valid;
30120         }
30121         
30122         this.markInvalid();
30123         
30124         return valid;
30125     },
30126     
30127     markValid : function()
30128     {
30129         
30130         var label = this.el.select('label', true).first();
30131         var icon = this.el.select('i.fa-star', true).first();
30132
30133         if(label && icon){
30134             icon.remove();
30135         }
30136         
30137         this.fireEvent('valid', this);
30138     },
30139     
30140      /**
30141      * Mark this field as invalid
30142      * @param {String} msg The validation message
30143      */
30144     markInvalid : function(msg)
30145     {
30146         
30147         var label = this.el.select('label', true).first();
30148         var icon = this.el.select('i.fa-star', true).first();
30149
30150         if(label && !icon){
30151             this.el.select('.roo-date-split-field-label', true).createChild({
30152                 tag : 'i',
30153                 cls : 'text-danger fa fa-lg fa-star',
30154                 tooltip : 'This field is required',
30155                 style : 'margin-right:5px;'
30156             }, label, true);
30157         }
30158         
30159         this.fireEvent('invalid', this, msg);
30160     },
30161     
30162     clearInvalid : function()
30163     {
30164         var label = this.el.select('label', true).first();
30165         var icon = this.el.select('i.fa-star', true).first();
30166
30167         if(label && icon){
30168             icon.remove();
30169         }
30170         
30171         this.fireEvent('valid', this);
30172     },
30173     
30174     getName: function()
30175     {
30176         return this.name;
30177     }
30178     
30179 });
30180
30181  /**
30182  *
30183  * This is based on 
30184  * http://masonry.desandro.com
30185  *
30186  * The idea is to render all the bricks based on vertical width...
30187  *
30188  * The original code extends 'outlayer' - we might need to use that....
30189  * 
30190  */
30191
30192
30193 /**
30194  * @class Roo.bootstrap.LayoutMasonry
30195  * @extends Roo.bootstrap.Component
30196  * Bootstrap Layout Masonry class
30197  * 
30198  * @constructor
30199  * Create a new Element
30200  * @param {Object} config The config object
30201  */
30202
30203 Roo.bootstrap.LayoutMasonry = function(config){
30204     
30205     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30206     
30207     this.bricks = [];
30208     
30209     Roo.bootstrap.LayoutMasonry.register(this);
30210     
30211     this.addEvents({
30212         // raw events
30213         /**
30214          * @event layout
30215          * Fire after layout the items
30216          * @param {Roo.bootstrap.LayoutMasonry} this
30217          * @param {Roo.EventObject} e
30218          */
30219         "layout" : true
30220     });
30221     
30222 };
30223
30224 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30225     
30226     /**
30227      * @cfg {Boolean} isLayoutInstant = no animation?
30228      */   
30229     isLayoutInstant : false, // needed?
30230    
30231     /**
30232      * @cfg {Number} boxWidth  width of the columns
30233      */   
30234     boxWidth : 450,
30235     
30236       /**
30237      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30238      */   
30239     boxHeight : 0,
30240     
30241     /**
30242      * @cfg {Number} padWidth padding below box..
30243      */   
30244     padWidth : 10, 
30245     
30246     /**
30247      * @cfg {Number} gutter gutter width..
30248      */   
30249     gutter : 10,
30250     
30251      /**
30252      * @cfg {Number} maxCols maximum number of columns
30253      */   
30254     
30255     maxCols: 0,
30256     
30257     /**
30258      * @cfg {Boolean} isAutoInitial defalut true
30259      */   
30260     isAutoInitial : true, 
30261     
30262     containerWidth: 0,
30263     
30264     /**
30265      * @cfg {Boolean} isHorizontal defalut false
30266      */   
30267     isHorizontal : false, 
30268
30269     currentSize : null,
30270     
30271     tag: 'div',
30272     
30273     cls: '',
30274     
30275     bricks: null, //CompositeElement
30276     
30277     cols : 1,
30278     
30279     _isLayoutInited : false,
30280     
30281 //    isAlternative : false, // only use for vertical layout...
30282     
30283     /**
30284      * @cfg {Number} alternativePadWidth padding below box..
30285      */   
30286     alternativePadWidth : 50,
30287     
30288     selectedBrick : [],
30289     
30290     getAutoCreate : function(){
30291         
30292         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30293         
30294         var cfg = {
30295             tag: this.tag,
30296             cls: 'blog-masonary-wrapper ' + this.cls,
30297             cn : {
30298                 cls : 'mas-boxes masonary'
30299             }
30300         };
30301         
30302         return cfg;
30303     },
30304     
30305     getChildContainer: function( )
30306     {
30307         if (this.boxesEl) {
30308             return this.boxesEl;
30309         }
30310         
30311         this.boxesEl = this.el.select('.mas-boxes').first();
30312         
30313         return this.boxesEl;
30314     },
30315     
30316     
30317     initEvents : function()
30318     {
30319         var _this = this;
30320         
30321         if(this.isAutoInitial){
30322             Roo.log('hook children rendered');
30323             this.on('childrenrendered', function() {
30324                 Roo.log('children rendered');
30325                 _this.initial();
30326             } ,this);
30327         }
30328     },
30329     
30330     initial : function()
30331     {
30332         this.selectedBrick = [];
30333         
30334         this.currentSize = this.el.getBox(true);
30335         
30336         Roo.EventManager.onWindowResize(this.resize, this); 
30337
30338         if(!this.isAutoInitial){
30339             this.layout();
30340             return;
30341         }
30342         
30343         this.layout();
30344         
30345         return;
30346         //this.layout.defer(500,this);
30347         
30348     },
30349     
30350     resize : function()
30351     {
30352         var cs = this.el.getBox(true);
30353         
30354         if (
30355                 this.currentSize.width == cs.width && 
30356                 this.currentSize.x == cs.x && 
30357                 this.currentSize.height == cs.height && 
30358                 this.currentSize.y == cs.y 
30359         ) {
30360             Roo.log("no change in with or X or Y");
30361             return;
30362         }
30363         
30364         this.currentSize = cs;
30365         
30366         this.layout();
30367         
30368     },
30369     
30370     layout : function()
30371     {   
30372         this._resetLayout();
30373         
30374         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30375         
30376         this.layoutItems( isInstant );
30377       
30378         this._isLayoutInited = true;
30379         
30380         this.fireEvent('layout', this);
30381         
30382     },
30383     
30384     _resetLayout : function()
30385     {
30386         if(this.isHorizontal){
30387             this.horizontalMeasureColumns();
30388             return;
30389         }
30390         
30391         this.verticalMeasureColumns();
30392         
30393     },
30394     
30395     verticalMeasureColumns : function()
30396     {
30397         this.getContainerWidth();
30398         
30399 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30400 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30401 //            return;
30402 //        }
30403         
30404         var boxWidth = this.boxWidth + this.padWidth;
30405         
30406         if(this.containerWidth < this.boxWidth){
30407             boxWidth = this.containerWidth
30408         }
30409         
30410         var containerWidth = this.containerWidth;
30411         
30412         var cols = Math.floor(containerWidth / boxWidth);
30413         
30414         this.cols = Math.max( cols, 1 );
30415         
30416         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30417         
30418         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30419         
30420         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30421         
30422         this.colWidth = boxWidth + avail - this.padWidth;
30423         
30424         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30425         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30426     },
30427     
30428     horizontalMeasureColumns : function()
30429     {
30430         this.getContainerWidth();
30431         
30432         var boxWidth = this.boxWidth;
30433         
30434         if(this.containerWidth < boxWidth){
30435             boxWidth = this.containerWidth;
30436         }
30437         
30438         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30439         
30440         this.el.setHeight(boxWidth);
30441         
30442     },
30443     
30444     getContainerWidth : function()
30445     {
30446         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30447     },
30448     
30449     layoutItems : function( isInstant )
30450     {
30451         Roo.log(this.bricks);
30452         
30453         var items = Roo.apply([], this.bricks);
30454         
30455         if(this.isHorizontal){
30456             this._horizontalLayoutItems( items , isInstant );
30457             return;
30458         }
30459         
30460 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30461 //            this._verticalAlternativeLayoutItems( items , isInstant );
30462 //            return;
30463 //        }
30464         
30465         this._verticalLayoutItems( items , isInstant );
30466         
30467     },
30468     
30469     _verticalLayoutItems : function ( items , isInstant)
30470     {
30471         if ( !items || !items.length ) {
30472             return;
30473         }
30474         
30475         var standard = [
30476             ['xs', 'xs', 'xs', 'tall'],
30477             ['xs', 'xs', 'tall'],
30478             ['xs', 'xs', 'sm'],
30479             ['xs', 'xs', 'xs'],
30480             ['xs', 'tall'],
30481             ['xs', 'sm'],
30482             ['xs', 'xs'],
30483             ['xs'],
30484             
30485             ['sm', 'xs', 'xs'],
30486             ['sm', 'xs'],
30487             ['sm'],
30488             
30489             ['tall', 'xs', 'xs', 'xs'],
30490             ['tall', 'xs', 'xs'],
30491             ['tall', 'xs'],
30492             ['tall']
30493             
30494         ];
30495         
30496         var queue = [];
30497         
30498         var boxes = [];
30499         
30500         var box = [];
30501         
30502         Roo.each(items, function(item, k){
30503             
30504             switch (item.size) {
30505                 // these layouts take up a full box,
30506                 case 'md' :
30507                 case 'md-left' :
30508                 case 'md-right' :
30509                 case 'wide' :
30510                     
30511                     if(box.length){
30512                         boxes.push(box);
30513                         box = [];
30514                     }
30515                     
30516                     boxes.push([item]);
30517                     
30518                     break;
30519                     
30520                 case 'xs' :
30521                 case 'sm' :
30522                 case 'tall' :
30523                     
30524                     box.push(item);
30525                     
30526                     break;
30527                 default :
30528                     break;
30529                     
30530             }
30531             
30532         }, this);
30533         
30534         if(box.length){
30535             boxes.push(box);
30536             box = [];
30537         }
30538         
30539         var filterPattern = function(box, length)
30540         {
30541             if(!box.length){
30542                 return;
30543             }
30544             
30545             var match = false;
30546             
30547             var pattern = box.slice(0, length);
30548             
30549             var format = [];
30550             
30551             Roo.each(pattern, function(i){
30552                 format.push(i.size);
30553             }, this);
30554             
30555             Roo.each(standard, function(s){
30556                 
30557                 if(String(s) != String(format)){
30558                     return;
30559                 }
30560                 
30561                 match = true;
30562                 return false;
30563                 
30564             }, this);
30565             
30566             if(!match && length == 1){
30567                 return;
30568             }
30569             
30570             if(!match){
30571                 filterPattern(box, length - 1);
30572                 return;
30573             }
30574                 
30575             queue.push(pattern);
30576
30577             box = box.slice(length, box.length);
30578
30579             filterPattern(box, 4);
30580
30581             return;
30582             
30583         }
30584         
30585         Roo.each(boxes, function(box, k){
30586             
30587             if(!box.length){
30588                 return;
30589             }
30590             
30591             if(box.length == 1){
30592                 queue.push(box);
30593                 return;
30594             }
30595             
30596             filterPattern(box, 4);
30597             
30598         }, this);
30599         
30600         this._processVerticalLayoutQueue( queue, isInstant );
30601         
30602     },
30603     
30604 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30605 //    {
30606 //        if ( !items || !items.length ) {
30607 //            return;
30608 //        }
30609 //
30610 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30611 //        
30612 //    },
30613     
30614     _horizontalLayoutItems : function ( items , isInstant)
30615     {
30616         if ( !items || !items.length || items.length < 3) {
30617             return;
30618         }
30619         
30620         items.reverse();
30621         
30622         var eItems = items.slice(0, 3);
30623         
30624         items = items.slice(3, items.length);
30625         
30626         var standard = [
30627             ['xs', 'xs', 'xs', 'wide'],
30628             ['xs', 'xs', 'wide'],
30629             ['xs', 'xs', 'sm'],
30630             ['xs', 'xs', 'xs'],
30631             ['xs', 'wide'],
30632             ['xs', 'sm'],
30633             ['xs', 'xs'],
30634             ['xs'],
30635             
30636             ['sm', 'xs', 'xs'],
30637             ['sm', 'xs'],
30638             ['sm'],
30639             
30640             ['wide', 'xs', 'xs', 'xs'],
30641             ['wide', 'xs', 'xs'],
30642             ['wide', 'xs'],
30643             ['wide'],
30644             
30645             ['wide-thin']
30646         ];
30647         
30648         var queue = [];
30649         
30650         var boxes = [];
30651         
30652         var box = [];
30653         
30654         Roo.each(items, function(item, k){
30655             
30656             switch (item.size) {
30657                 case 'md' :
30658                 case 'md-left' :
30659                 case 'md-right' :
30660                 case 'tall' :
30661                     
30662                     if(box.length){
30663                         boxes.push(box);
30664                         box = [];
30665                     }
30666                     
30667                     boxes.push([item]);
30668                     
30669                     break;
30670                     
30671                 case 'xs' :
30672                 case 'sm' :
30673                 case 'wide' :
30674                 case 'wide-thin' :
30675                     
30676                     box.push(item);
30677                     
30678                     break;
30679                 default :
30680                     break;
30681                     
30682             }
30683             
30684         }, this);
30685         
30686         if(box.length){
30687             boxes.push(box);
30688             box = [];
30689         }
30690         
30691         var filterPattern = function(box, length)
30692         {
30693             if(!box.length){
30694                 return;
30695             }
30696             
30697             var match = false;
30698             
30699             var pattern = box.slice(0, length);
30700             
30701             var format = [];
30702             
30703             Roo.each(pattern, function(i){
30704                 format.push(i.size);
30705             }, this);
30706             
30707             Roo.each(standard, function(s){
30708                 
30709                 if(String(s) != String(format)){
30710                     return;
30711                 }
30712                 
30713                 match = true;
30714                 return false;
30715                 
30716             }, this);
30717             
30718             if(!match && length == 1){
30719                 return;
30720             }
30721             
30722             if(!match){
30723                 filterPattern(box, length - 1);
30724                 return;
30725             }
30726                 
30727             queue.push(pattern);
30728
30729             box = box.slice(length, box.length);
30730
30731             filterPattern(box, 4);
30732
30733             return;
30734             
30735         }
30736         
30737         Roo.each(boxes, function(box, k){
30738             
30739             if(!box.length){
30740                 return;
30741             }
30742             
30743             if(box.length == 1){
30744                 queue.push(box);
30745                 return;
30746             }
30747             
30748             filterPattern(box, 4);
30749             
30750         }, this);
30751         
30752         
30753         var prune = [];
30754         
30755         var pos = this.el.getBox(true);
30756         
30757         var minX = pos.x;
30758         
30759         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30760         
30761         var hit_end = false;
30762         
30763         Roo.each(queue, function(box){
30764             
30765             if(hit_end){
30766                 
30767                 Roo.each(box, function(b){
30768                 
30769                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30770                     b.el.hide();
30771
30772                 }, this);
30773
30774                 return;
30775             }
30776             
30777             var mx = 0;
30778             
30779             Roo.each(box, function(b){
30780                 
30781                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30782                 b.el.show();
30783
30784                 mx = Math.max(mx, b.x);
30785                 
30786             }, this);
30787             
30788             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30789             
30790             if(maxX < minX){
30791                 
30792                 Roo.each(box, function(b){
30793                 
30794                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30795                     b.el.hide();
30796                     
30797                 }, this);
30798                 
30799                 hit_end = true;
30800                 
30801                 return;
30802             }
30803             
30804             prune.push(box);
30805             
30806         }, this);
30807         
30808         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30809     },
30810     
30811     /** Sets position of item in DOM
30812     * @param {Element} item
30813     * @param {Number} x - horizontal position
30814     * @param {Number} y - vertical position
30815     * @param {Boolean} isInstant - disables transitions
30816     */
30817     _processVerticalLayoutQueue : function( queue, isInstant )
30818     {
30819         var pos = this.el.getBox(true);
30820         var x = pos.x;
30821         var y = pos.y;
30822         var maxY = [];
30823         
30824         for (var i = 0; i < this.cols; i++){
30825             maxY[i] = pos.y;
30826         }
30827         
30828         Roo.each(queue, function(box, k){
30829             
30830             var col = k % this.cols;
30831             
30832             Roo.each(box, function(b,kk){
30833                 
30834                 b.el.position('absolute');
30835                 
30836                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30837                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30838                 
30839                 if(b.size == 'md-left' || b.size == 'md-right'){
30840                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30841                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30842                 }
30843                 
30844                 b.el.setWidth(width);
30845                 b.el.setHeight(height);
30846                 // iframe?
30847                 b.el.select('iframe',true).setSize(width,height);
30848                 
30849             }, this);
30850             
30851             for (var i = 0; i < this.cols; i++){
30852                 
30853                 if(maxY[i] < maxY[col]){
30854                     col = i;
30855                     continue;
30856                 }
30857                 
30858                 col = Math.min(col, i);
30859                 
30860             }
30861             
30862             x = pos.x + col * (this.colWidth + this.padWidth);
30863             
30864             y = maxY[col];
30865             
30866             var positions = [];
30867             
30868             switch (box.length){
30869                 case 1 :
30870                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30871                     break;
30872                 case 2 :
30873                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30874                     break;
30875                 case 3 :
30876                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30877                     break;
30878                 case 4 :
30879                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30880                     break;
30881                 default :
30882                     break;
30883             }
30884             
30885             Roo.each(box, function(b,kk){
30886                 
30887                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30888                 
30889                 var sz = b.el.getSize();
30890                 
30891                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30892                 
30893             }, this);
30894             
30895         }, this);
30896         
30897         var mY = 0;
30898         
30899         for (var i = 0; i < this.cols; i++){
30900             mY = Math.max(mY, maxY[i]);
30901         }
30902         
30903         this.el.setHeight(mY - pos.y);
30904         
30905     },
30906     
30907 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30908 //    {
30909 //        var pos = this.el.getBox(true);
30910 //        var x = pos.x;
30911 //        var y = pos.y;
30912 //        var maxX = pos.right;
30913 //        
30914 //        var maxHeight = 0;
30915 //        
30916 //        Roo.each(items, function(item, k){
30917 //            
30918 //            var c = k % 2;
30919 //            
30920 //            item.el.position('absolute');
30921 //                
30922 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30923 //
30924 //            item.el.setWidth(width);
30925 //
30926 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30927 //
30928 //            item.el.setHeight(height);
30929 //            
30930 //            if(c == 0){
30931 //                item.el.setXY([x, y], isInstant ? false : true);
30932 //            } else {
30933 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30934 //            }
30935 //            
30936 //            y = y + height + this.alternativePadWidth;
30937 //            
30938 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30939 //            
30940 //        }, this);
30941 //        
30942 //        this.el.setHeight(maxHeight);
30943 //        
30944 //    },
30945     
30946     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30947     {
30948         var pos = this.el.getBox(true);
30949         
30950         var minX = pos.x;
30951         var minY = pos.y;
30952         
30953         var maxX = pos.right;
30954         
30955         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30956         
30957         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30958         
30959         Roo.each(queue, function(box, k){
30960             
30961             Roo.each(box, function(b, kk){
30962                 
30963                 b.el.position('absolute');
30964                 
30965                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30966                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30967                 
30968                 if(b.size == 'md-left' || b.size == 'md-right'){
30969                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30970                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30971                 }
30972                 
30973                 b.el.setWidth(width);
30974                 b.el.setHeight(height);
30975                 
30976             }, this);
30977             
30978             if(!box.length){
30979                 return;
30980             }
30981             
30982             var positions = [];
30983             
30984             switch (box.length){
30985                 case 1 :
30986                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30987                     break;
30988                 case 2 :
30989                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30990                     break;
30991                 case 3 :
30992                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30993                     break;
30994                 case 4 :
30995                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30996                     break;
30997                 default :
30998                     break;
30999             }
31000             
31001             Roo.each(box, function(b,kk){
31002                 
31003                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31004                 
31005                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31006                 
31007             }, this);
31008             
31009         }, this);
31010         
31011     },
31012     
31013     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31014     {
31015         Roo.each(eItems, function(b,k){
31016             
31017             b.size = (k == 0) ? 'sm' : 'xs';
31018             b.x = (k == 0) ? 2 : 1;
31019             b.y = (k == 0) ? 2 : 1;
31020             
31021             b.el.position('absolute');
31022             
31023             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31024                 
31025             b.el.setWidth(width);
31026             
31027             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31028             
31029             b.el.setHeight(height);
31030             
31031         }, this);
31032
31033         var positions = [];
31034         
31035         positions.push({
31036             x : maxX - this.unitWidth * 2 - this.gutter,
31037             y : minY
31038         });
31039         
31040         positions.push({
31041             x : maxX - this.unitWidth,
31042             y : minY + (this.unitWidth + this.gutter) * 2
31043         });
31044         
31045         positions.push({
31046             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31047             y : minY
31048         });
31049         
31050         Roo.each(eItems, function(b,k){
31051             
31052             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31053
31054         }, this);
31055         
31056     },
31057     
31058     getVerticalOneBoxColPositions : function(x, y, box)
31059     {
31060         var pos = [];
31061         
31062         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31063         
31064         if(box[0].size == 'md-left'){
31065             rand = 0;
31066         }
31067         
31068         if(box[0].size == 'md-right'){
31069             rand = 1;
31070         }
31071         
31072         pos.push({
31073             x : x + (this.unitWidth + this.gutter) * rand,
31074             y : y
31075         });
31076         
31077         return pos;
31078     },
31079     
31080     getVerticalTwoBoxColPositions : function(x, y, box)
31081     {
31082         var pos = [];
31083         
31084         if(box[0].size == 'xs'){
31085             
31086             pos.push({
31087                 x : x,
31088                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31089             });
31090
31091             pos.push({
31092                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31093                 y : y
31094             });
31095             
31096             return pos;
31097             
31098         }
31099         
31100         pos.push({
31101             x : x,
31102             y : y
31103         });
31104
31105         pos.push({
31106             x : x + (this.unitWidth + this.gutter) * 2,
31107             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31108         });
31109         
31110         return pos;
31111         
31112     },
31113     
31114     getVerticalThreeBoxColPositions : function(x, y, box)
31115     {
31116         var pos = [];
31117         
31118         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31119             
31120             pos.push({
31121                 x : x,
31122                 y : y
31123             });
31124
31125             pos.push({
31126                 x : x + (this.unitWidth + this.gutter) * 1,
31127                 y : y
31128             });
31129             
31130             pos.push({
31131                 x : x + (this.unitWidth + this.gutter) * 2,
31132                 y : y
31133             });
31134             
31135             return pos;
31136             
31137         }
31138         
31139         if(box[0].size == 'xs' && box[1].size == 'xs'){
31140             
31141             pos.push({
31142                 x : x,
31143                 y : y
31144             });
31145
31146             pos.push({
31147                 x : x,
31148                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31149             });
31150             
31151             pos.push({
31152                 x : x + (this.unitWidth + this.gutter) * 1,
31153                 y : y
31154             });
31155             
31156             return pos;
31157             
31158         }
31159         
31160         pos.push({
31161             x : x,
31162             y : y
31163         });
31164
31165         pos.push({
31166             x : x + (this.unitWidth + this.gutter) * 2,
31167             y : y
31168         });
31169
31170         pos.push({
31171             x : x + (this.unitWidth + this.gutter) * 2,
31172             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31173         });
31174             
31175         return pos;
31176         
31177     },
31178     
31179     getVerticalFourBoxColPositions : function(x, y, box)
31180     {
31181         var pos = [];
31182         
31183         if(box[0].size == 'xs'){
31184             
31185             pos.push({
31186                 x : x,
31187                 y : y
31188             });
31189
31190             pos.push({
31191                 x : x,
31192                 y : y + (this.unitHeight + this.gutter) * 1
31193             });
31194             
31195             pos.push({
31196                 x : x,
31197                 y : y + (this.unitHeight + this.gutter) * 2
31198             });
31199             
31200             pos.push({
31201                 x : x + (this.unitWidth + this.gutter) * 1,
31202                 y : y
31203             });
31204             
31205             return pos;
31206             
31207         }
31208         
31209         pos.push({
31210             x : x,
31211             y : y
31212         });
31213
31214         pos.push({
31215             x : x + (this.unitWidth + this.gutter) * 2,
31216             y : y
31217         });
31218
31219         pos.push({
31220             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31221             y : y + (this.unitHeight + this.gutter) * 1
31222         });
31223
31224         pos.push({
31225             x : x + (this.unitWidth + this.gutter) * 2,
31226             y : y + (this.unitWidth + this.gutter) * 2
31227         });
31228
31229         return pos;
31230         
31231     },
31232     
31233     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31234     {
31235         var pos = [];
31236         
31237         if(box[0].size == 'md-left'){
31238             pos.push({
31239                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31240                 y : minY
31241             });
31242             
31243             return pos;
31244         }
31245         
31246         if(box[0].size == 'md-right'){
31247             pos.push({
31248                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31249                 y : minY + (this.unitWidth + this.gutter) * 1
31250             });
31251             
31252             return pos;
31253         }
31254         
31255         var rand = Math.floor(Math.random() * (4 - box[0].y));
31256         
31257         pos.push({
31258             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31259             y : minY + (this.unitWidth + this.gutter) * rand
31260         });
31261         
31262         return pos;
31263         
31264     },
31265     
31266     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31267     {
31268         var pos = [];
31269         
31270         if(box[0].size == 'xs'){
31271             
31272             pos.push({
31273                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31274                 y : minY
31275             });
31276
31277             pos.push({
31278                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31279                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31280             });
31281             
31282             return pos;
31283             
31284         }
31285         
31286         pos.push({
31287             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31288             y : minY
31289         });
31290
31291         pos.push({
31292             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31293             y : minY + (this.unitWidth + this.gutter) * 2
31294         });
31295         
31296         return pos;
31297         
31298     },
31299     
31300     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31301     {
31302         var pos = [];
31303         
31304         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31305             
31306             pos.push({
31307                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31308                 y : minY
31309             });
31310
31311             pos.push({
31312                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31313                 y : minY + (this.unitWidth + this.gutter) * 1
31314             });
31315             
31316             pos.push({
31317                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31318                 y : minY + (this.unitWidth + this.gutter) * 2
31319             });
31320             
31321             return pos;
31322             
31323         }
31324         
31325         if(box[0].size == 'xs' && box[1].size == 'xs'){
31326             
31327             pos.push({
31328                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31329                 y : minY
31330             });
31331
31332             pos.push({
31333                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31334                 y : minY
31335             });
31336             
31337             pos.push({
31338                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31339                 y : minY + (this.unitWidth + this.gutter) * 1
31340             });
31341             
31342             return pos;
31343             
31344         }
31345         
31346         pos.push({
31347             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31348             y : minY
31349         });
31350
31351         pos.push({
31352             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31353             y : minY + (this.unitWidth + this.gutter) * 2
31354         });
31355
31356         pos.push({
31357             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31358             y : minY + (this.unitWidth + this.gutter) * 2
31359         });
31360             
31361         return pos;
31362         
31363     },
31364     
31365     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31366     {
31367         var pos = [];
31368         
31369         if(box[0].size == 'xs'){
31370             
31371             pos.push({
31372                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31373                 y : minY
31374             });
31375
31376             pos.push({
31377                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31378                 y : minY
31379             });
31380             
31381             pos.push({
31382                 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),
31383                 y : minY
31384             });
31385             
31386             pos.push({
31387                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31388                 y : minY + (this.unitWidth + this.gutter) * 1
31389             });
31390             
31391             return pos;
31392             
31393         }
31394         
31395         pos.push({
31396             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31397             y : minY
31398         });
31399         
31400         pos.push({
31401             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31402             y : minY + (this.unitWidth + this.gutter) * 2
31403         });
31404         
31405         pos.push({
31406             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31407             y : minY + (this.unitWidth + this.gutter) * 2
31408         });
31409         
31410         pos.push({
31411             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),
31412             y : minY + (this.unitWidth + this.gutter) * 2
31413         });
31414
31415         return pos;
31416         
31417     },
31418     
31419     /**
31420     * remove a Masonry Brick
31421     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31422     */
31423     removeBrick : function(brick_id)
31424     {
31425         if (!brick_id) {
31426             return;
31427         }
31428         
31429         for (var i = 0; i<this.bricks.length; i++) {
31430             if (this.bricks[i].id == brick_id) {
31431                 this.bricks.splice(i,1);
31432                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31433                 this.initial();
31434             }
31435         }
31436     },
31437     
31438     /**
31439     * adds a Masonry Brick
31440     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31441     */
31442     addBrick : function(cfg)
31443     {
31444         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31445         //this.register(cn);
31446         cn.parentId = this.id;
31447         cn.onRender(this.el, null);
31448         return cn;
31449     },
31450     
31451     /**
31452     * register a Masonry Brick
31453     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31454     */
31455     
31456     register : function(brick)
31457     {
31458         this.bricks.push(brick);
31459         brick.masonryId = this.id;
31460     },
31461     
31462     /**
31463     * clear all the Masonry Brick
31464     */
31465     clearAll : function()
31466     {
31467         this.bricks = [];
31468         //this.getChildContainer().dom.innerHTML = "";
31469         this.el.dom.innerHTML = '';
31470     },
31471     
31472     getSelected : function()
31473     {
31474         if (!this.selectedBrick) {
31475             return false;
31476         }
31477         
31478         return this.selectedBrick;
31479     }
31480 });
31481
31482 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31483     
31484     groups: {},
31485      /**
31486     * register a Masonry Layout
31487     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31488     */
31489     
31490     register : function(layout)
31491     {
31492         this.groups[layout.id] = layout;
31493     },
31494     /**
31495     * fetch a  Masonry Layout based on the masonry layout ID
31496     * @param {string} the masonry layout to add
31497     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31498     */
31499     
31500     get: function(layout_id) {
31501         if (typeof(this.groups[layout_id]) == 'undefined') {
31502             return false;
31503         }
31504         return this.groups[layout_id] ;
31505     }
31506     
31507     
31508     
31509 });
31510
31511  
31512
31513  /**
31514  *
31515  * This is based on 
31516  * http://masonry.desandro.com
31517  *
31518  * The idea is to render all the bricks based on vertical width...
31519  *
31520  * The original code extends 'outlayer' - we might need to use that....
31521  * 
31522  */
31523
31524
31525 /**
31526  * @class Roo.bootstrap.LayoutMasonryAuto
31527  * @extends Roo.bootstrap.Component
31528  * Bootstrap Layout Masonry class
31529  * 
31530  * @constructor
31531  * Create a new Element
31532  * @param {Object} config The config object
31533  */
31534
31535 Roo.bootstrap.LayoutMasonryAuto = function(config){
31536     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31537 };
31538
31539 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31540     
31541       /**
31542      * @cfg {Boolean} isFitWidth  - resize the width..
31543      */   
31544     isFitWidth : false,  // options..
31545     /**
31546      * @cfg {Boolean} isOriginLeft = left align?
31547      */   
31548     isOriginLeft : true,
31549     /**
31550      * @cfg {Boolean} isOriginTop = top align?
31551      */   
31552     isOriginTop : false,
31553     /**
31554      * @cfg {Boolean} isLayoutInstant = no animation?
31555      */   
31556     isLayoutInstant : false, // needed?
31557     /**
31558      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31559      */   
31560     isResizingContainer : true,
31561     /**
31562      * @cfg {Number} columnWidth  width of the columns 
31563      */   
31564     
31565     columnWidth : 0,
31566     
31567     /**
31568      * @cfg {Number} maxCols maximum number of columns
31569      */   
31570     
31571     maxCols: 0,
31572     /**
31573      * @cfg {Number} padHeight padding below box..
31574      */   
31575     
31576     padHeight : 10, 
31577     
31578     /**
31579      * @cfg {Boolean} isAutoInitial defalut true
31580      */   
31581     
31582     isAutoInitial : true, 
31583     
31584     // private?
31585     gutter : 0,
31586     
31587     containerWidth: 0,
31588     initialColumnWidth : 0,
31589     currentSize : null,
31590     
31591     colYs : null, // array.
31592     maxY : 0,
31593     padWidth: 10,
31594     
31595     
31596     tag: 'div',
31597     cls: '',
31598     bricks: null, //CompositeElement
31599     cols : 0, // array?
31600     // element : null, // wrapped now this.el
31601     _isLayoutInited : null, 
31602     
31603     
31604     getAutoCreate : function(){
31605         
31606         var cfg = {
31607             tag: this.tag,
31608             cls: 'blog-masonary-wrapper ' + this.cls,
31609             cn : {
31610                 cls : 'mas-boxes masonary'
31611             }
31612         };
31613         
31614         return cfg;
31615     },
31616     
31617     getChildContainer: function( )
31618     {
31619         if (this.boxesEl) {
31620             return this.boxesEl;
31621         }
31622         
31623         this.boxesEl = this.el.select('.mas-boxes').first();
31624         
31625         return this.boxesEl;
31626     },
31627     
31628     
31629     initEvents : function()
31630     {
31631         var _this = this;
31632         
31633         if(this.isAutoInitial){
31634             Roo.log('hook children rendered');
31635             this.on('childrenrendered', function() {
31636                 Roo.log('children rendered');
31637                 _this.initial();
31638             } ,this);
31639         }
31640         
31641     },
31642     
31643     initial : function()
31644     {
31645         this.reloadItems();
31646
31647         this.currentSize = this.el.getBox(true);
31648
31649         /// was window resize... - let's see if this works..
31650         Roo.EventManager.onWindowResize(this.resize, this); 
31651
31652         if(!this.isAutoInitial){
31653             this.layout();
31654             return;
31655         }
31656         
31657         this.layout.defer(500,this);
31658     },
31659     
31660     reloadItems: function()
31661     {
31662         this.bricks = this.el.select('.masonry-brick', true);
31663         
31664         this.bricks.each(function(b) {
31665             //Roo.log(b.getSize());
31666             if (!b.attr('originalwidth')) {
31667                 b.attr('originalwidth',  b.getSize().width);
31668             }
31669             
31670         });
31671         
31672         Roo.log(this.bricks.elements.length);
31673     },
31674     
31675     resize : function()
31676     {
31677         Roo.log('resize');
31678         var cs = this.el.getBox(true);
31679         
31680         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31681             Roo.log("no change in with or X");
31682             return;
31683         }
31684         this.currentSize = cs;
31685         this.layout();
31686     },
31687     
31688     layout : function()
31689     {
31690          Roo.log('layout');
31691         this._resetLayout();
31692         //this._manageStamps();
31693       
31694         // don't animate first layout
31695         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31696         this.layoutItems( isInstant );
31697       
31698         // flag for initalized
31699         this._isLayoutInited = true;
31700     },
31701     
31702     layoutItems : function( isInstant )
31703     {
31704         //var items = this._getItemsForLayout( this.items );
31705         // original code supports filtering layout items.. we just ignore it..
31706         
31707         this._layoutItems( this.bricks , isInstant );
31708       
31709         this._postLayout();
31710     },
31711     _layoutItems : function ( items , isInstant)
31712     {
31713        //this.fireEvent( 'layout', this, items );
31714     
31715
31716         if ( !items || !items.elements.length ) {
31717           // no items, emit event with empty array
31718             return;
31719         }
31720
31721         var queue = [];
31722         items.each(function(item) {
31723             Roo.log("layout item");
31724             Roo.log(item);
31725             // get x/y object from method
31726             var position = this._getItemLayoutPosition( item );
31727             // enqueue
31728             position.item = item;
31729             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31730             queue.push( position );
31731         }, this);
31732       
31733         this._processLayoutQueue( queue );
31734     },
31735     /** Sets position of item in DOM
31736     * @param {Element} item
31737     * @param {Number} x - horizontal position
31738     * @param {Number} y - vertical position
31739     * @param {Boolean} isInstant - disables transitions
31740     */
31741     _processLayoutQueue : function( queue )
31742     {
31743         for ( var i=0, len = queue.length; i < len; i++ ) {
31744             var obj = queue[i];
31745             obj.item.position('absolute');
31746             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31747         }
31748     },
31749       
31750     
31751     /**
31752     * Any logic you want to do after each layout,
31753     * i.e. size the container
31754     */
31755     _postLayout : function()
31756     {
31757         this.resizeContainer();
31758     },
31759     
31760     resizeContainer : function()
31761     {
31762         if ( !this.isResizingContainer ) {
31763             return;
31764         }
31765         var size = this._getContainerSize();
31766         if ( size ) {
31767             this.el.setSize(size.width,size.height);
31768             this.boxesEl.setSize(size.width,size.height);
31769         }
31770     },
31771     
31772     
31773     
31774     _resetLayout : function()
31775     {
31776         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31777         this.colWidth = this.el.getWidth();
31778         //this.gutter = this.el.getWidth(); 
31779         
31780         this.measureColumns();
31781
31782         // reset column Y
31783         var i = this.cols;
31784         this.colYs = [];
31785         while (i--) {
31786             this.colYs.push( 0 );
31787         }
31788     
31789         this.maxY = 0;
31790     },
31791
31792     measureColumns : function()
31793     {
31794         this.getContainerWidth();
31795       // if columnWidth is 0, default to outerWidth of first item
31796         if ( !this.columnWidth ) {
31797             var firstItem = this.bricks.first();
31798             Roo.log(firstItem);
31799             this.columnWidth  = this.containerWidth;
31800             if (firstItem && firstItem.attr('originalwidth') ) {
31801                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31802             }
31803             // columnWidth fall back to item of first element
31804             Roo.log("set column width?");
31805                         this.initialColumnWidth = this.columnWidth  ;
31806
31807             // if first elem has no width, default to size of container
31808             
31809         }
31810         
31811         
31812         if (this.initialColumnWidth) {
31813             this.columnWidth = this.initialColumnWidth;
31814         }
31815         
31816         
31817             
31818         // column width is fixed at the top - however if container width get's smaller we should
31819         // reduce it...
31820         
31821         // this bit calcs how man columns..
31822             
31823         var columnWidth = this.columnWidth += this.gutter;
31824       
31825         // calculate columns
31826         var containerWidth = this.containerWidth + this.gutter;
31827         
31828         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31829         // fix rounding errors, typically with gutters
31830         var excess = columnWidth - containerWidth % columnWidth;
31831         
31832         
31833         // if overshoot is less than a pixel, round up, otherwise floor it
31834         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31835         cols = Math[ mathMethod ]( cols );
31836         this.cols = Math.max( cols, 1 );
31837         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31838         
31839          // padding positioning..
31840         var totalColWidth = this.cols * this.columnWidth;
31841         var padavail = this.containerWidth - totalColWidth;
31842         // so for 2 columns - we need 3 'pads'
31843         
31844         var padNeeded = (1+this.cols) * this.padWidth;
31845         
31846         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31847         
31848         this.columnWidth += padExtra
31849         //this.padWidth = Math.floor(padavail /  ( this.cols));
31850         
31851         // adjust colum width so that padding is fixed??
31852         
31853         // we have 3 columns ... total = width * 3
31854         // we have X left over... that should be used by 
31855         
31856         //if (this.expandC) {
31857             
31858         //}
31859         
31860         
31861         
31862     },
31863     
31864     getContainerWidth : function()
31865     {
31866        /* // container is parent if fit width
31867         var container = this.isFitWidth ? this.element.parentNode : this.element;
31868         // check that this.size and size are there
31869         // IE8 triggers resize on body size change, so they might not be
31870         
31871         var size = getSize( container );  //FIXME
31872         this.containerWidth = size && size.innerWidth; //FIXME
31873         */
31874          
31875         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31876         
31877     },
31878     
31879     _getItemLayoutPosition : function( item )  // what is item?
31880     {
31881         // we resize the item to our columnWidth..
31882       
31883         item.setWidth(this.columnWidth);
31884         item.autoBoxAdjust  = false;
31885         
31886         var sz = item.getSize();
31887  
31888         // how many columns does this brick span
31889         var remainder = this.containerWidth % this.columnWidth;
31890         
31891         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31892         // round if off by 1 pixel, otherwise use ceil
31893         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31894         colSpan = Math.min( colSpan, this.cols );
31895         
31896         // normally this should be '1' as we dont' currently allow multi width columns..
31897         
31898         var colGroup = this._getColGroup( colSpan );
31899         // get the minimum Y value from the columns
31900         var minimumY = Math.min.apply( Math, colGroup );
31901         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31902         
31903         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31904          
31905         // position the brick
31906         var position = {
31907             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31908             y: this.currentSize.y + minimumY + this.padHeight
31909         };
31910         
31911         Roo.log(position);
31912         // apply setHeight to necessary columns
31913         var setHeight = minimumY + sz.height + this.padHeight;
31914         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31915         
31916         var setSpan = this.cols + 1 - colGroup.length;
31917         for ( var i = 0; i < setSpan; i++ ) {
31918           this.colYs[ shortColIndex + i ] = setHeight ;
31919         }
31920       
31921         return position;
31922     },
31923     
31924     /**
31925      * @param {Number} colSpan - number of columns the element spans
31926      * @returns {Array} colGroup
31927      */
31928     _getColGroup : function( colSpan )
31929     {
31930         if ( colSpan < 2 ) {
31931           // if brick spans only one column, use all the column Ys
31932           return this.colYs;
31933         }
31934       
31935         var colGroup = [];
31936         // how many different places could this brick fit horizontally
31937         var groupCount = this.cols + 1 - colSpan;
31938         // for each group potential horizontal position
31939         for ( var i = 0; i < groupCount; i++ ) {
31940           // make an array of colY values for that one group
31941           var groupColYs = this.colYs.slice( i, i + colSpan );
31942           // and get the max value of the array
31943           colGroup[i] = Math.max.apply( Math, groupColYs );
31944         }
31945         return colGroup;
31946     },
31947     /*
31948     _manageStamp : function( stamp )
31949     {
31950         var stampSize =  stamp.getSize();
31951         var offset = stamp.getBox();
31952         // get the columns that this stamp affects
31953         var firstX = this.isOriginLeft ? offset.x : offset.right;
31954         var lastX = firstX + stampSize.width;
31955         var firstCol = Math.floor( firstX / this.columnWidth );
31956         firstCol = Math.max( 0, firstCol );
31957         
31958         var lastCol = Math.floor( lastX / this.columnWidth );
31959         // lastCol should not go over if multiple of columnWidth #425
31960         lastCol -= lastX % this.columnWidth ? 0 : 1;
31961         lastCol = Math.min( this.cols - 1, lastCol );
31962         
31963         // set colYs to bottom of the stamp
31964         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31965             stampSize.height;
31966             
31967         for ( var i = firstCol; i <= lastCol; i++ ) {
31968           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31969         }
31970     },
31971     */
31972     
31973     _getContainerSize : function()
31974     {
31975         this.maxY = Math.max.apply( Math, this.colYs );
31976         var size = {
31977             height: this.maxY
31978         };
31979       
31980         if ( this.isFitWidth ) {
31981             size.width = this._getContainerFitWidth();
31982         }
31983       
31984         return size;
31985     },
31986     
31987     _getContainerFitWidth : function()
31988     {
31989         var unusedCols = 0;
31990         // count unused columns
31991         var i = this.cols;
31992         while ( --i ) {
31993           if ( this.colYs[i] !== 0 ) {
31994             break;
31995           }
31996           unusedCols++;
31997         }
31998         // fit container to columns that have been used
31999         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32000     },
32001     
32002     needsResizeLayout : function()
32003     {
32004         var previousWidth = this.containerWidth;
32005         this.getContainerWidth();
32006         return previousWidth !== this.containerWidth;
32007     }
32008  
32009 });
32010
32011  
32012
32013  /*
32014  * - LGPL
32015  *
32016  * element
32017  * 
32018  */
32019
32020 /**
32021  * @class Roo.bootstrap.MasonryBrick
32022  * @extends Roo.bootstrap.Component
32023  * Bootstrap MasonryBrick class
32024  * 
32025  * @constructor
32026  * Create a new MasonryBrick
32027  * @param {Object} config The config object
32028  */
32029
32030 Roo.bootstrap.MasonryBrick = function(config){
32031     
32032     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32033     
32034     Roo.bootstrap.MasonryBrick.register(this);
32035     
32036     this.addEvents({
32037         // raw events
32038         /**
32039          * @event click
32040          * When a MasonryBrick is clcik
32041          * @param {Roo.bootstrap.MasonryBrick} this
32042          * @param {Roo.EventObject} e
32043          */
32044         "click" : true
32045     });
32046 };
32047
32048 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32049     
32050     /**
32051      * @cfg {String} title
32052      */   
32053     title : '',
32054     /**
32055      * @cfg {String} html
32056      */   
32057     html : '',
32058     /**
32059      * @cfg {String} bgimage
32060      */   
32061     bgimage : '',
32062     /**
32063      * @cfg {String} videourl
32064      */   
32065     videourl : '',
32066     /**
32067      * @cfg {String} cls
32068      */   
32069     cls : '',
32070     /**
32071      * @cfg {String} href
32072      */   
32073     href : '',
32074     /**
32075      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32076      */   
32077     size : 'xs',
32078     
32079     /**
32080      * @cfg {String} placetitle (center|bottom)
32081      */   
32082     placetitle : '',
32083     
32084     /**
32085      * @cfg {Boolean} isFitContainer defalut true
32086      */   
32087     isFitContainer : true, 
32088     
32089     /**
32090      * @cfg {Boolean} preventDefault defalut false
32091      */   
32092     preventDefault : false, 
32093     
32094     /**
32095      * @cfg {Boolean} inverse defalut false
32096      */   
32097     maskInverse : false, 
32098     
32099     getAutoCreate : function()
32100     {
32101         if(!this.isFitContainer){
32102             return this.getSplitAutoCreate();
32103         }
32104         
32105         var cls = 'masonry-brick masonry-brick-full';
32106         
32107         if(this.href.length){
32108             cls += ' masonry-brick-link';
32109         }
32110         
32111         if(this.bgimage.length){
32112             cls += ' masonry-brick-image';
32113         }
32114         
32115         if(this.maskInverse){
32116             cls += ' mask-inverse';
32117         }
32118         
32119         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32120             cls += ' enable-mask';
32121         }
32122         
32123         if(this.size){
32124             cls += ' masonry-' + this.size + '-brick';
32125         }
32126         
32127         if(this.placetitle.length){
32128             
32129             switch (this.placetitle) {
32130                 case 'center' :
32131                     cls += ' masonry-center-title';
32132                     break;
32133                 case 'bottom' :
32134                     cls += ' masonry-bottom-title';
32135                     break;
32136                 default:
32137                     break;
32138             }
32139             
32140         } else {
32141             if(!this.html.length && !this.bgimage.length){
32142                 cls += ' masonry-center-title';
32143             }
32144
32145             if(!this.html.length && this.bgimage.length){
32146                 cls += ' masonry-bottom-title';
32147             }
32148         }
32149         
32150         if(this.cls){
32151             cls += ' ' + this.cls;
32152         }
32153         
32154         var cfg = {
32155             tag: (this.href.length) ? 'a' : 'div',
32156             cls: cls,
32157             cn: [
32158                 {
32159                     tag: 'div',
32160                     cls: 'masonry-brick-mask'
32161                 },
32162                 {
32163                     tag: 'div',
32164                     cls: 'masonry-brick-paragraph',
32165                     cn: []
32166                 }
32167             ]
32168         };
32169         
32170         if(this.href.length){
32171             cfg.href = this.href;
32172         }
32173         
32174         var cn = cfg.cn[1].cn;
32175         
32176         if(this.title.length){
32177             cn.push({
32178                 tag: 'h4',
32179                 cls: 'masonry-brick-title',
32180                 html: this.title
32181             });
32182         }
32183         
32184         if(this.html.length){
32185             cn.push({
32186                 tag: 'p',
32187                 cls: 'masonry-brick-text',
32188                 html: this.html
32189             });
32190         }
32191         
32192         if (!this.title.length && !this.html.length) {
32193             cfg.cn[1].cls += ' hide';
32194         }
32195         
32196         if(this.bgimage.length){
32197             cfg.cn.push({
32198                 tag: 'img',
32199                 cls: 'masonry-brick-image-view',
32200                 src: this.bgimage
32201             });
32202         }
32203         
32204         if(this.videourl.length){
32205             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32206             // youtube support only?
32207             cfg.cn.push({
32208                 tag: 'iframe',
32209                 cls: 'masonry-brick-image-view',
32210                 src: vurl,
32211                 frameborder : 0,
32212                 allowfullscreen : true
32213             });
32214         }
32215         
32216         return cfg;
32217         
32218     },
32219     
32220     getSplitAutoCreate : function()
32221     {
32222         var cls = 'masonry-brick masonry-brick-split';
32223         
32224         if(this.href.length){
32225             cls += ' masonry-brick-link';
32226         }
32227         
32228         if(this.bgimage.length){
32229             cls += ' masonry-brick-image';
32230         }
32231         
32232         if(this.size){
32233             cls += ' masonry-' + this.size + '-brick';
32234         }
32235         
32236         switch (this.placetitle) {
32237             case 'center' :
32238                 cls += ' masonry-center-title';
32239                 break;
32240             case 'bottom' :
32241                 cls += ' masonry-bottom-title';
32242                 break;
32243             default:
32244                 if(!this.bgimage.length){
32245                     cls += ' masonry-center-title';
32246                 }
32247
32248                 if(this.bgimage.length){
32249                     cls += ' masonry-bottom-title';
32250                 }
32251                 break;
32252         }
32253         
32254         if(this.cls){
32255             cls += ' ' + this.cls;
32256         }
32257         
32258         var cfg = {
32259             tag: (this.href.length) ? 'a' : 'div',
32260             cls: cls,
32261             cn: [
32262                 {
32263                     tag: 'div',
32264                     cls: 'masonry-brick-split-head',
32265                     cn: [
32266                         {
32267                             tag: 'div',
32268                             cls: 'masonry-brick-paragraph',
32269                             cn: []
32270                         }
32271                     ]
32272                 },
32273                 {
32274                     tag: 'div',
32275                     cls: 'masonry-brick-split-body',
32276                     cn: []
32277                 }
32278             ]
32279         };
32280         
32281         if(this.href.length){
32282             cfg.href = this.href;
32283         }
32284         
32285         if(this.title.length){
32286             cfg.cn[0].cn[0].cn.push({
32287                 tag: 'h4',
32288                 cls: 'masonry-brick-title',
32289                 html: this.title
32290             });
32291         }
32292         
32293         if(this.html.length){
32294             cfg.cn[1].cn.push({
32295                 tag: 'p',
32296                 cls: 'masonry-brick-text',
32297                 html: this.html
32298             });
32299         }
32300
32301         if(this.bgimage.length){
32302             cfg.cn[0].cn.push({
32303                 tag: 'img',
32304                 cls: 'masonry-brick-image-view',
32305                 src: this.bgimage
32306             });
32307         }
32308         
32309         if(this.videourl.length){
32310             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32311             // youtube support only?
32312             cfg.cn[0].cn.cn.push({
32313                 tag: 'iframe',
32314                 cls: 'masonry-brick-image-view',
32315                 src: vurl,
32316                 frameborder : 0,
32317                 allowfullscreen : true
32318             });
32319         }
32320         
32321         return cfg;
32322     },
32323     
32324     initEvents: function() 
32325     {
32326         switch (this.size) {
32327             case 'xs' :
32328                 this.x = 1;
32329                 this.y = 1;
32330                 break;
32331             case 'sm' :
32332                 this.x = 2;
32333                 this.y = 2;
32334                 break;
32335             case 'md' :
32336             case 'md-left' :
32337             case 'md-right' :
32338                 this.x = 3;
32339                 this.y = 3;
32340                 break;
32341             case 'tall' :
32342                 this.x = 2;
32343                 this.y = 3;
32344                 break;
32345             case 'wide' :
32346                 this.x = 3;
32347                 this.y = 2;
32348                 break;
32349             case 'wide-thin' :
32350                 this.x = 3;
32351                 this.y = 1;
32352                 break;
32353                         
32354             default :
32355                 break;
32356         }
32357         
32358         if(Roo.isTouch){
32359             this.el.on('touchstart', this.onTouchStart, this);
32360             this.el.on('touchmove', this.onTouchMove, this);
32361             this.el.on('touchend', this.onTouchEnd, this);
32362             this.el.on('contextmenu', this.onContextMenu, this);
32363         } else {
32364             this.el.on('mouseenter'  ,this.enter, this);
32365             this.el.on('mouseleave', this.leave, this);
32366             this.el.on('click', this.onClick, this);
32367         }
32368         
32369         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32370             this.parent().bricks.push(this);   
32371         }
32372         
32373     },
32374     
32375     onClick: function(e, el)
32376     {
32377         var time = this.endTimer - this.startTimer;
32378         // Roo.log(e.preventDefault());
32379         if(Roo.isTouch){
32380             if(time > 1000){
32381                 e.preventDefault();
32382                 return;
32383             }
32384         }
32385         
32386         if(!this.preventDefault){
32387             return;
32388         }
32389         
32390         e.preventDefault();
32391         
32392         if (this.activcClass != '') {
32393             this.selectBrick();
32394         }
32395         
32396         this.fireEvent('click', this);
32397     },
32398     
32399     enter: function(e, el)
32400     {
32401         e.preventDefault();
32402         
32403         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32404             return;
32405         }
32406         
32407         if(this.bgimage.length && this.html.length){
32408             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32409         }
32410     },
32411     
32412     leave: function(e, el)
32413     {
32414         e.preventDefault();
32415         
32416         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32417             return;
32418         }
32419         
32420         if(this.bgimage.length && this.html.length){
32421             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32422         }
32423     },
32424     
32425     onTouchStart: function(e, el)
32426     {
32427 //        e.preventDefault();
32428         
32429         this.touchmoved = false;
32430         
32431         if(!this.isFitContainer){
32432             return;
32433         }
32434         
32435         if(!this.bgimage.length || !this.html.length){
32436             return;
32437         }
32438         
32439         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32440         
32441         this.timer = new Date().getTime();
32442         
32443     },
32444     
32445     onTouchMove: function(e, el)
32446     {
32447         this.touchmoved = true;
32448     },
32449     
32450     onContextMenu : function(e,el)
32451     {
32452         e.preventDefault();
32453         e.stopPropagation();
32454         return false;
32455     },
32456     
32457     onTouchEnd: function(e, el)
32458     {
32459 //        e.preventDefault();
32460         
32461         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32462         
32463             this.leave(e,el);
32464             
32465             return;
32466         }
32467         
32468         if(!this.bgimage.length || !this.html.length){
32469             
32470             if(this.href.length){
32471                 window.location.href = this.href;
32472             }
32473             
32474             return;
32475         }
32476         
32477         if(!this.isFitContainer){
32478             return;
32479         }
32480         
32481         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32482         
32483         window.location.href = this.href;
32484     },
32485     
32486     //selection on single brick only
32487     selectBrick : function() {
32488         
32489         if (!this.parentId) {
32490             return;
32491         }
32492         
32493         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32494         var index = m.selectedBrick.indexOf(this.id);
32495         
32496         if ( index > -1) {
32497             m.selectedBrick.splice(index,1);
32498             this.el.removeClass(this.activeClass);
32499             return;
32500         }
32501         
32502         for(var i = 0; i < m.selectedBrick.length; i++) {
32503             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32504             b.el.removeClass(b.activeClass);
32505         }
32506         
32507         m.selectedBrick = [];
32508         
32509         m.selectedBrick.push(this.id);
32510         this.el.addClass(this.activeClass);
32511         return;
32512     }
32513     
32514 });
32515
32516 Roo.apply(Roo.bootstrap.MasonryBrick, {
32517     
32518     //groups: {},
32519     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32520      /**
32521     * register a Masonry Brick
32522     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32523     */
32524     
32525     register : function(brick)
32526     {
32527         //this.groups[brick.id] = brick;
32528         this.groups.add(brick.id, brick);
32529     },
32530     /**
32531     * fetch a  masonry brick based on the masonry brick ID
32532     * @param {string} the masonry brick to add
32533     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32534     */
32535     
32536     get: function(brick_id) 
32537     {
32538         // if (typeof(this.groups[brick_id]) == 'undefined') {
32539         //     return false;
32540         // }
32541         // return this.groups[brick_id] ;
32542         
32543         if(this.groups.key(brick_id)) {
32544             return this.groups.key(brick_id);
32545         }
32546         
32547         return false;
32548     }
32549     
32550     
32551     
32552 });
32553
32554  /*
32555  * - LGPL
32556  *
32557  * element
32558  * 
32559  */
32560
32561 /**
32562  * @class Roo.bootstrap.Brick
32563  * @extends Roo.bootstrap.Component
32564  * Bootstrap Brick class
32565  * 
32566  * @constructor
32567  * Create a new Brick
32568  * @param {Object} config The config object
32569  */
32570
32571 Roo.bootstrap.Brick = function(config){
32572     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32573     
32574     this.addEvents({
32575         // raw events
32576         /**
32577          * @event click
32578          * When a Brick is click
32579          * @param {Roo.bootstrap.Brick} this
32580          * @param {Roo.EventObject} e
32581          */
32582         "click" : true
32583     });
32584 };
32585
32586 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32587     
32588     /**
32589      * @cfg {String} title
32590      */   
32591     title : '',
32592     /**
32593      * @cfg {String} html
32594      */   
32595     html : '',
32596     /**
32597      * @cfg {String} bgimage
32598      */   
32599     bgimage : '',
32600     /**
32601      * @cfg {String} cls
32602      */   
32603     cls : '',
32604     /**
32605      * @cfg {String} href
32606      */   
32607     href : '',
32608     /**
32609      * @cfg {String} video
32610      */   
32611     video : '',
32612     /**
32613      * @cfg {Boolean} square
32614      */   
32615     square : true,
32616     
32617     getAutoCreate : function()
32618     {
32619         var cls = 'roo-brick';
32620         
32621         if(this.href.length){
32622             cls += ' roo-brick-link';
32623         }
32624         
32625         if(this.bgimage.length){
32626             cls += ' roo-brick-image';
32627         }
32628         
32629         if(!this.html.length && !this.bgimage.length){
32630             cls += ' roo-brick-center-title';
32631         }
32632         
32633         if(!this.html.length && this.bgimage.length){
32634             cls += ' roo-brick-bottom-title';
32635         }
32636         
32637         if(this.cls){
32638             cls += ' ' + this.cls;
32639         }
32640         
32641         var cfg = {
32642             tag: (this.href.length) ? 'a' : 'div',
32643             cls: cls,
32644             cn: [
32645                 {
32646                     tag: 'div',
32647                     cls: 'roo-brick-paragraph',
32648                     cn: []
32649                 }
32650             ]
32651         };
32652         
32653         if(this.href.length){
32654             cfg.href = this.href;
32655         }
32656         
32657         var cn = cfg.cn[0].cn;
32658         
32659         if(this.title.length){
32660             cn.push({
32661                 tag: 'h4',
32662                 cls: 'roo-brick-title',
32663                 html: this.title
32664             });
32665         }
32666         
32667         if(this.html.length){
32668             cn.push({
32669                 tag: 'p',
32670                 cls: 'roo-brick-text',
32671                 html: this.html
32672             });
32673         } else {
32674             cn.cls += ' hide';
32675         }
32676         
32677         if(this.bgimage.length){
32678             cfg.cn.push({
32679                 tag: 'img',
32680                 cls: 'roo-brick-image-view',
32681                 src: this.bgimage
32682             });
32683         }
32684         
32685         return cfg;
32686     },
32687     
32688     initEvents: function() 
32689     {
32690         if(this.title.length || this.html.length){
32691             this.el.on('mouseenter'  ,this.enter, this);
32692             this.el.on('mouseleave', this.leave, this);
32693         }
32694         
32695         Roo.EventManager.onWindowResize(this.resize, this); 
32696         
32697         if(this.bgimage.length){
32698             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32699             this.imageEl.on('load', this.onImageLoad, this);
32700             return;
32701         }
32702         
32703         this.resize();
32704     },
32705     
32706     onImageLoad : function()
32707     {
32708         this.resize();
32709     },
32710     
32711     resize : function()
32712     {
32713         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32714         
32715         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32716         
32717         if(this.bgimage.length){
32718             var image = this.el.select('.roo-brick-image-view', true).first();
32719             
32720             image.setWidth(paragraph.getWidth());
32721             
32722             if(this.square){
32723                 image.setHeight(paragraph.getWidth());
32724             }
32725             
32726             this.el.setHeight(image.getHeight());
32727             paragraph.setHeight(image.getHeight());
32728             
32729         }
32730         
32731     },
32732     
32733     enter: function(e, el)
32734     {
32735         e.preventDefault();
32736         
32737         if(this.bgimage.length){
32738             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32739             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32740         }
32741     },
32742     
32743     leave: function(e, el)
32744     {
32745         e.preventDefault();
32746         
32747         if(this.bgimage.length){
32748             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32749             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32750         }
32751     }
32752     
32753 });
32754
32755  
32756
32757  /*
32758  * - LGPL
32759  *
32760  * Input
32761  * 
32762  */
32763
32764 /**
32765  * @class Roo.bootstrap.NumberField
32766  * @extends Roo.bootstrap.Input
32767  * Bootstrap NumberField class
32768  * 
32769  * 
32770  * 
32771  * 
32772  * @constructor
32773  * Create a new NumberField
32774  * @param {Object} config The config object
32775  */
32776
32777 Roo.bootstrap.NumberField = function(config){
32778     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32779 };
32780
32781 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32782     
32783     /**
32784      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32785      */
32786     allowDecimals : true,
32787     /**
32788      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32789      */
32790     decimalSeparator : ".",
32791     /**
32792      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32793      */
32794     decimalPrecision : 2,
32795     /**
32796      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32797      */
32798     allowNegative : true,
32799     /**
32800      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32801      */
32802     minValue : Number.NEGATIVE_INFINITY,
32803     /**
32804      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32805      */
32806     maxValue : Number.MAX_VALUE,
32807     /**
32808      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32809      */
32810     minText : "The minimum value for this field is {0}",
32811     /**
32812      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32813      */
32814     maxText : "The maximum value for this field is {0}",
32815     /**
32816      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32817      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32818      */
32819     nanText : "{0} is not a valid number",
32820     /**
32821      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32822      */
32823     castInt : true,
32824
32825     // private
32826     initEvents : function()
32827     {   
32828         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32829         
32830         var allowed = "0123456789";
32831         
32832         if(this.allowDecimals){
32833             allowed += this.decimalSeparator;
32834         }
32835         
32836         if(this.allowNegative){
32837             allowed += "-";
32838         }
32839         
32840         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32841         
32842         var keyPress = function(e){
32843             
32844             var k = e.getKey();
32845             
32846             var c = e.getCharCode();
32847             
32848             if(
32849                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32850                     allowed.indexOf(String.fromCharCode(c)) === -1
32851             ){
32852                 e.stopEvent();
32853                 return;
32854             }
32855             
32856             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32857                 return;
32858             }
32859             
32860             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32861                 e.stopEvent();
32862             }
32863         };
32864         
32865         this.el.on("keypress", keyPress, this);
32866     },
32867     
32868     validateValue : function(value)
32869     {
32870         
32871         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32872             return false;
32873         }
32874         
32875         var num = this.parseValue(value);
32876         
32877         if(isNaN(num)){
32878             this.markInvalid(String.format(this.nanText, value));
32879             return false;
32880         }
32881         
32882         if(num < this.minValue){
32883             this.markInvalid(String.format(this.minText, this.minValue));
32884             return false;
32885         }
32886         
32887         if(num > this.maxValue){
32888             this.markInvalid(String.format(this.maxText, this.maxValue));
32889             return false;
32890         }
32891         
32892         return true;
32893     },
32894
32895     getValue : function()
32896     {
32897         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32898     },
32899
32900     parseValue : function(value)
32901     {
32902         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32903         return isNaN(value) ? '' : value;
32904     },
32905
32906     fixPrecision : function(value)
32907     {
32908         var nan = isNaN(value);
32909         
32910         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32911             return nan ? '' : value;
32912         }
32913         return parseFloat(value).toFixed(this.decimalPrecision);
32914     },
32915
32916     setValue : function(v)
32917     {
32918         v = this.fixPrecision(v);
32919         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32920     },
32921
32922     decimalPrecisionFcn : function(v)
32923     {
32924         return Math.floor(v);
32925     },
32926
32927     beforeBlur : function()
32928     {
32929         if(!this.castInt){
32930             return;
32931         }
32932         
32933         var v = this.parseValue(this.getRawValue());
32934         if(v){
32935             this.setValue(v);
32936         }
32937     }
32938     
32939 });
32940
32941  
32942
32943 /*
32944 * Licence: LGPL
32945 */
32946
32947 /**
32948  * @class Roo.bootstrap.DocumentSlider
32949  * @extends Roo.bootstrap.Component
32950  * Bootstrap DocumentSlider class
32951  * 
32952  * @constructor
32953  * Create a new DocumentViewer
32954  * @param {Object} config The config object
32955  */
32956
32957 Roo.bootstrap.DocumentSlider = function(config){
32958     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32959     
32960     this.files = [];
32961     
32962     this.addEvents({
32963         /**
32964          * @event initial
32965          * Fire after initEvent
32966          * @param {Roo.bootstrap.DocumentSlider} this
32967          */
32968         "initial" : true,
32969         /**
32970          * @event update
32971          * Fire after update
32972          * @param {Roo.bootstrap.DocumentSlider} this
32973          */
32974         "update" : true,
32975         /**
32976          * @event click
32977          * Fire after click
32978          * @param {Roo.bootstrap.DocumentSlider} this
32979          */
32980         "click" : true
32981     });
32982 };
32983
32984 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32985     
32986     files : false,
32987     
32988     indicator : 0,
32989     
32990     getAutoCreate : function()
32991     {
32992         var cfg = {
32993             tag : 'div',
32994             cls : 'roo-document-slider',
32995             cn : [
32996                 {
32997                     tag : 'div',
32998                     cls : 'roo-document-slider-header',
32999                     cn : [
33000                         {
33001                             tag : 'div',
33002                             cls : 'roo-document-slider-header-title'
33003                         }
33004                     ]
33005                 },
33006                 {
33007                     tag : 'div',
33008                     cls : 'roo-document-slider-body',
33009                     cn : [
33010                         {
33011                             tag : 'div',
33012                             cls : 'roo-document-slider-prev',
33013                             cn : [
33014                                 {
33015                                     tag : 'i',
33016                                     cls : 'fa fa-chevron-left'
33017                                 }
33018                             ]
33019                         },
33020                         {
33021                             tag : 'div',
33022                             cls : 'roo-document-slider-thumb',
33023                             cn : [
33024                                 {
33025                                     tag : 'img',
33026                                     cls : 'roo-document-slider-image'
33027                                 }
33028                             ]
33029                         },
33030                         {
33031                             tag : 'div',
33032                             cls : 'roo-document-slider-next',
33033                             cn : [
33034                                 {
33035                                     tag : 'i',
33036                                     cls : 'fa fa-chevron-right'
33037                                 }
33038                             ]
33039                         }
33040                     ]
33041                 }
33042             ]
33043         };
33044         
33045         return cfg;
33046     },
33047     
33048     initEvents : function()
33049     {
33050         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33051         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33052         
33053         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33054         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33055         
33056         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33057         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33058         
33059         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33060         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33061         
33062         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33063         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33064         
33065         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33066         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33067         
33068         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33069         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33070         
33071         this.thumbEl.on('click', this.onClick, this);
33072         
33073         this.prevIndicator.on('click', this.prev, this);
33074         
33075         this.nextIndicator.on('click', this.next, this);
33076         
33077     },
33078     
33079     initial : function()
33080     {
33081         if(this.files.length){
33082             this.indicator = 1;
33083             this.update()
33084         }
33085         
33086         this.fireEvent('initial', this);
33087     },
33088     
33089     update : function()
33090     {
33091         this.imageEl.attr('src', this.files[this.indicator - 1]);
33092         
33093         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33094         
33095         this.prevIndicator.show();
33096         
33097         if(this.indicator == 1){
33098             this.prevIndicator.hide();
33099         }
33100         
33101         this.nextIndicator.show();
33102         
33103         if(this.indicator == this.files.length){
33104             this.nextIndicator.hide();
33105         }
33106         
33107         this.thumbEl.scrollTo('top');
33108         
33109         this.fireEvent('update', this);
33110     },
33111     
33112     onClick : function(e)
33113     {
33114         e.preventDefault();
33115         
33116         this.fireEvent('click', this);
33117     },
33118     
33119     prev : function(e)
33120     {
33121         e.preventDefault();
33122         
33123         this.indicator = Math.max(1, this.indicator - 1);
33124         
33125         this.update();
33126     },
33127     
33128     next : function(e)
33129     {
33130         e.preventDefault();
33131         
33132         this.indicator = Math.min(this.files.length, this.indicator + 1);
33133         
33134         this.update();
33135     }
33136 });
33137 /*
33138  * - LGPL
33139  *
33140  * RadioSet
33141  *
33142  *
33143  */
33144
33145 /**
33146  * @class Roo.bootstrap.RadioSet
33147  * @extends Roo.bootstrap.Input
33148  * Bootstrap RadioSet class
33149  * @cfg {String} indicatorpos (left|right) default left
33150  * @cfg {Boolean} inline (true|false) inline the element (default true)
33151  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33152  * @constructor
33153  * Create a new RadioSet
33154  * @param {Object} config The config object
33155  */
33156
33157 Roo.bootstrap.RadioSet = function(config){
33158     
33159     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33160     
33161     this.radioes = [];
33162     
33163     Roo.bootstrap.RadioSet.register(this);
33164     
33165     this.addEvents({
33166         /**
33167         * @event check
33168         * Fires when the element is checked or unchecked.
33169         * @param {Roo.bootstrap.RadioSet} this This radio
33170         * @param {Roo.bootstrap.Radio} item The checked item
33171         */
33172        check : true
33173     });
33174     
33175 };
33176
33177 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33178
33179     radioes : false,
33180     
33181     inline : true,
33182     
33183     weight : '',
33184     
33185     indicatorpos : 'left',
33186     
33187     getAutoCreate : function()
33188     {
33189         var label = {
33190             tag : 'label',
33191             cls : 'roo-radio-set-label',
33192             cn : [
33193                 {
33194                     tag : 'span',
33195                     html : this.fieldLabel
33196                 }
33197             ]
33198         };
33199         
33200         if(this.indicatorpos == 'left'){
33201             label.cn.unshift({
33202                 tag : 'i',
33203                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33204                 tooltip : 'This field is required'
33205             });
33206         } else {
33207             label.cn.push({
33208                 tag : 'i',
33209                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33210                 tooltip : 'This field is required'
33211             });
33212         }
33213         
33214         var items = {
33215             tag : 'div',
33216             cls : 'roo-radio-set-items'
33217         };
33218         
33219         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33220         
33221         if (align === 'left' && this.fieldLabel.length) {
33222             
33223             items = {
33224                 cls : "roo-radio-set-right", 
33225                 cn: [
33226                     items
33227                 ]
33228             };
33229             
33230             if(this.labelWidth > 12){
33231                 label.style = "width: " + this.labelWidth + 'px';
33232             }
33233             
33234             if(this.labelWidth < 13 && this.labelmd == 0){
33235                 this.labelmd = this.labelWidth;
33236             }
33237             
33238             if(this.labellg > 0){
33239                 label.cls += ' col-lg-' + this.labellg;
33240                 items.cls += ' col-lg-' + (12 - this.labellg);
33241             }
33242             
33243             if(this.labelmd > 0){
33244                 label.cls += ' col-md-' + this.labelmd;
33245                 items.cls += ' col-md-' + (12 - this.labelmd);
33246             }
33247             
33248             if(this.labelsm > 0){
33249                 label.cls += ' col-sm-' + this.labelsm;
33250                 items.cls += ' col-sm-' + (12 - this.labelsm);
33251             }
33252             
33253             if(this.labelxs > 0){
33254                 label.cls += ' col-xs-' + this.labelxs;
33255                 items.cls += ' col-xs-' + (12 - this.labelxs);
33256             }
33257         }
33258         
33259         var cfg = {
33260             tag : 'div',
33261             cls : 'roo-radio-set',
33262             cn : [
33263                 {
33264                     tag : 'input',
33265                     cls : 'roo-radio-set-input',
33266                     type : 'hidden',
33267                     name : this.name,
33268                     value : this.value ? this.value :  ''
33269                 },
33270                 label,
33271                 items
33272             ]
33273         };
33274         
33275         if(this.weight.length){
33276             cfg.cls += ' roo-radio-' + this.weight;
33277         }
33278         
33279         if(this.inline) {
33280             cfg.cls += ' roo-radio-set-inline';
33281         }
33282         
33283         return cfg;
33284         
33285     },
33286
33287     initEvents : function()
33288     {
33289         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33290         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33291         
33292         if(!this.fieldLabel.length){
33293             this.labelEl.hide();
33294         }
33295         
33296         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33297         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33298         
33299         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33300         this.indicatorEl().hide();
33301         
33302         this.originalValue = this.getValue();
33303         
33304     },
33305     
33306     inputEl: function ()
33307     {
33308         return this.el.select('.roo-radio-set-input', true).first();
33309     },
33310     
33311     getChildContainer : function()
33312     {
33313         return this.itemsEl;
33314     },
33315     
33316     register : function(item)
33317     {
33318         this.radioes.push(item);
33319         
33320     },
33321     
33322     validate : function()
33323     {   
33324         var valid = false;
33325         
33326         Roo.each(this.radioes, function(i){
33327             if(!i.checked){
33328                 return;
33329             }
33330             
33331             valid = true;
33332             return false;
33333         });
33334         
33335         if(this.allowBlank) {
33336             return true;
33337         }
33338         
33339         if(this.disabled || valid){
33340             this.markValid();
33341             return true;
33342         }
33343         
33344         this.markInvalid();
33345         return false;
33346         
33347     },
33348     
33349     markValid : function()
33350     {
33351         if(this.labelEl.isVisible(true)){
33352             this.indicatorEl().hide();
33353         }
33354         
33355         this.el.removeClass([this.invalidClass, this.validClass]);
33356         this.el.addClass(this.validClass);
33357         
33358         this.fireEvent('valid', this);
33359     },
33360     
33361     markInvalid : function(msg)
33362     {
33363         if(this.allowBlank || this.disabled){
33364             return;
33365         }
33366         
33367         if(this.labelEl.isVisible(true)){
33368             this.indicatorEl().show();
33369         }
33370         
33371         this.el.removeClass([this.invalidClass, this.validClass]);
33372         this.el.addClass(this.invalidClass);
33373         
33374         this.fireEvent('invalid', this, msg);
33375         
33376     },
33377     
33378     setValue : function(v, suppressEvent)
33379     {   
33380         this.value = v;
33381         if(this.rendered){
33382             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33383         }
33384         
33385         Roo.each(this.radioes, function(i){
33386             
33387             i.checked = false;
33388             i.el.removeClass('checked');
33389             
33390             if(i.value === v || i.value.toString() === v.toString()){
33391                 i.checked = true;
33392                 i.el.addClass('checked');
33393                 
33394                 if(suppressEvent !== true){
33395                     this.fireEvent('check', this, i);
33396                 }
33397             }
33398             
33399         }, this);
33400         
33401         this.validate();
33402     },
33403     
33404     clearInvalid : function(){
33405         
33406         if(!this.el || this.preventMark){
33407             return;
33408         }
33409         
33410         this.el.removeClass([this.invalidClass]);
33411         
33412         this.fireEvent('valid', this);
33413     }
33414     
33415 });
33416
33417 Roo.apply(Roo.bootstrap.RadioSet, {
33418     
33419     groups: {},
33420     
33421     register : function(set)
33422     {
33423         this.groups[set.name] = set;
33424     },
33425     
33426     get: function(name) 
33427     {
33428         if (typeof(this.groups[name]) == 'undefined') {
33429             return false;
33430         }
33431         
33432         return this.groups[name] ;
33433     }
33434     
33435 });
33436 /*
33437  * Based on:
33438  * Ext JS Library 1.1.1
33439  * Copyright(c) 2006-2007, Ext JS, LLC.
33440  *
33441  * Originally Released Under LGPL - original licence link has changed is not relivant.
33442  *
33443  * Fork - LGPL
33444  * <script type="text/javascript">
33445  */
33446
33447
33448 /**
33449  * @class Roo.bootstrap.SplitBar
33450  * @extends Roo.util.Observable
33451  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33452  * <br><br>
33453  * Usage:
33454  * <pre><code>
33455 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33456                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33457 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33458 split.minSize = 100;
33459 split.maxSize = 600;
33460 split.animate = true;
33461 split.on('moved', splitterMoved);
33462 </code></pre>
33463  * @constructor
33464  * Create a new SplitBar
33465  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33466  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33467  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33468  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33469                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33470                         position of the SplitBar).
33471  */
33472 Roo.bootstrap.SplitBar = function(cfg){
33473     
33474     /** @private */
33475     
33476     //{
33477     //  dragElement : elm
33478     //  resizingElement: el,
33479         // optional..
33480     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33481     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33482         // existingProxy ???
33483     //}
33484     
33485     this.el = Roo.get(cfg.dragElement, true);
33486     this.el.dom.unselectable = "on";
33487     /** @private */
33488     this.resizingEl = Roo.get(cfg.resizingElement, true);
33489
33490     /**
33491      * @private
33492      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33493      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33494      * @type Number
33495      */
33496     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33497     
33498     /**
33499      * The minimum size of the resizing element. (Defaults to 0)
33500      * @type Number
33501      */
33502     this.minSize = 0;
33503     
33504     /**
33505      * The maximum size of the resizing element. (Defaults to 2000)
33506      * @type Number
33507      */
33508     this.maxSize = 2000;
33509     
33510     /**
33511      * Whether to animate the transition to the new size
33512      * @type Boolean
33513      */
33514     this.animate = false;
33515     
33516     /**
33517      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33518      * @type Boolean
33519      */
33520     this.useShim = false;
33521     
33522     /** @private */
33523     this.shim = null;
33524     
33525     if(!cfg.existingProxy){
33526         /** @private */
33527         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33528     }else{
33529         this.proxy = Roo.get(cfg.existingProxy).dom;
33530     }
33531     /** @private */
33532     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33533     
33534     /** @private */
33535     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33536     
33537     /** @private */
33538     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33539     
33540     /** @private */
33541     this.dragSpecs = {};
33542     
33543     /**
33544      * @private The adapter to use to positon and resize elements
33545      */
33546     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33547     this.adapter.init(this);
33548     
33549     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33550         /** @private */
33551         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33552         this.el.addClass("roo-splitbar-h");
33553     }else{
33554         /** @private */
33555         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33556         this.el.addClass("roo-splitbar-v");
33557     }
33558     
33559     this.addEvents({
33560         /**
33561          * @event resize
33562          * Fires when the splitter is moved (alias for {@link #event-moved})
33563          * @param {Roo.bootstrap.SplitBar} this
33564          * @param {Number} newSize the new width or height
33565          */
33566         "resize" : true,
33567         /**
33568          * @event moved
33569          * Fires when the splitter is moved
33570          * @param {Roo.bootstrap.SplitBar} this
33571          * @param {Number} newSize the new width or height
33572          */
33573         "moved" : true,
33574         /**
33575          * @event beforeresize
33576          * Fires before the splitter is dragged
33577          * @param {Roo.bootstrap.SplitBar} this
33578          */
33579         "beforeresize" : true,
33580
33581         "beforeapply" : true
33582     });
33583
33584     Roo.util.Observable.call(this);
33585 };
33586
33587 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33588     onStartProxyDrag : function(x, y){
33589         this.fireEvent("beforeresize", this);
33590         if(!this.overlay){
33591             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33592             o.unselectable();
33593             o.enableDisplayMode("block");
33594             // all splitbars share the same overlay
33595             Roo.bootstrap.SplitBar.prototype.overlay = o;
33596         }
33597         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33598         this.overlay.show();
33599         Roo.get(this.proxy).setDisplayed("block");
33600         var size = this.adapter.getElementSize(this);
33601         this.activeMinSize = this.getMinimumSize();;
33602         this.activeMaxSize = this.getMaximumSize();;
33603         var c1 = size - this.activeMinSize;
33604         var c2 = Math.max(this.activeMaxSize - size, 0);
33605         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33606             this.dd.resetConstraints();
33607             this.dd.setXConstraint(
33608                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33609                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33610             );
33611             this.dd.setYConstraint(0, 0);
33612         }else{
33613             this.dd.resetConstraints();
33614             this.dd.setXConstraint(0, 0);
33615             this.dd.setYConstraint(
33616                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33617                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33618             );
33619          }
33620         this.dragSpecs.startSize = size;
33621         this.dragSpecs.startPoint = [x, y];
33622         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33623     },
33624     
33625     /** 
33626      * @private Called after the drag operation by the DDProxy
33627      */
33628     onEndProxyDrag : function(e){
33629         Roo.get(this.proxy).setDisplayed(false);
33630         var endPoint = Roo.lib.Event.getXY(e);
33631         if(this.overlay){
33632             this.overlay.hide();
33633         }
33634         var newSize;
33635         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33636             newSize = this.dragSpecs.startSize + 
33637                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33638                     endPoint[0] - this.dragSpecs.startPoint[0] :
33639                     this.dragSpecs.startPoint[0] - endPoint[0]
33640                 );
33641         }else{
33642             newSize = this.dragSpecs.startSize + 
33643                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33644                     endPoint[1] - this.dragSpecs.startPoint[1] :
33645                     this.dragSpecs.startPoint[1] - endPoint[1]
33646                 );
33647         }
33648         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33649         if(newSize != this.dragSpecs.startSize){
33650             if(this.fireEvent('beforeapply', this, newSize) !== false){
33651                 this.adapter.setElementSize(this, newSize);
33652                 this.fireEvent("moved", this, newSize);
33653                 this.fireEvent("resize", this, newSize);
33654             }
33655         }
33656     },
33657     
33658     /**
33659      * Get the adapter this SplitBar uses
33660      * @return The adapter object
33661      */
33662     getAdapter : function(){
33663         return this.adapter;
33664     },
33665     
33666     /**
33667      * Set the adapter this SplitBar uses
33668      * @param {Object} adapter A SplitBar adapter object
33669      */
33670     setAdapter : function(adapter){
33671         this.adapter = adapter;
33672         this.adapter.init(this);
33673     },
33674     
33675     /**
33676      * Gets the minimum size for the resizing element
33677      * @return {Number} The minimum size
33678      */
33679     getMinimumSize : function(){
33680         return this.minSize;
33681     },
33682     
33683     /**
33684      * Sets the minimum size for the resizing element
33685      * @param {Number} minSize The minimum size
33686      */
33687     setMinimumSize : function(minSize){
33688         this.minSize = minSize;
33689     },
33690     
33691     /**
33692      * Gets the maximum size for the resizing element
33693      * @return {Number} The maximum size
33694      */
33695     getMaximumSize : function(){
33696         return this.maxSize;
33697     },
33698     
33699     /**
33700      * Sets the maximum size for the resizing element
33701      * @param {Number} maxSize The maximum size
33702      */
33703     setMaximumSize : function(maxSize){
33704         this.maxSize = maxSize;
33705     },
33706     
33707     /**
33708      * Sets the initialize size for the resizing element
33709      * @param {Number} size The initial size
33710      */
33711     setCurrentSize : function(size){
33712         var oldAnimate = this.animate;
33713         this.animate = false;
33714         this.adapter.setElementSize(this, size);
33715         this.animate = oldAnimate;
33716     },
33717     
33718     /**
33719      * Destroy this splitbar. 
33720      * @param {Boolean} removeEl True to remove the element
33721      */
33722     destroy : function(removeEl){
33723         if(this.shim){
33724             this.shim.remove();
33725         }
33726         this.dd.unreg();
33727         this.proxy.parentNode.removeChild(this.proxy);
33728         if(removeEl){
33729             this.el.remove();
33730         }
33731     }
33732 });
33733
33734 /**
33735  * @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.
33736  */
33737 Roo.bootstrap.SplitBar.createProxy = function(dir){
33738     var proxy = new Roo.Element(document.createElement("div"));
33739     proxy.unselectable();
33740     var cls = 'roo-splitbar-proxy';
33741     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33742     document.body.appendChild(proxy.dom);
33743     return proxy.dom;
33744 };
33745
33746 /** 
33747  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33748  * Default Adapter. It assumes the splitter and resizing element are not positioned
33749  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33750  */
33751 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33752 };
33753
33754 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33755     // do nothing for now
33756     init : function(s){
33757     
33758     },
33759     /**
33760      * Called before drag operations to get the current size of the resizing element. 
33761      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33762      */
33763      getElementSize : function(s){
33764         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33765             return s.resizingEl.getWidth();
33766         }else{
33767             return s.resizingEl.getHeight();
33768         }
33769     },
33770     
33771     /**
33772      * Called after drag operations to set the size of the resizing element.
33773      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33774      * @param {Number} newSize The new size to set
33775      * @param {Function} onComplete A function to be invoked when resizing is complete
33776      */
33777     setElementSize : function(s, newSize, onComplete){
33778         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33779             if(!s.animate){
33780                 s.resizingEl.setWidth(newSize);
33781                 if(onComplete){
33782                     onComplete(s, newSize);
33783                 }
33784             }else{
33785                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33786             }
33787         }else{
33788             
33789             if(!s.animate){
33790                 s.resizingEl.setHeight(newSize);
33791                 if(onComplete){
33792                     onComplete(s, newSize);
33793                 }
33794             }else{
33795                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33796             }
33797         }
33798     }
33799 };
33800
33801 /** 
33802  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33803  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33804  * Adapter that  moves the splitter element to align with the resized sizing element. 
33805  * Used with an absolute positioned SplitBar.
33806  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33807  * document.body, make sure you assign an id to the body element.
33808  */
33809 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33810     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33811     this.container = Roo.get(container);
33812 };
33813
33814 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33815     init : function(s){
33816         this.basic.init(s);
33817     },
33818     
33819     getElementSize : function(s){
33820         return this.basic.getElementSize(s);
33821     },
33822     
33823     setElementSize : function(s, newSize, onComplete){
33824         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33825     },
33826     
33827     moveSplitter : function(s){
33828         var yes = Roo.bootstrap.SplitBar;
33829         switch(s.placement){
33830             case yes.LEFT:
33831                 s.el.setX(s.resizingEl.getRight());
33832                 break;
33833             case yes.RIGHT:
33834                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33835                 break;
33836             case yes.TOP:
33837                 s.el.setY(s.resizingEl.getBottom());
33838                 break;
33839             case yes.BOTTOM:
33840                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33841                 break;
33842         }
33843     }
33844 };
33845
33846 /**
33847  * Orientation constant - Create a vertical SplitBar
33848  * @static
33849  * @type Number
33850  */
33851 Roo.bootstrap.SplitBar.VERTICAL = 1;
33852
33853 /**
33854  * Orientation constant - Create a horizontal SplitBar
33855  * @static
33856  * @type Number
33857  */
33858 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33859
33860 /**
33861  * Placement constant - The resizing element is to the left of the splitter element
33862  * @static
33863  * @type Number
33864  */
33865 Roo.bootstrap.SplitBar.LEFT = 1;
33866
33867 /**
33868  * Placement constant - The resizing element is to the right of the splitter element
33869  * @static
33870  * @type Number
33871  */
33872 Roo.bootstrap.SplitBar.RIGHT = 2;
33873
33874 /**
33875  * Placement constant - The resizing element is positioned above the splitter element
33876  * @static
33877  * @type Number
33878  */
33879 Roo.bootstrap.SplitBar.TOP = 3;
33880
33881 /**
33882  * Placement constant - The resizing element is positioned under splitter element
33883  * @static
33884  * @type Number
33885  */
33886 Roo.bootstrap.SplitBar.BOTTOM = 4;
33887 Roo.namespace("Roo.bootstrap.layout");/*
33888  * Based on:
33889  * Ext JS Library 1.1.1
33890  * Copyright(c) 2006-2007, Ext JS, LLC.
33891  *
33892  * Originally Released Under LGPL - original licence link has changed is not relivant.
33893  *
33894  * Fork - LGPL
33895  * <script type="text/javascript">
33896  */
33897
33898 /**
33899  * @class Roo.bootstrap.layout.Manager
33900  * @extends Roo.bootstrap.Component
33901  * Base class for layout managers.
33902  */
33903 Roo.bootstrap.layout.Manager = function(config)
33904 {
33905     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33906
33907
33908
33909
33910
33911     /** false to disable window resize monitoring @type Boolean */
33912     this.monitorWindowResize = true;
33913     this.regions = {};
33914     this.addEvents({
33915         /**
33916          * @event layout
33917          * Fires when a layout is performed.
33918          * @param {Roo.LayoutManager} this
33919          */
33920         "layout" : true,
33921         /**
33922          * @event regionresized
33923          * Fires when the user resizes a region.
33924          * @param {Roo.LayoutRegion} region The resized region
33925          * @param {Number} newSize The new size (width for east/west, height for north/south)
33926          */
33927         "regionresized" : true,
33928         /**
33929          * @event regioncollapsed
33930          * Fires when a region is collapsed.
33931          * @param {Roo.LayoutRegion} region The collapsed region
33932          */
33933         "regioncollapsed" : true,
33934         /**
33935          * @event regionexpanded
33936          * Fires when a region is expanded.
33937          * @param {Roo.LayoutRegion} region The expanded region
33938          */
33939         "regionexpanded" : true
33940     });
33941     this.updating = false;
33942
33943     if (config.el) {
33944         this.el = Roo.get(config.el);
33945         this.initEvents();
33946     }
33947
33948 };
33949
33950 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33951
33952
33953     regions : null,
33954
33955     monitorWindowResize : true,
33956
33957
33958     updating : false,
33959
33960
33961     onRender : function(ct, position)
33962     {
33963         if(!this.el){
33964             this.el = Roo.get(ct);
33965             this.initEvents();
33966         }
33967         //this.fireEvent('render',this);
33968     },
33969
33970
33971     initEvents: function()
33972     {
33973
33974
33975         // ie scrollbar fix
33976         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33977             document.body.scroll = "no";
33978         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33979             this.el.position('relative');
33980         }
33981         this.id = this.el.id;
33982         this.el.addClass("roo-layout-container");
33983         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33984         if(this.el.dom != document.body ) {
33985             this.el.on('resize', this.layout,this);
33986             this.el.on('show', this.layout,this);
33987         }
33988
33989     },
33990
33991     /**
33992      * Returns true if this layout is currently being updated
33993      * @return {Boolean}
33994      */
33995     isUpdating : function(){
33996         return this.updating;
33997     },
33998
33999     /**
34000      * Suspend the LayoutManager from doing auto-layouts while
34001      * making multiple add or remove calls
34002      */
34003     beginUpdate : function(){
34004         this.updating = true;
34005     },
34006
34007     /**
34008      * Restore auto-layouts and optionally disable the manager from performing a layout
34009      * @param {Boolean} noLayout true to disable a layout update
34010      */
34011     endUpdate : function(noLayout){
34012         this.updating = false;
34013         if(!noLayout){
34014             this.layout();
34015         }
34016     },
34017
34018     layout: function(){
34019         // abstract...
34020     },
34021
34022     onRegionResized : function(region, newSize){
34023         this.fireEvent("regionresized", region, newSize);
34024         this.layout();
34025     },
34026
34027     onRegionCollapsed : function(region){
34028         this.fireEvent("regioncollapsed", region);
34029     },
34030
34031     onRegionExpanded : function(region){
34032         this.fireEvent("regionexpanded", region);
34033     },
34034
34035     /**
34036      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34037      * performs box-model adjustments.
34038      * @return {Object} The size as an object {width: (the width), height: (the height)}
34039      */
34040     getViewSize : function()
34041     {
34042         var size;
34043         if(this.el.dom != document.body){
34044             size = this.el.getSize();
34045         }else{
34046             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34047         }
34048         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34049         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34050         return size;
34051     },
34052
34053     /**
34054      * Returns the Element this layout is bound to.
34055      * @return {Roo.Element}
34056      */
34057     getEl : function(){
34058         return this.el;
34059     },
34060
34061     /**
34062      * Returns the specified region.
34063      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34064      * @return {Roo.LayoutRegion}
34065      */
34066     getRegion : function(target){
34067         return this.regions[target.toLowerCase()];
34068     },
34069
34070     onWindowResize : function(){
34071         if(this.monitorWindowResize){
34072             this.layout();
34073         }
34074     }
34075 });
34076 /*
34077  * Based on:
34078  * Ext JS Library 1.1.1
34079  * Copyright(c) 2006-2007, Ext JS, LLC.
34080  *
34081  * Originally Released Under LGPL - original licence link has changed is not relivant.
34082  *
34083  * Fork - LGPL
34084  * <script type="text/javascript">
34085  */
34086 /**
34087  * @class Roo.bootstrap.layout.Border
34088  * @extends Roo.bootstrap.layout.Manager
34089  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34090  * please see: examples/bootstrap/nested.html<br><br>
34091  
34092 <b>The container the layout is rendered into can be either the body element or any other element.
34093 If it is not the body element, the container needs to either be an absolute positioned element,
34094 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34095 the container size if it is not the body element.</b>
34096
34097 * @constructor
34098 * Create a new Border
34099 * @param {Object} config Configuration options
34100  */
34101 Roo.bootstrap.layout.Border = function(config){
34102     config = config || {};
34103     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34104     
34105     
34106     
34107     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34108         if(config[region]){
34109             config[region].region = region;
34110             this.addRegion(config[region]);
34111         }
34112     },this);
34113     
34114 };
34115
34116 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34117
34118 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34119     /**
34120      * Creates and adds a new region if it doesn't already exist.
34121      * @param {String} target The target region key (north, south, east, west or center).
34122      * @param {Object} config The regions config object
34123      * @return {BorderLayoutRegion} The new region
34124      */
34125     addRegion : function(config)
34126     {
34127         if(!this.regions[config.region]){
34128             var r = this.factory(config);
34129             this.bindRegion(r);
34130         }
34131         return this.regions[config.region];
34132     },
34133
34134     // private (kinda)
34135     bindRegion : function(r){
34136         this.regions[r.config.region] = r;
34137         
34138         r.on("visibilitychange",    this.layout, this);
34139         r.on("paneladded",          this.layout, this);
34140         r.on("panelremoved",        this.layout, this);
34141         r.on("invalidated",         this.layout, this);
34142         r.on("resized",             this.onRegionResized, this);
34143         r.on("collapsed",           this.onRegionCollapsed, this);
34144         r.on("expanded",            this.onRegionExpanded, this);
34145     },
34146
34147     /**
34148      * Performs a layout update.
34149      */
34150     layout : function()
34151     {
34152         if(this.updating) {
34153             return;
34154         }
34155         
34156         // render all the rebions if they have not been done alreayd?
34157         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34158             if(this.regions[region] && !this.regions[region].bodyEl){
34159                 this.regions[region].onRender(this.el)
34160             }
34161         },this);
34162         
34163         var size = this.getViewSize();
34164         var w = size.width;
34165         var h = size.height;
34166         var centerW = w;
34167         var centerH = h;
34168         var centerY = 0;
34169         var centerX = 0;
34170         //var x = 0, y = 0;
34171
34172         var rs = this.regions;
34173         var north = rs["north"];
34174         var south = rs["south"]; 
34175         var west = rs["west"];
34176         var east = rs["east"];
34177         var center = rs["center"];
34178         //if(this.hideOnLayout){ // not supported anymore
34179             //c.el.setStyle("display", "none");
34180         //}
34181         if(north && north.isVisible()){
34182             var b = north.getBox();
34183             var m = north.getMargins();
34184             b.width = w - (m.left+m.right);
34185             b.x = m.left;
34186             b.y = m.top;
34187             centerY = b.height + b.y + m.bottom;
34188             centerH -= centerY;
34189             north.updateBox(this.safeBox(b));
34190         }
34191         if(south && south.isVisible()){
34192             var b = south.getBox();
34193             var m = south.getMargins();
34194             b.width = w - (m.left+m.right);
34195             b.x = m.left;
34196             var totalHeight = (b.height + m.top + m.bottom);
34197             b.y = h - totalHeight + m.top;
34198             centerH -= totalHeight;
34199             south.updateBox(this.safeBox(b));
34200         }
34201         if(west && west.isVisible()){
34202             var b = west.getBox();
34203             var m = west.getMargins();
34204             b.height = centerH - (m.top+m.bottom);
34205             b.x = m.left;
34206             b.y = centerY + m.top;
34207             var totalWidth = (b.width + m.left + m.right);
34208             centerX += totalWidth;
34209             centerW -= totalWidth;
34210             west.updateBox(this.safeBox(b));
34211         }
34212         if(east && east.isVisible()){
34213             var b = east.getBox();
34214             var m = east.getMargins();
34215             b.height = centerH - (m.top+m.bottom);
34216             var totalWidth = (b.width + m.left + m.right);
34217             b.x = w - totalWidth + m.left;
34218             b.y = centerY + m.top;
34219             centerW -= totalWidth;
34220             east.updateBox(this.safeBox(b));
34221         }
34222         if(center){
34223             var m = center.getMargins();
34224             var centerBox = {
34225                 x: centerX + m.left,
34226                 y: centerY + m.top,
34227                 width: centerW - (m.left+m.right),
34228                 height: centerH - (m.top+m.bottom)
34229             };
34230             //if(this.hideOnLayout){
34231                 //center.el.setStyle("display", "block");
34232             //}
34233             center.updateBox(this.safeBox(centerBox));
34234         }
34235         this.el.repaint();
34236         this.fireEvent("layout", this);
34237     },
34238
34239     // private
34240     safeBox : function(box){
34241         box.width = Math.max(0, box.width);
34242         box.height = Math.max(0, box.height);
34243         return box;
34244     },
34245
34246     /**
34247      * Adds a ContentPanel (or subclass) to this layout.
34248      * @param {String} target The target region key (north, south, east, west or center).
34249      * @param {Roo.ContentPanel} panel The panel to add
34250      * @return {Roo.ContentPanel} The added panel
34251      */
34252     add : function(target, panel){
34253          
34254         target = target.toLowerCase();
34255         return this.regions[target].add(panel);
34256     },
34257
34258     /**
34259      * Remove a ContentPanel (or subclass) to this layout.
34260      * @param {String} target The target region key (north, south, east, west or center).
34261      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34262      * @return {Roo.ContentPanel} The removed panel
34263      */
34264     remove : function(target, panel){
34265         target = target.toLowerCase();
34266         return this.regions[target].remove(panel);
34267     },
34268
34269     /**
34270      * Searches all regions for a panel with the specified id
34271      * @param {String} panelId
34272      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34273      */
34274     findPanel : function(panelId){
34275         var rs = this.regions;
34276         for(var target in rs){
34277             if(typeof rs[target] != "function"){
34278                 var p = rs[target].getPanel(panelId);
34279                 if(p){
34280                     return p;
34281                 }
34282             }
34283         }
34284         return null;
34285     },
34286
34287     /**
34288      * Searches all regions for a panel with the specified id and activates (shows) it.
34289      * @param {String/ContentPanel} panelId The panels id or the panel itself
34290      * @return {Roo.ContentPanel} The shown panel or null
34291      */
34292     showPanel : function(panelId) {
34293       var rs = this.regions;
34294       for(var target in rs){
34295          var r = rs[target];
34296          if(typeof r != "function"){
34297             if(r.hasPanel(panelId)){
34298                return r.showPanel(panelId);
34299             }
34300          }
34301       }
34302       return null;
34303    },
34304
34305    /**
34306      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34307      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34308      */
34309    /*
34310     restoreState : function(provider){
34311         if(!provider){
34312             provider = Roo.state.Manager;
34313         }
34314         var sm = new Roo.LayoutStateManager();
34315         sm.init(this, provider);
34316     },
34317 */
34318  
34319  
34320     /**
34321      * Adds a xtype elements to the layout.
34322      * <pre><code>
34323
34324 layout.addxtype({
34325        xtype : 'ContentPanel',
34326        region: 'west',
34327        items: [ .... ]
34328    }
34329 );
34330
34331 layout.addxtype({
34332         xtype : 'NestedLayoutPanel',
34333         region: 'west',
34334         layout: {
34335            center: { },
34336            west: { }   
34337         },
34338         items : [ ... list of content panels or nested layout panels.. ]
34339    }
34340 );
34341 </code></pre>
34342      * @param {Object} cfg Xtype definition of item to add.
34343      */
34344     addxtype : function(cfg)
34345     {
34346         // basically accepts a pannel...
34347         // can accept a layout region..!?!?
34348         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34349         
34350         
34351         // theory?  children can only be panels??
34352         
34353         //if (!cfg.xtype.match(/Panel$/)) {
34354         //    return false;
34355         //}
34356         var ret = false;
34357         
34358         if (typeof(cfg.region) == 'undefined') {
34359             Roo.log("Failed to add Panel, region was not set");
34360             Roo.log(cfg);
34361             return false;
34362         }
34363         var region = cfg.region;
34364         delete cfg.region;
34365         
34366           
34367         var xitems = [];
34368         if (cfg.items) {
34369             xitems = cfg.items;
34370             delete cfg.items;
34371         }
34372         var nb = false;
34373         
34374         switch(cfg.xtype) 
34375         {
34376             case 'Content':  // ContentPanel (el, cfg)
34377             case 'Scroll':  // ContentPanel (el, cfg)
34378             case 'View': 
34379                 cfg.autoCreate = true;
34380                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34381                 //} else {
34382                 //    var el = this.el.createChild();
34383                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34384                 //}
34385                 
34386                 this.add(region, ret);
34387                 break;
34388             
34389             /*
34390             case 'TreePanel': // our new panel!
34391                 cfg.el = this.el.createChild();
34392                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34393                 this.add(region, ret);
34394                 break;
34395             */
34396             
34397             case 'Nest': 
34398                 // create a new Layout (which is  a Border Layout...
34399                 
34400                 var clayout = cfg.layout;
34401                 clayout.el  = this.el.createChild();
34402                 clayout.items   = clayout.items  || [];
34403                 
34404                 delete cfg.layout;
34405                 
34406                 // replace this exitems with the clayout ones..
34407                 xitems = clayout.items;
34408                  
34409                 // force background off if it's in center...
34410                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34411                     cfg.background = false;
34412                 }
34413                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34414                 
34415                 
34416                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34417                 //console.log('adding nested layout panel '  + cfg.toSource());
34418                 this.add(region, ret);
34419                 nb = {}; /// find first...
34420                 break;
34421             
34422             case 'Grid':
34423                 
34424                 // needs grid and region
34425                 
34426                 //var el = this.getRegion(region).el.createChild();
34427                 /*
34428                  *var el = this.el.createChild();
34429                 // create the grid first...
34430                 cfg.grid.container = el;
34431                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34432                 */
34433                 
34434                 if (region == 'center' && this.active ) {
34435                     cfg.background = false;
34436                 }
34437                 
34438                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34439                 
34440                 this.add(region, ret);
34441                 /*
34442                 if (cfg.background) {
34443                     // render grid on panel activation (if panel background)
34444                     ret.on('activate', function(gp) {
34445                         if (!gp.grid.rendered) {
34446                     //        gp.grid.render(el);
34447                         }
34448                     });
34449                 } else {
34450                   //  cfg.grid.render(el);
34451                 }
34452                 */
34453                 break;
34454            
34455            
34456             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34457                 // it was the old xcomponent building that caused this before.
34458                 // espeically if border is the top element in the tree.
34459                 ret = this;
34460                 break; 
34461                 
34462                     
34463                 
34464                 
34465                 
34466             default:
34467                 /*
34468                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34469                     
34470                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34471                     this.add(region, ret);
34472                 } else {
34473                 */
34474                     Roo.log(cfg);
34475                     throw "Can not add '" + cfg.xtype + "' to Border";
34476                     return null;
34477              
34478                                 
34479              
34480         }
34481         this.beginUpdate();
34482         // add children..
34483         var region = '';
34484         var abn = {};
34485         Roo.each(xitems, function(i)  {
34486             region = nb && i.region ? i.region : false;
34487             
34488             var add = ret.addxtype(i);
34489            
34490             if (region) {
34491                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34492                 if (!i.background) {
34493                     abn[region] = nb[region] ;
34494                 }
34495             }
34496             
34497         });
34498         this.endUpdate();
34499
34500         // make the last non-background panel active..
34501         //if (nb) { Roo.log(abn); }
34502         if (nb) {
34503             
34504             for(var r in abn) {
34505                 region = this.getRegion(r);
34506                 if (region) {
34507                     // tried using nb[r], but it does not work..
34508                      
34509                     region.showPanel(abn[r]);
34510                    
34511                 }
34512             }
34513         }
34514         return ret;
34515         
34516     },
34517     
34518     
34519 // private
34520     factory : function(cfg)
34521     {
34522         
34523         var validRegions = Roo.bootstrap.layout.Border.regions;
34524
34525         var target = cfg.region;
34526         cfg.mgr = this;
34527         
34528         var r = Roo.bootstrap.layout;
34529         Roo.log(target);
34530         switch(target){
34531             case "north":
34532                 return new r.North(cfg);
34533             case "south":
34534                 return new r.South(cfg);
34535             case "east":
34536                 return new r.East(cfg);
34537             case "west":
34538                 return new r.West(cfg);
34539             case "center":
34540                 return new r.Center(cfg);
34541         }
34542         throw 'Layout region "'+target+'" not supported.';
34543     }
34544     
34545     
34546 });
34547  /*
34548  * Based on:
34549  * Ext JS Library 1.1.1
34550  * Copyright(c) 2006-2007, Ext JS, LLC.
34551  *
34552  * Originally Released Under LGPL - original licence link has changed is not relivant.
34553  *
34554  * Fork - LGPL
34555  * <script type="text/javascript">
34556  */
34557  
34558 /**
34559  * @class Roo.bootstrap.layout.Basic
34560  * @extends Roo.util.Observable
34561  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34562  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34563  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34564  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34565  * @cfg {string}   region  the region that it inhabits..
34566  * @cfg {bool}   skipConfig skip config?
34567  * 
34568
34569  */
34570 Roo.bootstrap.layout.Basic = function(config){
34571     
34572     this.mgr = config.mgr;
34573     
34574     this.position = config.region;
34575     
34576     var skipConfig = config.skipConfig;
34577     
34578     this.events = {
34579         /**
34580          * @scope Roo.BasicLayoutRegion
34581          */
34582         
34583         /**
34584          * @event beforeremove
34585          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34586          * @param {Roo.LayoutRegion} this
34587          * @param {Roo.ContentPanel} panel The panel
34588          * @param {Object} e The cancel event object
34589          */
34590         "beforeremove" : true,
34591         /**
34592          * @event invalidated
34593          * Fires when the layout for this region is changed.
34594          * @param {Roo.LayoutRegion} this
34595          */
34596         "invalidated" : true,
34597         /**
34598          * @event visibilitychange
34599          * Fires when this region is shown or hidden 
34600          * @param {Roo.LayoutRegion} this
34601          * @param {Boolean} visibility true or false
34602          */
34603         "visibilitychange" : true,
34604         /**
34605          * @event paneladded
34606          * Fires when a panel is added. 
34607          * @param {Roo.LayoutRegion} this
34608          * @param {Roo.ContentPanel} panel The panel
34609          */
34610         "paneladded" : true,
34611         /**
34612          * @event panelremoved
34613          * Fires when a panel is removed. 
34614          * @param {Roo.LayoutRegion} this
34615          * @param {Roo.ContentPanel} panel The panel
34616          */
34617         "panelremoved" : true,
34618         /**
34619          * @event beforecollapse
34620          * Fires when this region before collapse.
34621          * @param {Roo.LayoutRegion} this
34622          */
34623         "beforecollapse" : true,
34624         /**
34625          * @event collapsed
34626          * Fires when this region is collapsed.
34627          * @param {Roo.LayoutRegion} this
34628          */
34629         "collapsed" : true,
34630         /**
34631          * @event expanded
34632          * Fires when this region is expanded.
34633          * @param {Roo.LayoutRegion} this
34634          */
34635         "expanded" : true,
34636         /**
34637          * @event slideshow
34638          * Fires when this region is slid into view.
34639          * @param {Roo.LayoutRegion} this
34640          */
34641         "slideshow" : true,
34642         /**
34643          * @event slidehide
34644          * Fires when this region slides out of view. 
34645          * @param {Roo.LayoutRegion} this
34646          */
34647         "slidehide" : true,
34648         /**
34649          * @event panelactivated
34650          * Fires when a panel is activated. 
34651          * @param {Roo.LayoutRegion} this
34652          * @param {Roo.ContentPanel} panel The activated panel
34653          */
34654         "panelactivated" : true,
34655         /**
34656          * @event resized
34657          * Fires when the user resizes this region. 
34658          * @param {Roo.LayoutRegion} this
34659          * @param {Number} newSize The new size (width for east/west, height for north/south)
34660          */
34661         "resized" : true
34662     };
34663     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34664     this.panels = new Roo.util.MixedCollection();
34665     this.panels.getKey = this.getPanelId.createDelegate(this);
34666     this.box = null;
34667     this.activePanel = null;
34668     // ensure listeners are added...
34669     
34670     if (config.listeners || config.events) {
34671         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34672             listeners : config.listeners || {},
34673             events : config.events || {}
34674         });
34675     }
34676     
34677     if(skipConfig !== true){
34678         this.applyConfig(config);
34679     }
34680 };
34681
34682 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34683 {
34684     getPanelId : function(p){
34685         return p.getId();
34686     },
34687     
34688     applyConfig : function(config){
34689         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34690         this.config = config;
34691         
34692     },
34693     
34694     /**
34695      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34696      * the width, for horizontal (north, south) the height.
34697      * @param {Number} newSize The new width or height
34698      */
34699     resizeTo : function(newSize){
34700         var el = this.el ? this.el :
34701                  (this.activePanel ? this.activePanel.getEl() : null);
34702         if(el){
34703             switch(this.position){
34704                 case "east":
34705                 case "west":
34706                     el.setWidth(newSize);
34707                     this.fireEvent("resized", this, newSize);
34708                 break;
34709                 case "north":
34710                 case "south":
34711                     el.setHeight(newSize);
34712                     this.fireEvent("resized", this, newSize);
34713                 break;                
34714             }
34715         }
34716     },
34717     
34718     getBox : function(){
34719         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34720     },
34721     
34722     getMargins : function(){
34723         return this.margins;
34724     },
34725     
34726     updateBox : function(box){
34727         this.box = box;
34728         var el = this.activePanel.getEl();
34729         el.dom.style.left = box.x + "px";
34730         el.dom.style.top = box.y + "px";
34731         this.activePanel.setSize(box.width, box.height);
34732     },
34733     
34734     /**
34735      * Returns the container element for this region.
34736      * @return {Roo.Element}
34737      */
34738     getEl : function(){
34739         return this.activePanel;
34740     },
34741     
34742     /**
34743      * Returns true if this region is currently visible.
34744      * @return {Boolean}
34745      */
34746     isVisible : function(){
34747         return this.activePanel ? true : false;
34748     },
34749     
34750     setActivePanel : function(panel){
34751         panel = this.getPanel(panel);
34752         if(this.activePanel && this.activePanel != panel){
34753             this.activePanel.setActiveState(false);
34754             this.activePanel.getEl().setLeftTop(-10000,-10000);
34755         }
34756         this.activePanel = panel;
34757         panel.setActiveState(true);
34758         if(this.box){
34759             panel.setSize(this.box.width, this.box.height);
34760         }
34761         this.fireEvent("panelactivated", this, panel);
34762         this.fireEvent("invalidated");
34763     },
34764     
34765     /**
34766      * Show the specified panel.
34767      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34768      * @return {Roo.ContentPanel} The shown panel or null
34769      */
34770     showPanel : function(panel){
34771         panel = this.getPanel(panel);
34772         if(panel){
34773             this.setActivePanel(panel);
34774         }
34775         return panel;
34776     },
34777     
34778     /**
34779      * Get the active panel for this region.
34780      * @return {Roo.ContentPanel} The active panel or null
34781      */
34782     getActivePanel : function(){
34783         return this.activePanel;
34784     },
34785     
34786     /**
34787      * Add the passed ContentPanel(s)
34788      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34789      * @return {Roo.ContentPanel} The panel added (if only one was added)
34790      */
34791     add : function(panel){
34792         if(arguments.length > 1){
34793             for(var i = 0, len = arguments.length; i < len; i++) {
34794                 this.add(arguments[i]);
34795             }
34796             return null;
34797         }
34798         if(this.hasPanel(panel)){
34799             this.showPanel(panel);
34800             return panel;
34801         }
34802         var el = panel.getEl();
34803         if(el.dom.parentNode != this.mgr.el.dom){
34804             this.mgr.el.dom.appendChild(el.dom);
34805         }
34806         if(panel.setRegion){
34807             panel.setRegion(this);
34808         }
34809         this.panels.add(panel);
34810         el.setStyle("position", "absolute");
34811         if(!panel.background){
34812             this.setActivePanel(panel);
34813             if(this.config.initialSize && this.panels.getCount()==1){
34814                 this.resizeTo(this.config.initialSize);
34815             }
34816         }
34817         this.fireEvent("paneladded", this, panel);
34818         return panel;
34819     },
34820     
34821     /**
34822      * Returns true if the panel is in this region.
34823      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34824      * @return {Boolean}
34825      */
34826     hasPanel : function(panel){
34827         if(typeof panel == "object"){ // must be panel obj
34828             panel = panel.getId();
34829         }
34830         return this.getPanel(panel) ? true : false;
34831     },
34832     
34833     /**
34834      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34835      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34836      * @param {Boolean} preservePanel Overrides the config preservePanel option
34837      * @return {Roo.ContentPanel} The panel that was removed
34838      */
34839     remove : function(panel, preservePanel){
34840         panel = this.getPanel(panel);
34841         if(!panel){
34842             return null;
34843         }
34844         var e = {};
34845         this.fireEvent("beforeremove", this, panel, e);
34846         if(e.cancel === true){
34847             return null;
34848         }
34849         var panelId = panel.getId();
34850         this.panels.removeKey(panelId);
34851         return panel;
34852     },
34853     
34854     /**
34855      * Returns the panel specified or null if it's not in this region.
34856      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34857      * @return {Roo.ContentPanel}
34858      */
34859     getPanel : function(id){
34860         if(typeof id == "object"){ // must be panel obj
34861             return id;
34862         }
34863         return this.panels.get(id);
34864     },
34865     
34866     /**
34867      * Returns this regions position (north/south/east/west/center).
34868      * @return {String} 
34869      */
34870     getPosition: function(){
34871         return this.position;    
34872     }
34873 });/*
34874  * Based on:
34875  * Ext JS Library 1.1.1
34876  * Copyright(c) 2006-2007, Ext JS, LLC.
34877  *
34878  * Originally Released Under LGPL - original licence link has changed is not relivant.
34879  *
34880  * Fork - LGPL
34881  * <script type="text/javascript">
34882  */
34883  
34884 /**
34885  * @class Roo.bootstrap.layout.Region
34886  * @extends Roo.bootstrap.layout.Basic
34887  * This class represents a region in a layout manager.
34888  
34889  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34890  * @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})
34891  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34892  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34893  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34894  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34895  * @cfg {String}    title           The title for the region (overrides panel titles)
34896  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34897  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34898  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34899  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34900  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34901  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34902  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34903  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34904  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34905  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34906
34907  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34908  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34909  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34910  * @cfg {Number}    width           For East/West panels
34911  * @cfg {Number}    height          For North/South panels
34912  * @cfg {Boolean}   split           To show the splitter
34913  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34914  * 
34915  * @cfg {string}   cls             Extra CSS classes to add to region
34916  * 
34917  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34918  * @cfg {string}   region  the region that it inhabits..
34919  *
34920
34921  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34922  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34923
34924  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34925  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34926  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34927  */
34928 Roo.bootstrap.layout.Region = function(config)
34929 {
34930     this.applyConfig(config);
34931
34932     var mgr = config.mgr;
34933     var pos = config.region;
34934     config.skipConfig = true;
34935     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34936     
34937     if (mgr.el) {
34938         this.onRender(mgr.el);   
34939     }
34940      
34941     this.visible = true;
34942     this.collapsed = false;
34943     this.unrendered_panels = [];
34944 };
34945
34946 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34947
34948     position: '', // set by wrapper (eg. north/south etc..)
34949     unrendered_panels : null,  // unrendered panels.
34950     createBody : function(){
34951         /** This region's body element 
34952         * @type Roo.Element */
34953         this.bodyEl = this.el.createChild({
34954                 tag: "div",
34955                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34956         });
34957     },
34958
34959     onRender: function(ctr, pos)
34960     {
34961         var dh = Roo.DomHelper;
34962         /** This region's container element 
34963         * @type Roo.Element */
34964         this.el = dh.append(ctr.dom, {
34965                 tag: "div",
34966                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34967             }, true);
34968         /** This region's title element 
34969         * @type Roo.Element */
34970     
34971         this.titleEl = dh.append(this.el.dom,
34972             {
34973                     tag: "div",
34974                     unselectable: "on",
34975                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34976                     children:[
34977                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34978                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34979                     ]}, true);
34980         
34981         this.titleEl.enableDisplayMode();
34982         /** This region's title text element 
34983         * @type HTMLElement */
34984         this.titleTextEl = this.titleEl.dom.firstChild;
34985         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34986         /*
34987         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34988         this.closeBtn.enableDisplayMode();
34989         this.closeBtn.on("click", this.closeClicked, this);
34990         this.closeBtn.hide();
34991     */
34992         this.createBody(this.config);
34993         if(this.config.hideWhenEmpty){
34994             this.hide();
34995             this.on("paneladded", this.validateVisibility, this);
34996             this.on("panelremoved", this.validateVisibility, this);
34997         }
34998         if(this.autoScroll){
34999             this.bodyEl.setStyle("overflow", "auto");
35000         }else{
35001             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35002         }
35003         //if(c.titlebar !== false){
35004             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35005                 this.titleEl.hide();
35006             }else{
35007                 this.titleEl.show();
35008                 if(this.config.title){
35009                     this.titleTextEl.innerHTML = this.config.title;
35010                 }
35011             }
35012         //}
35013         if(this.config.collapsed){
35014             this.collapse(true);
35015         }
35016         if(this.config.hidden){
35017             this.hide();
35018         }
35019         
35020         if (this.unrendered_panels && this.unrendered_panels.length) {
35021             for (var i =0;i< this.unrendered_panels.length; i++) {
35022                 this.add(this.unrendered_panels[i]);
35023             }
35024             this.unrendered_panels = null;
35025             
35026         }
35027         
35028     },
35029     
35030     applyConfig : function(c)
35031     {
35032         /*
35033          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35034             var dh = Roo.DomHelper;
35035             if(c.titlebar !== false){
35036                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35037                 this.collapseBtn.on("click", this.collapse, this);
35038                 this.collapseBtn.enableDisplayMode();
35039                 /*
35040                 if(c.showPin === true || this.showPin){
35041                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35042                     this.stickBtn.enableDisplayMode();
35043                     this.stickBtn.on("click", this.expand, this);
35044                     this.stickBtn.hide();
35045                 }
35046                 
35047             }
35048             */
35049             /** This region's collapsed element
35050             * @type Roo.Element */
35051             /*
35052              *
35053             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35054                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35055             ]}, true);
35056             
35057             if(c.floatable !== false){
35058                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35059                this.collapsedEl.on("click", this.collapseClick, this);
35060             }
35061
35062             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35063                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35064                    id: "message", unselectable: "on", style:{"float":"left"}});
35065                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35066              }
35067             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35068             this.expandBtn.on("click", this.expand, this);
35069             
35070         }
35071         
35072         if(this.collapseBtn){
35073             this.collapseBtn.setVisible(c.collapsible == true);
35074         }
35075         
35076         this.cmargins = c.cmargins || this.cmargins ||
35077                          (this.position == "west" || this.position == "east" ?
35078                              {top: 0, left: 2, right:2, bottom: 0} :
35079                              {top: 2, left: 0, right:0, bottom: 2});
35080         */
35081         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35082         
35083         
35084         this.bottomTabs = c.tabPosition != "top";
35085         
35086         this.autoScroll = c.autoScroll || false;
35087         
35088         
35089        
35090         
35091         this.duration = c.duration || .30;
35092         this.slideDuration = c.slideDuration || .45;
35093         this.config = c;
35094        
35095     },
35096     /**
35097      * Returns true if this region is currently visible.
35098      * @return {Boolean}
35099      */
35100     isVisible : function(){
35101         return this.visible;
35102     },
35103
35104     /**
35105      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35106      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35107      */
35108     //setCollapsedTitle : function(title){
35109     //    title = title || "&#160;";
35110      //   if(this.collapsedTitleTextEl){
35111       //      this.collapsedTitleTextEl.innerHTML = title;
35112        // }
35113     //},
35114
35115     getBox : function(){
35116         var b;
35117       //  if(!this.collapsed){
35118             b = this.el.getBox(false, true);
35119        // }else{
35120           //  b = this.collapsedEl.getBox(false, true);
35121         //}
35122         return b;
35123     },
35124
35125     getMargins : function(){
35126         return this.margins;
35127         //return this.collapsed ? this.cmargins : this.margins;
35128     },
35129 /*
35130     highlight : function(){
35131         this.el.addClass("x-layout-panel-dragover");
35132     },
35133
35134     unhighlight : function(){
35135         this.el.removeClass("x-layout-panel-dragover");
35136     },
35137 */
35138     updateBox : function(box)
35139     {
35140         if (!this.bodyEl) {
35141             return; // not rendered yet..
35142         }
35143         
35144         this.box = box;
35145         if(!this.collapsed){
35146             this.el.dom.style.left = box.x + "px";
35147             this.el.dom.style.top = box.y + "px";
35148             this.updateBody(box.width, box.height);
35149         }else{
35150             this.collapsedEl.dom.style.left = box.x + "px";
35151             this.collapsedEl.dom.style.top = box.y + "px";
35152             this.collapsedEl.setSize(box.width, box.height);
35153         }
35154         if(this.tabs){
35155             this.tabs.autoSizeTabs();
35156         }
35157     },
35158
35159     updateBody : function(w, h)
35160     {
35161         if(w !== null){
35162             this.el.setWidth(w);
35163             w -= this.el.getBorderWidth("rl");
35164             if(this.config.adjustments){
35165                 w += this.config.adjustments[0];
35166             }
35167         }
35168         if(h !== null && h > 0){
35169             this.el.setHeight(h);
35170             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35171             h -= this.el.getBorderWidth("tb");
35172             if(this.config.adjustments){
35173                 h += this.config.adjustments[1];
35174             }
35175             this.bodyEl.setHeight(h);
35176             if(this.tabs){
35177                 h = this.tabs.syncHeight(h);
35178             }
35179         }
35180         if(this.panelSize){
35181             w = w !== null ? w : this.panelSize.width;
35182             h = h !== null ? h : this.panelSize.height;
35183         }
35184         if(this.activePanel){
35185             var el = this.activePanel.getEl();
35186             w = w !== null ? w : el.getWidth();
35187             h = h !== null ? h : el.getHeight();
35188             this.panelSize = {width: w, height: h};
35189             this.activePanel.setSize(w, h);
35190         }
35191         if(Roo.isIE && this.tabs){
35192             this.tabs.el.repaint();
35193         }
35194     },
35195
35196     /**
35197      * Returns the container element for this region.
35198      * @return {Roo.Element}
35199      */
35200     getEl : function(){
35201         return this.el;
35202     },
35203
35204     /**
35205      * Hides this region.
35206      */
35207     hide : function(){
35208         //if(!this.collapsed){
35209             this.el.dom.style.left = "-2000px";
35210             this.el.hide();
35211         //}else{
35212          //   this.collapsedEl.dom.style.left = "-2000px";
35213          //   this.collapsedEl.hide();
35214        // }
35215         this.visible = false;
35216         this.fireEvent("visibilitychange", this, false);
35217     },
35218
35219     /**
35220      * Shows this region if it was previously hidden.
35221      */
35222     show : function(){
35223         //if(!this.collapsed){
35224             this.el.show();
35225         //}else{
35226         //    this.collapsedEl.show();
35227        // }
35228         this.visible = true;
35229         this.fireEvent("visibilitychange", this, true);
35230     },
35231 /*
35232     closeClicked : function(){
35233         if(this.activePanel){
35234             this.remove(this.activePanel);
35235         }
35236     },
35237
35238     collapseClick : function(e){
35239         if(this.isSlid){
35240            e.stopPropagation();
35241            this.slideIn();
35242         }else{
35243            e.stopPropagation();
35244            this.slideOut();
35245         }
35246     },
35247 */
35248     /**
35249      * Collapses this region.
35250      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35251      */
35252     /*
35253     collapse : function(skipAnim, skipCheck = false){
35254         if(this.collapsed) {
35255             return;
35256         }
35257         
35258         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35259             
35260             this.collapsed = true;
35261             if(this.split){
35262                 this.split.el.hide();
35263             }
35264             if(this.config.animate && skipAnim !== true){
35265                 this.fireEvent("invalidated", this);
35266                 this.animateCollapse();
35267             }else{
35268                 this.el.setLocation(-20000,-20000);
35269                 this.el.hide();
35270                 this.collapsedEl.show();
35271                 this.fireEvent("collapsed", this);
35272                 this.fireEvent("invalidated", this);
35273             }
35274         }
35275         
35276     },
35277 */
35278     animateCollapse : function(){
35279         // overridden
35280     },
35281
35282     /**
35283      * Expands this region if it was previously collapsed.
35284      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35285      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35286      */
35287     /*
35288     expand : function(e, skipAnim){
35289         if(e) {
35290             e.stopPropagation();
35291         }
35292         if(!this.collapsed || this.el.hasActiveFx()) {
35293             return;
35294         }
35295         if(this.isSlid){
35296             this.afterSlideIn();
35297             skipAnim = true;
35298         }
35299         this.collapsed = false;
35300         if(this.config.animate && skipAnim !== true){
35301             this.animateExpand();
35302         }else{
35303             this.el.show();
35304             if(this.split){
35305                 this.split.el.show();
35306             }
35307             this.collapsedEl.setLocation(-2000,-2000);
35308             this.collapsedEl.hide();
35309             this.fireEvent("invalidated", this);
35310             this.fireEvent("expanded", this);
35311         }
35312     },
35313 */
35314     animateExpand : function(){
35315         // overridden
35316     },
35317
35318     initTabs : function()
35319     {
35320         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35321         
35322         var ts = new Roo.bootstrap.panel.Tabs({
35323                 el: this.bodyEl.dom,
35324                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35325                 disableTooltips: this.config.disableTabTips,
35326                 toolbar : this.config.toolbar
35327             });
35328         
35329         if(this.config.hideTabs){
35330             ts.stripWrap.setDisplayed(false);
35331         }
35332         this.tabs = ts;
35333         ts.resizeTabs = this.config.resizeTabs === true;
35334         ts.minTabWidth = this.config.minTabWidth || 40;
35335         ts.maxTabWidth = this.config.maxTabWidth || 250;
35336         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35337         ts.monitorResize = false;
35338         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35339         ts.bodyEl.addClass('roo-layout-tabs-body');
35340         this.panels.each(this.initPanelAsTab, this);
35341     },
35342
35343     initPanelAsTab : function(panel){
35344         var ti = this.tabs.addTab(
35345             panel.getEl().id,
35346             panel.getTitle(),
35347             null,
35348             this.config.closeOnTab && panel.isClosable(),
35349             panel.tpl
35350         );
35351         if(panel.tabTip !== undefined){
35352             ti.setTooltip(panel.tabTip);
35353         }
35354         ti.on("activate", function(){
35355               this.setActivePanel(panel);
35356         }, this);
35357         
35358         if(this.config.closeOnTab){
35359             ti.on("beforeclose", function(t, e){
35360                 e.cancel = true;
35361                 this.remove(panel);
35362             }, this);
35363         }
35364         
35365         panel.tabItem = ti;
35366         
35367         return ti;
35368     },
35369
35370     updatePanelTitle : function(panel, title)
35371     {
35372         if(this.activePanel == panel){
35373             this.updateTitle(title);
35374         }
35375         if(this.tabs){
35376             var ti = this.tabs.getTab(panel.getEl().id);
35377             ti.setText(title);
35378             if(panel.tabTip !== undefined){
35379                 ti.setTooltip(panel.tabTip);
35380             }
35381         }
35382     },
35383
35384     updateTitle : function(title){
35385         if(this.titleTextEl && !this.config.title){
35386             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35387         }
35388     },
35389
35390     setActivePanel : function(panel)
35391     {
35392         panel = this.getPanel(panel);
35393         if(this.activePanel && this.activePanel != panel){
35394             this.activePanel.setActiveState(false);
35395         }
35396         this.activePanel = panel;
35397         panel.setActiveState(true);
35398         if(this.panelSize){
35399             panel.setSize(this.panelSize.width, this.panelSize.height);
35400         }
35401         if(this.closeBtn){
35402             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35403         }
35404         this.updateTitle(panel.getTitle());
35405         if(this.tabs){
35406             this.fireEvent("invalidated", this);
35407         }
35408         this.fireEvent("panelactivated", this, panel);
35409     },
35410
35411     /**
35412      * Shows the specified panel.
35413      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35414      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35415      */
35416     showPanel : function(panel)
35417     {
35418         panel = this.getPanel(panel);
35419         if(panel){
35420             if(this.tabs){
35421                 var tab = this.tabs.getTab(panel.getEl().id);
35422                 if(tab.isHidden()){
35423                     this.tabs.unhideTab(tab.id);
35424                 }
35425                 tab.activate();
35426             }else{
35427                 this.setActivePanel(panel);
35428             }
35429         }
35430         return panel;
35431     },
35432
35433     /**
35434      * Get the active panel for this region.
35435      * @return {Roo.ContentPanel} The active panel or null
35436      */
35437     getActivePanel : function(){
35438         return this.activePanel;
35439     },
35440
35441     validateVisibility : function(){
35442         if(this.panels.getCount() < 1){
35443             this.updateTitle("&#160;");
35444             this.closeBtn.hide();
35445             this.hide();
35446         }else{
35447             if(!this.isVisible()){
35448                 this.show();
35449             }
35450         }
35451     },
35452
35453     /**
35454      * Adds the passed ContentPanel(s) to this region.
35455      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35456      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35457      */
35458     add : function(panel)
35459     {
35460         if(arguments.length > 1){
35461             for(var i = 0, len = arguments.length; i < len; i++) {
35462                 this.add(arguments[i]);
35463             }
35464             return null;
35465         }
35466         
35467         // if we have not been rendered yet, then we can not really do much of this..
35468         if (!this.bodyEl) {
35469             this.unrendered_panels.push(panel);
35470             return panel;
35471         }
35472         
35473         
35474         
35475         
35476         if(this.hasPanel(panel)){
35477             this.showPanel(panel);
35478             return panel;
35479         }
35480         panel.setRegion(this);
35481         this.panels.add(panel);
35482        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35483             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35484             // and hide them... ???
35485             this.bodyEl.dom.appendChild(panel.getEl().dom);
35486             if(panel.background !== true){
35487                 this.setActivePanel(panel);
35488             }
35489             this.fireEvent("paneladded", this, panel);
35490             return panel;
35491         }
35492         */
35493         if(!this.tabs){
35494             this.initTabs();
35495         }else{
35496             this.initPanelAsTab(panel);
35497         }
35498         
35499         
35500         if(panel.background !== true){
35501             this.tabs.activate(panel.getEl().id);
35502         }
35503         this.fireEvent("paneladded", this, panel);
35504         return panel;
35505     },
35506
35507     /**
35508      * Hides the tab for the specified panel.
35509      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35510      */
35511     hidePanel : function(panel){
35512         if(this.tabs && (panel = this.getPanel(panel))){
35513             this.tabs.hideTab(panel.getEl().id);
35514         }
35515     },
35516
35517     /**
35518      * Unhides the tab for a previously hidden panel.
35519      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35520      */
35521     unhidePanel : function(panel){
35522         if(this.tabs && (panel = this.getPanel(panel))){
35523             this.tabs.unhideTab(panel.getEl().id);
35524         }
35525     },
35526
35527     clearPanels : function(){
35528         while(this.panels.getCount() > 0){
35529              this.remove(this.panels.first());
35530         }
35531     },
35532
35533     /**
35534      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35535      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35536      * @param {Boolean} preservePanel Overrides the config preservePanel option
35537      * @return {Roo.ContentPanel} The panel that was removed
35538      */
35539     remove : function(panel, preservePanel)
35540     {
35541         panel = this.getPanel(panel);
35542         if(!panel){
35543             return null;
35544         }
35545         var e = {};
35546         this.fireEvent("beforeremove", this, panel, e);
35547         if(e.cancel === true){
35548             return null;
35549         }
35550         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35551         var panelId = panel.getId();
35552         this.panels.removeKey(panelId);
35553         if(preservePanel){
35554             document.body.appendChild(panel.getEl().dom);
35555         }
35556         if(this.tabs){
35557             this.tabs.removeTab(panel.getEl().id);
35558         }else if (!preservePanel){
35559             this.bodyEl.dom.removeChild(panel.getEl().dom);
35560         }
35561         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35562             var p = this.panels.first();
35563             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35564             tempEl.appendChild(p.getEl().dom);
35565             this.bodyEl.update("");
35566             this.bodyEl.dom.appendChild(p.getEl().dom);
35567             tempEl = null;
35568             this.updateTitle(p.getTitle());
35569             this.tabs = null;
35570             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35571             this.setActivePanel(p);
35572         }
35573         panel.setRegion(null);
35574         if(this.activePanel == panel){
35575             this.activePanel = null;
35576         }
35577         if(this.config.autoDestroy !== false && preservePanel !== true){
35578             try{panel.destroy();}catch(e){}
35579         }
35580         this.fireEvent("panelremoved", this, panel);
35581         return panel;
35582     },
35583
35584     /**
35585      * Returns the TabPanel component used by this region
35586      * @return {Roo.TabPanel}
35587      */
35588     getTabs : function(){
35589         return this.tabs;
35590     },
35591
35592     createTool : function(parentEl, className){
35593         var btn = Roo.DomHelper.append(parentEl, {
35594             tag: "div",
35595             cls: "x-layout-tools-button",
35596             children: [ {
35597                 tag: "div",
35598                 cls: "roo-layout-tools-button-inner " + className,
35599                 html: "&#160;"
35600             }]
35601         }, true);
35602         btn.addClassOnOver("roo-layout-tools-button-over");
35603         return btn;
35604     }
35605 });/*
35606  * Based on:
35607  * Ext JS Library 1.1.1
35608  * Copyright(c) 2006-2007, Ext JS, LLC.
35609  *
35610  * Originally Released Under LGPL - original licence link has changed is not relivant.
35611  *
35612  * Fork - LGPL
35613  * <script type="text/javascript">
35614  */
35615  
35616
35617
35618 /**
35619  * @class Roo.SplitLayoutRegion
35620  * @extends Roo.LayoutRegion
35621  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35622  */
35623 Roo.bootstrap.layout.Split = function(config){
35624     this.cursor = config.cursor;
35625     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35626 };
35627
35628 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35629 {
35630     splitTip : "Drag to resize.",
35631     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35632     useSplitTips : false,
35633
35634     applyConfig : function(config){
35635         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35636     },
35637     
35638     onRender : function(ctr,pos) {
35639         
35640         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35641         if(!this.config.split){
35642             return;
35643         }
35644         if(!this.split){
35645             
35646             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35647                             tag: "div",
35648                             id: this.el.id + "-split",
35649                             cls: "roo-layout-split roo-layout-split-"+this.position,
35650                             html: "&#160;"
35651             });
35652             /** The SplitBar for this region 
35653             * @type Roo.SplitBar */
35654             // does not exist yet...
35655             Roo.log([this.position, this.orientation]);
35656             
35657             this.split = new Roo.bootstrap.SplitBar({
35658                 dragElement : splitEl,
35659                 resizingElement: this.el,
35660                 orientation : this.orientation
35661             });
35662             
35663             this.split.on("moved", this.onSplitMove, this);
35664             this.split.useShim = this.config.useShim === true;
35665             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35666             if(this.useSplitTips){
35667                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35668             }
35669             //if(config.collapsible){
35670             //    this.split.el.on("dblclick", this.collapse,  this);
35671             //}
35672         }
35673         if(typeof this.config.minSize != "undefined"){
35674             this.split.minSize = this.config.minSize;
35675         }
35676         if(typeof this.config.maxSize != "undefined"){
35677             this.split.maxSize = this.config.maxSize;
35678         }
35679         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35680             this.hideSplitter();
35681         }
35682         
35683     },
35684
35685     getHMaxSize : function(){
35686          var cmax = this.config.maxSize || 10000;
35687          var center = this.mgr.getRegion("center");
35688          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35689     },
35690
35691     getVMaxSize : function(){
35692          var cmax = this.config.maxSize || 10000;
35693          var center = this.mgr.getRegion("center");
35694          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35695     },
35696
35697     onSplitMove : function(split, newSize){
35698         this.fireEvent("resized", this, newSize);
35699     },
35700     
35701     /** 
35702      * Returns the {@link Roo.SplitBar} for this region.
35703      * @return {Roo.SplitBar}
35704      */
35705     getSplitBar : function(){
35706         return this.split;
35707     },
35708     
35709     hide : function(){
35710         this.hideSplitter();
35711         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35712     },
35713
35714     hideSplitter : function(){
35715         if(this.split){
35716             this.split.el.setLocation(-2000,-2000);
35717             this.split.el.hide();
35718         }
35719     },
35720
35721     show : function(){
35722         if(this.split){
35723             this.split.el.show();
35724         }
35725         Roo.bootstrap.layout.Split.superclass.show.call(this);
35726     },
35727     
35728     beforeSlide: function(){
35729         if(Roo.isGecko){// firefox overflow auto bug workaround
35730             this.bodyEl.clip();
35731             if(this.tabs) {
35732                 this.tabs.bodyEl.clip();
35733             }
35734             if(this.activePanel){
35735                 this.activePanel.getEl().clip();
35736                 
35737                 if(this.activePanel.beforeSlide){
35738                     this.activePanel.beforeSlide();
35739                 }
35740             }
35741         }
35742     },
35743     
35744     afterSlide : function(){
35745         if(Roo.isGecko){// firefox overflow auto bug workaround
35746             this.bodyEl.unclip();
35747             if(this.tabs) {
35748                 this.tabs.bodyEl.unclip();
35749             }
35750             if(this.activePanel){
35751                 this.activePanel.getEl().unclip();
35752                 if(this.activePanel.afterSlide){
35753                     this.activePanel.afterSlide();
35754                 }
35755             }
35756         }
35757     },
35758
35759     initAutoHide : function(){
35760         if(this.autoHide !== false){
35761             if(!this.autoHideHd){
35762                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35763                 this.autoHideHd = {
35764                     "mouseout": function(e){
35765                         if(!e.within(this.el, true)){
35766                             st.delay(500);
35767                         }
35768                     },
35769                     "mouseover" : function(e){
35770                         st.cancel();
35771                     },
35772                     scope : this
35773                 };
35774             }
35775             this.el.on(this.autoHideHd);
35776         }
35777     },
35778
35779     clearAutoHide : function(){
35780         if(this.autoHide !== false){
35781             this.el.un("mouseout", this.autoHideHd.mouseout);
35782             this.el.un("mouseover", this.autoHideHd.mouseover);
35783         }
35784     },
35785
35786     clearMonitor : function(){
35787         Roo.get(document).un("click", this.slideInIf, this);
35788     },
35789
35790     // these names are backwards but not changed for compat
35791     slideOut : function(){
35792         if(this.isSlid || this.el.hasActiveFx()){
35793             return;
35794         }
35795         this.isSlid = true;
35796         if(this.collapseBtn){
35797             this.collapseBtn.hide();
35798         }
35799         this.closeBtnState = this.closeBtn.getStyle('display');
35800         this.closeBtn.hide();
35801         if(this.stickBtn){
35802             this.stickBtn.show();
35803         }
35804         this.el.show();
35805         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35806         this.beforeSlide();
35807         this.el.setStyle("z-index", 10001);
35808         this.el.slideIn(this.getSlideAnchor(), {
35809             callback: function(){
35810                 this.afterSlide();
35811                 this.initAutoHide();
35812                 Roo.get(document).on("click", this.slideInIf, this);
35813                 this.fireEvent("slideshow", this);
35814             },
35815             scope: this,
35816             block: true
35817         });
35818     },
35819
35820     afterSlideIn : function(){
35821         this.clearAutoHide();
35822         this.isSlid = false;
35823         this.clearMonitor();
35824         this.el.setStyle("z-index", "");
35825         if(this.collapseBtn){
35826             this.collapseBtn.show();
35827         }
35828         this.closeBtn.setStyle('display', this.closeBtnState);
35829         if(this.stickBtn){
35830             this.stickBtn.hide();
35831         }
35832         this.fireEvent("slidehide", this);
35833     },
35834
35835     slideIn : function(cb){
35836         if(!this.isSlid || this.el.hasActiveFx()){
35837             Roo.callback(cb);
35838             return;
35839         }
35840         this.isSlid = false;
35841         this.beforeSlide();
35842         this.el.slideOut(this.getSlideAnchor(), {
35843             callback: function(){
35844                 this.el.setLeftTop(-10000, -10000);
35845                 this.afterSlide();
35846                 this.afterSlideIn();
35847                 Roo.callback(cb);
35848             },
35849             scope: this,
35850             block: true
35851         });
35852     },
35853     
35854     slideInIf : function(e){
35855         if(!e.within(this.el)){
35856             this.slideIn();
35857         }
35858     },
35859
35860     animateCollapse : function(){
35861         this.beforeSlide();
35862         this.el.setStyle("z-index", 20000);
35863         var anchor = this.getSlideAnchor();
35864         this.el.slideOut(anchor, {
35865             callback : function(){
35866                 this.el.setStyle("z-index", "");
35867                 this.collapsedEl.slideIn(anchor, {duration:.3});
35868                 this.afterSlide();
35869                 this.el.setLocation(-10000,-10000);
35870                 this.el.hide();
35871                 this.fireEvent("collapsed", this);
35872             },
35873             scope: this,
35874             block: true
35875         });
35876     },
35877
35878     animateExpand : function(){
35879         this.beforeSlide();
35880         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35881         this.el.setStyle("z-index", 20000);
35882         this.collapsedEl.hide({
35883             duration:.1
35884         });
35885         this.el.slideIn(this.getSlideAnchor(), {
35886             callback : function(){
35887                 this.el.setStyle("z-index", "");
35888                 this.afterSlide();
35889                 if(this.split){
35890                     this.split.el.show();
35891                 }
35892                 this.fireEvent("invalidated", this);
35893                 this.fireEvent("expanded", this);
35894             },
35895             scope: this,
35896             block: true
35897         });
35898     },
35899
35900     anchors : {
35901         "west" : "left",
35902         "east" : "right",
35903         "north" : "top",
35904         "south" : "bottom"
35905     },
35906
35907     sanchors : {
35908         "west" : "l",
35909         "east" : "r",
35910         "north" : "t",
35911         "south" : "b"
35912     },
35913
35914     canchors : {
35915         "west" : "tl-tr",
35916         "east" : "tr-tl",
35917         "north" : "tl-bl",
35918         "south" : "bl-tl"
35919     },
35920
35921     getAnchor : function(){
35922         return this.anchors[this.position];
35923     },
35924
35925     getCollapseAnchor : function(){
35926         return this.canchors[this.position];
35927     },
35928
35929     getSlideAnchor : function(){
35930         return this.sanchors[this.position];
35931     },
35932
35933     getAlignAdj : function(){
35934         var cm = this.cmargins;
35935         switch(this.position){
35936             case "west":
35937                 return [0, 0];
35938             break;
35939             case "east":
35940                 return [0, 0];
35941             break;
35942             case "north":
35943                 return [0, 0];
35944             break;
35945             case "south":
35946                 return [0, 0];
35947             break;
35948         }
35949     },
35950
35951     getExpandAdj : function(){
35952         var c = this.collapsedEl, cm = this.cmargins;
35953         switch(this.position){
35954             case "west":
35955                 return [-(cm.right+c.getWidth()+cm.left), 0];
35956             break;
35957             case "east":
35958                 return [cm.right+c.getWidth()+cm.left, 0];
35959             break;
35960             case "north":
35961                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35962             break;
35963             case "south":
35964                 return [0, cm.top+cm.bottom+c.getHeight()];
35965             break;
35966         }
35967     }
35968 });/*
35969  * Based on:
35970  * Ext JS Library 1.1.1
35971  * Copyright(c) 2006-2007, Ext JS, LLC.
35972  *
35973  * Originally Released Under LGPL - original licence link has changed is not relivant.
35974  *
35975  * Fork - LGPL
35976  * <script type="text/javascript">
35977  */
35978 /*
35979  * These classes are private internal classes
35980  */
35981 Roo.bootstrap.layout.Center = function(config){
35982     config.region = "center";
35983     Roo.bootstrap.layout.Region.call(this, config);
35984     this.visible = true;
35985     this.minWidth = config.minWidth || 20;
35986     this.minHeight = config.minHeight || 20;
35987 };
35988
35989 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35990     hide : function(){
35991         // center panel can't be hidden
35992     },
35993     
35994     show : function(){
35995         // center panel can't be hidden
35996     },
35997     
35998     getMinWidth: function(){
35999         return this.minWidth;
36000     },
36001     
36002     getMinHeight: function(){
36003         return this.minHeight;
36004     }
36005 });
36006
36007
36008
36009
36010  
36011
36012
36013
36014
36015
36016 Roo.bootstrap.layout.North = function(config)
36017 {
36018     config.region = 'north';
36019     config.cursor = 'n-resize';
36020     
36021     Roo.bootstrap.layout.Split.call(this, config);
36022     
36023     
36024     if(this.split){
36025         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36026         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36027         this.split.el.addClass("roo-layout-split-v");
36028     }
36029     var size = config.initialSize || config.height;
36030     if(typeof size != "undefined"){
36031         this.el.setHeight(size);
36032     }
36033 };
36034 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36035 {
36036     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36037     
36038     
36039     
36040     getBox : function(){
36041         if(this.collapsed){
36042             return this.collapsedEl.getBox();
36043         }
36044         var box = this.el.getBox();
36045         if(this.split){
36046             box.height += this.split.el.getHeight();
36047         }
36048         return box;
36049     },
36050     
36051     updateBox : function(box){
36052         if(this.split && !this.collapsed){
36053             box.height -= this.split.el.getHeight();
36054             this.split.el.setLeft(box.x);
36055             this.split.el.setTop(box.y+box.height);
36056             this.split.el.setWidth(box.width);
36057         }
36058         if(this.collapsed){
36059             this.updateBody(box.width, null);
36060         }
36061         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36062     }
36063 });
36064
36065
36066
36067
36068
36069 Roo.bootstrap.layout.South = function(config){
36070     config.region = 'south';
36071     config.cursor = 's-resize';
36072     Roo.bootstrap.layout.Split.call(this, config);
36073     if(this.split){
36074         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36075         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36076         this.split.el.addClass("roo-layout-split-v");
36077     }
36078     var size = config.initialSize || config.height;
36079     if(typeof size != "undefined"){
36080         this.el.setHeight(size);
36081     }
36082 };
36083
36084 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36085     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36086     getBox : function(){
36087         if(this.collapsed){
36088             return this.collapsedEl.getBox();
36089         }
36090         var box = this.el.getBox();
36091         if(this.split){
36092             var sh = this.split.el.getHeight();
36093             box.height += sh;
36094             box.y -= sh;
36095         }
36096         return box;
36097     },
36098     
36099     updateBox : function(box){
36100         if(this.split && !this.collapsed){
36101             var sh = this.split.el.getHeight();
36102             box.height -= sh;
36103             box.y += sh;
36104             this.split.el.setLeft(box.x);
36105             this.split.el.setTop(box.y-sh);
36106             this.split.el.setWidth(box.width);
36107         }
36108         if(this.collapsed){
36109             this.updateBody(box.width, null);
36110         }
36111         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36112     }
36113 });
36114
36115 Roo.bootstrap.layout.East = function(config){
36116     config.region = "east";
36117     config.cursor = "e-resize";
36118     Roo.bootstrap.layout.Split.call(this, config);
36119     if(this.split){
36120         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36121         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36122         this.split.el.addClass("roo-layout-split-h");
36123     }
36124     var size = config.initialSize || config.width;
36125     if(typeof size != "undefined"){
36126         this.el.setWidth(size);
36127     }
36128 };
36129 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36130     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36131     getBox : function(){
36132         if(this.collapsed){
36133             return this.collapsedEl.getBox();
36134         }
36135         var box = this.el.getBox();
36136         if(this.split){
36137             var sw = this.split.el.getWidth();
36138             box.width += sw;
36139             box.x -= sw;
36140         }
36141         return box;
36142     },
36143
36144     updateBox : function(box){
36145         if(this.split && !this.collapsed){
36146             var sw = this.split.el.getWidth();
36147             box.width -= sw;
36148             this.split.el.setLeft(box.x);
36149             this.split.el.setTop(box.y);
36150             this.split.el.setHeight(box.height);
36151             box.x += sw;
36152         }
36153         if(this.collapsed){
36154             this.updateBody(null, box.height);
36155         }
36156         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36157     }
36158 });
36159
36160 Roo.bootstrap.layout.West = function(config){
36161     config.region = "west";
36162     config.cursor = "w-resize";
36163     
36164     Roo.bootstrap.layout.Split.call(this, config);
36165     if(this.split){
36166         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36167         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36168         this.split.el.addClass("roo-layout-split-h");
36169     }
36170     
36171 };
36172 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36173     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36174     
36175     onRender: function(ctr, pos)
36176     {
36177         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36178         var size = this.config.initialSize || this.config.width;
36179         if(typeof size != "undefined"){
36180             this.el.setWidth(size);
36181         }
36182     },
36183     
36184     getBox : function(){
36185         if(this.collapsed){
36186             return this.collapsedEl.getBox();
36187         }
36188         var box = this.el.getBox();
36189         if(this.split){
36190             box.width += this.split.el.getWidth();
36191         }
36192         return box;
36193     },
36194     
36195     updateBox : function(box){
36196         if(this.split && !this.collapsed){
36197             var sw = this.split.el.getWidth();
36198             box.width -= sw;
36199             this.split.el.setLeft(box.x+box.width);
36200             this.split.el.setTop(box.y);
36201             this.split.el.setHeight(box.height);
36202         }
36203         if(this.collapsed){
36204             this.updateBody(null, box.height);
36205         }
36206         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36207     }
36208 });
36209 Roo.namespace("Roo.bootstrap.panel");/*
36210  * Based on:
36211  * Ext JS Library 1.1.1
36212  * Copyright(c) 2006-2007, Ext JS, LLC.
36213  *
36214  * Originally Released Under LGPL - original licence link has changed is not relivant.
36215  *
36216  * Fork - LGPL
36217  * <script type="text/javascript">
36218  */
36219 /**
36220  * @class Roo.ContentPanel
36221  * @extends Roo.util.Observable
36222  * A basic ContentPanel element.
36223  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36224  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36225  * @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
36226  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36227  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36228  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36229  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36230  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36231  * @cfg {String} title          The title for this panel
36232  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36233  * @cfg {String} url            Calls {@link #setUrl} with this value
36234  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36235  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36236  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36237  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36238  * @cfg {Boolean} badges render the badges
36239
36240  * @constructor
36241  * Create a new ContentPanel.
36242  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36243  * @param {String/Object} config A string to set only the title or a config object
36244  * @param {String} content (optional) Set the HTML content for this panel
36245  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36246  */
36247 Roo.bootstrap.panel.Content = function( config){
36248     
36249     this.tpl = config.tpl || false;
36250     
36251     var el = config.el;
36252     var content = config.content;
36253
36254     if(config.autoCreate){ // xtype is available if this is called from factory
36255         el = Roo.id();
36256     }
36257     this.el = Roo.get(el);
36258     if(!this.el && config && config.autoCreate){
36259         if(typeof config.autoCreate == "object"){
36260             if(!config.autoCreate.id){
36261                 config.autoCreate.id = config.id||el;
36262             }
36263             this.el = Roo.DomHelper.append(document.body,
36264                         config.autoCreate, true);
36265         }else{
36266             var elcfg =  {   tag: "div",
36267                             cls: "roo-layout-inactive-content",
36268                             id: config.id||el
36269                             };
36270             if (config.html) {
36271                 elcfg.html = config.html;
36272                 
36273             }
36274                         
36275             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36276         }
36277     } 
36278     this.closable = false;
36279     this.loaded = false;
36280     this.active = false;
36281    
36282       
36283     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36284         
36285         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36286         
36287         this.wrapEl = this.el; //this.el.wrap();
36288         var ti = [];
36289         if (config.toolbar.items) {
36290             ti = config.toolbar.items ;
36291             delete config.toolbar.items ;
36292         }
36293         
36294         var nitems = [];
36295         this.toolbar.render(this.wrapEl, 'before');
36296         for(var i =0;i < ti.length;i++) {
36297           //  Roo.log(['add child', items[i]]);
36298             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36299         }
36300         this.toolbar.items = nitems;
36301         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36302         delete config.toolbar;
36303         
36304     }
36305     /*
36306     // xtype created footer. - not sure if will work as we normally have to render first..
36307     if (this.footer && !this.footer.el && this.footer.xtype) {
36308         if (!this.wrapEl) {
36309             this.wrapEl = this.el.wrap();
36310         }
36311     
36312         this.footer.container = this.wrapEl.createChild();
36313          
36314         this.footer = Roo.factory(this.footer, Roo);
36315         
36316     }
36317     */
36318     
36319      if(typeof config == "string"){
36320         this.title = config;
36321     }else{
36322         Roo.apply(this, config);
36323     }
36324     
36325     if(this.resizeEl){
36326         this.resizeEl = Roo.get(this.resizeEl, true);
36327     }else{
36328         this.resizeEl = this.el;
36329     }
36330     // handle view.xtype
36331     
36332  
36333     
36334     
36335     this.addEvents({
36336         /**
36337          * @event activate
36338          * Fires when this panel is activated. 
36339          * @param {Roo.ContentPanel} this
36340          */
36341         "activate" : true,
36342         /**
36343          * @event deactivate
36344          * Fires when this panel is activated. 
36345          * @param {Roo.ContentPanel} this
36346          */
36347         "deactivate" : true,
36348
36349         /**
36350          * @event resize
36351          * Fires when this panel is resized if fitToFrame is true.
36352          * @param {Roo.ContentPanel} this
36353          * @param {Number} width The width after any component adjustments
36354          * @param {Number} height The height after any component adjustments
36355          */
36356         "resize" : true,
36357         
36358          /**
36359          * @event render
36360          * Fires when this tab is created
36361          * @param {Roo.ContentPanel} this
36362          */
36363         "render" : true
36364         
36365         
36366         
36367     });
36368     
36369
36370     
36371     
36372     if(this.autoScroll){
36373         this.resizeEl.setStyle("overflow", "auto");
36374     } else {
36375         // fix randome scrolling
36376         //this.el.on('scroll', function() {
36377         //    Roo.log('fix random scolling');
36378         //    this.scrollTo('top',0); 
36379         //});
36380     }
36381     content = content || this.content;
36382     if(content){
36383         this.setContent(content);
36384     }
36385     if(config && config.url){
36386         this.setUrl(this.url, this.params, this.loadOnce);
36387     }
36388     
36389     
36390     
36391     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36392     
36393     if (this.view && typeof(this.view.xtype) != 'undefined') {
36394         this.view.el = this.el.appendChild(document.createElement("div"));
36395         this.view = Roo.factory(this.view); 
36396         this.view.render  &&  this.view.render(false, '');  
36397     }
36398     
36399     
36400     this.fireEvent('render', this);
36401 };
36402
36403 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36404     
36405     tabTip : '',
36406     
36407     setRegion : function(region){
36408         this.region = region;
36409         this.setActiveClass(region && !this.background);
36410     },
36411     
36412     
36413     setActiveClass: function(state)
36414     {
36415         if(state){
36416            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36417            this.el.setStyle('position','relative');
36418         }else{
36419            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36420            this.el.setStyle('position', 'absolute');
36421         } 
36422     },
36423     
36424     /**
36425      * Returns the toolbar for this Panel if one was configured. 
36426      * @return {Roo.Toolbar} 
36427      */
36428     getToolbar : function(){
36429         return this.toolbar;
36430     },
36431     
36432     setActiveState : function(active)
36433     {
36434         this.active = active;
36435         this.setActiveClass(active);
36436         if(!active){
36437             this.fireEvent("deactivate", this);
36438         }else{
36439             this.fireEvent("activate", this);
36440         }
36441     },
36442     /**
36443      * Updates this panel's element
36444      * @param {String} content The new content
36445      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36446     */
36447     setContent : function(content, loadScripts){
36448         this.el.update(content, loadScripts);
36449     },
36450
36451     ignoreResize : function(w, h){
36452         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36453             return true;
36454         }else{
36455             this.lastSize = {width: w, height: h};
36456             return false;
36457         }
36458     },
36459     /**
36460      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36461      * @return {Roo.UpdateManager} The UpdateManager
36462      */
36463     getUpdateManager : function(){
36464         return this.el.getUpdateManager();
36465     },
36466      /**
36467      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36468      * @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:
36469 <pre><code>
36470 panel.load({
36471     url: "your-url.php",
36472     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36473     callback: yourFunction,
36474     scope: yourObject, //(optional scope)
36475     discardUrl: false,
36476     nocache: false,
36477     text: "Loading...",
36478     timeout: 30,
36479     scripts: false
36480 });
36481 </code></pre>
36482      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36483      * 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.
36484      * @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}
36485      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36486      * @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.
36487      * @return {Roo.ContentPanel} this
36488      */
36489     load : function(){
36490         var um = this.el.getUpdateManager();
36491         um.update.apply(um, arguments);
36492         return this;
36493     },
36494
36495
36496     /**
36497      * 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.
36498      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36499      * @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)
36500      * @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)
36501      * @return {Roo.UpdateManager} The UpdateManager
36502      */
36503     setUrl : function(url, params, loadOnce){
36504         if(this.refreshDelegate){
36505             this.removeListener("activate", this.refreshDelegate);
36506         }
36507         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36508         this.on("activate", this.refreshDelegate);
36509         return this.el.getUpdateManager();
36510     },
36511     
36512     _handleRefresh : function(url, params, loadOnce){
36513         if(!loadOnce || !this.loaded){
36514             var updater = this.el.getUpdateManager();
36515             updater.update(url, params, this._setLoaded.createDelegate(this));
36516         }
36517     },
36518     
36519     _setLoaded : function(){
36520         this.loaded = true;
36521     }, 
36522     
36523     /**
36524      * Returns this panel's id
36525      * @return {String} 
36526      */
36527     getId : function(){
36528         return this.el.id;
36529     },
36530     
36531     /** 
36532      * Returns this panel's element - used by regiosn to add.
36533      * @return {Roo.Element} 
36534      */
36535     getEl : function(){
36536         return this.wrapEl || this.el;
36537     },
36538     
36539    
36540     
36541     adjustForComponents : function(width, height)
36542     {
36543         //Roo.log('adjustForComponents ');
36544         if(this.resizeEl != this.el){
36545             width -= this.el.getFrameWidth('lr');
36546             height -= this.el.getFrameWidth('tb');
36547         }
36548         if(this.toolbar){
36549             var te = this.toolbar.getEl();
36550             te.setWidth(width);
36551             height -= te.getHeight();
36552         }
36553         if(this.footer){
36554             var te = this.footer.getEl();
36555             te.setWidth(width);
36556             height -= te.getHeight();
36557         }
36558         
36559         
36560         if(this.adjustments){
36561             width += this.adjustments[0];
36562             height += this.adjustments[1];
36563         }
36564         return {"width": width, "height": height};
36565     },
36566     
36567     setSize : function(width, height){
36568         if(this.fitToFrame && !this.ignoreResize(width, height)){
36569             if(this.fitContainer && this.resizeEl != this.el){
36570                 this.el.setSize(width, height);
36571             }
36572             var size = this.adjustForComponents(width, height);
36573             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36574             this.fireEvent('resize', this, size.width, size.height);
36575         }
36576     },
36577     
36578     /**
36579      * Returns this panel's title
36580      * @return {String} 
36581      */
36582     getTitle : function(){
36583         
36584         if (typeof(this.title) != 'object') {
36585             return this.title;
36586         }
36587         
36588         var t = '';
36589         for (var k in this.title) {
36590             if (!this.title.hasOwnProperty(k)) {
36591                 continue;
36592             }
36593             
36594             if (k.indexOf('-') >= 0) {
36595                 var s = k.split('-');
36596                 for (var i = 0; i<s.length; i++) {
36597                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36598                 }
36599             } else {
36600                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36601             }
36602         }
36603         return t;
36604     },
36605     
36606     /**
36607      * Set this panel's title
36608      * @param {String} title
36609      */
36610     setTitle : function(title){
36611         this.title = title;
36612         if(this.region){
36613             this.region.updatePanelTitle(this, title);
36614         }
36615     },
36616     
36617     /**
36618      * Returns true is this panel was configured to be closable
36619      * @return {Boolean} 
36620      */
36621     isClosable : function(){
36622         return this.closable;
36623     },
36624     
36625     beforeSlide : function(){
36626         this.el.clip();
36627         this.resizeEl.clip();
36628     },
36629     
36630     afterSlide : function(){
36631         this.el.unclip();
36632         this.resizeEl.unclip();
36633     },
36634     
36635     /**
36636      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36637      *   Will fail silently if the {@link #setUrl} method has not been called.
36638      *   This does not activate the panel, just updates its content.
36639      */
36640     refresh : function(){
36641         if(this.refreshDelegate){
36642            this.loaded = false;
36643            this.refreshDelegate();
36644         }
36645     },
36646     
36647     /**
36648      * Destroys this panel
36649      */
36650     destroy : function(){
36651         this.el.removeAllListeners();
36652         var tempEl = document.createElement("span");
36653         tempEl.appendChild(this.el.dom);
36654         tempEl.innerHTML = "";
36655         this.el.remove();
36656         this.el = null;
36657     },
36658     
36659     /**
36660      * form - if the content panel contains a form - this is a reference to it.
36661      * @type {Roo.form.Form}
36662      */
36663     form : false,
36664     /**
36665      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36666      *    This contains a reference to it.
36667      * @type {Roo.View}
36668      */
36669     view : false,
36670     
36671       /**
36672      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36673      * <pre><code>
36674
36675 layout.addxtype({
36676        xtype : 'Form',
36677        items: [ .... ]
36678    }
36679 );
36680
36681 </code></pre>
36682      * @param {Object} cfg Xtype definition of item to add.
36683      */
36684     
36685     
36686     getChildContainer: function () {
36687         return this.getEl();
36688     }
36689     
36690     
36691     /*
36692         var  ret = new Roo.factory(cfg);
36693         return ret;
36694         
36695         
36696         // add form..
36697         if (cfg.xtype.match(/^Form$/)) {
36698             
36699             var el;
36700             //if (this.footer) {
36701             //    el = this.footer.container.insertSibling(false, 'before');
36702             //} else {
36703                 el = this.el.createChild();
36704             //}
36705
36706             this.form = new  Roo.form.Form(cfg);
36707             
36708             
36709             if ( this.form.allItems.length) {
36710                 this.form.render(el.dom);
36711             }
36712             return this.form;
36713         }
36714         // should only have one of theses..
36715         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36716             // views.. should not be just added - used named prop 'view''
36717             
36718             cfg.el = this.el.appendChild(document.createElement("div"));
36719             // factory?
36720             
36721             var ret = new Roo.factory(cfg);
36722              
36723              ret.render && ret.render(false, ''); // render blank..
36724             this.view = ret;
36725             return ret;
36726         }
36727         return false;
36728     }
36729     \*/
36730 });
36731  
36732 /**
36733  * @class Roo.bootstrap.panel.Grid
36734  * @extends Roo.bootstrap.panel.Content
36735  * @constructor
36736  * Create a new GridPanel.
36737  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36738  * @param {Object} config A the config object
36739   
36740  */
36741
36742
36743
36744 Roo.bootstrap.panel.Grid = function(config)
36745 {
36746     
36747       
36748     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36749         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36750
36751     config.el = this.wrapper;
36752     //this.el = this.wrapper;
36753     
36754       if (config.container) {
36755         // ctor'ed from a Border/panel.grid
36756         
36757         
36758         this.wrapper.setStyle("overflow", "hidden");
36759         this.wrapper.addClass('roo-grid-container');
36760
36761     }
36762     
36763     
36764     if(config.toolbar){
36765         var tool_el = this.wrapper.createChild();    
36766         this.toolbar = Roo.factory(config.toolbar);
36767         var ti = [];
36768         if (config.toolbar.items) {
36769             ti = config.toolbar.items ;
36770             delete config.toolbar.items ;
36771         }
36772         
36773         var nitems = [];
36774         this.toolbar.render(tool_el);
36775         for(var i =0;i < ti.length;i++) {
36776           //  Roo.log(['add child', items[i]]);
36777             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36778         }
36779         this.toolbar.items = nitems;
36780         
36781         delete config.toolbar;
36782     }
36783     
36784     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36785     config.grid.scrollBody = true;;
36786     config.grid.monitorWindowResize = false; // turn off autosizing
36787     config.grid.autoHeight = false;
36788     config.grid.autoWidth = false;
36789     
36790     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36791     
36792     if (config.background) {
36793         // render grid on panel activation (if panel background)
36794         this.on('activate', function(gp) {
36795             if (!gp.grid.rendered) {
36796                 gp.grid.render(this.wrapper);
36797                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36798             }
36799         });
36800             
36801     } else {
36802         this.grid.render(this.wrapper);
36803         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36804
36805     }
36806     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36807     // ??? needed ??? config.el = this.wrapper;
36808     
36809     
36810     
36811   
36812     // xtype created footer. - not sure if will work as we normally have to render first..
36813     if (this.footer && !this.footer.el && this.footer.xtype) {
36814         
36815         var ctr = this.grid.getView().getFooterPanel(true);
36816         this.footer.dataSource = this.grid.dataSource;
36817         this.footer = Roo.factory(this.footer, Roo);
36818         this.footer.render(ctr);
36819         
36820     }
36821     
36822     
36823     
36824     
36825      
36826 };
36827
36828 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36829     getId : function(){
36830         return this.grid.id;
36831     },
36832     
36833     /**
36834      * Returns the grid for this panel
36835      * @return {Roo.bootstrap.Table} 
36836      */
36837     getGrid : function(){
36838         return this.grid;    
36839     },
36840     
36841     setSize : function(width, height){
36842         if(!this.ignoreResize(width, height)){
36843             var grid = this.grid;
36844             var size = this.adjustForComponents(width, height);
36845             var gridel = grid.getGridEl();
36846             gridel.setSize(size.width, size.height);
36847             /*
36848             var thd = grid.getGridEl().select('thead',true).first();
36849             var tbd = grid.getGridEl().select('tbody', true).first();
36850             if (tbd) {
36851                 tbd.setSize(width, height - thd.getHeight());
36852             }
36853             */
36854             grid.autoSize();
36855         }
36856     },
36857      
36858     
36859     
36860     beforeSlide : function(){
36861         this.grid.getView().scroller.clip();
36862     },
36863     
36864     afterSlide : function(){
36865         this.grid.getView().scroller.unclip();
36866     },
36867     
36868     destroy : function(){
36869         this.grid.destroy();
36870         delete this.grid;
36871         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36872     }
36873 });
36874
36875 /**
36876  * @class Roo.bootstrap.panel.Nest
36877  * @extends Roo.bootstrap.panel.Content
36878  * @constructor
36879  * Create a new Panel, that can contain a layout.Border.
36880  * 
36881  * 
36882  * @param {Roo.BorderLayout} layout The layout for this panel
36883  * @param {String/Object} config A string to set only the title or a config object
36884  */
36885 Roo.bootstrap.panel.Nest = function(config)
36886 {
36887     // construct with only one argument..
36888     /* FIXME - implement nicer consturctors
36889     if (layout.layout) {
36890         config = layout;
36891         layout = config.layout;
36892         delete config.layout;
36893     }
36894     if (layout.xtype && !layout.getEl) {
36895         // then layout needs constructing..
36896         layout = Roo.factory(layout, Roo);
36897     }
36898     */
36899     
36900     config.el =  config.layout.getEl();
36901     
36902     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36903     
36904     config.layout.monitorWindowResize = false; // turn off autosizing
36905     this.layout = config.layout;
36906     this.layout.getEl().addClass("roo-layout-nested-layout");
36907     
36908     
36909     
36910     
36911 };
36912
36913 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36914
36915     setSize : function(width, height){
36916         if(!this.ignoreResize(width, height)){
36917             var size = this.adjustForComponents(width, height);
36918             var el = this.layout.getEl();
36919             if (size.height < 1) {
36920                 el.setWidth(size.width);   
36921             } else {
36922                 el.setSize(size.width, size.height);
36923             }
36924             var touch = el.dom.offsetWidth;
36925             this.layout.layout();
36926             // ie requires a double layout on the first pass
36927             if(Roo.isIE && !this.initialized){
36928                 this.initialized = true;
36929                 this.layout.layout();
36930             }
36931         }
36932     },
36933     
36934     // activate all subpanels if not currently active..
36935     
36936     setActiveState : function(active){
36937         this.active = active;
36938         this.setActiveClass(active);
36939         
36940         if(!active){
36941             this.fireEvent("deactivate", this);
36942             return;
36943         }
36944         
36945         this.fireEvent("activate", this);
36946         // not sure if this should happen before or after..
36947         if (!this.layout) {
36948             return; // should not happen..
36949         }
36950         var reg = false;
36951         for (var r in this.layout.regions) {
36952             reg = this.layout.getRegion(r);
36953             if (reg.getActivePanel()) {
36954                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36955                 reg.setActivePanel(reg.getActivePanel());
36956                 continue;
36957             }
36958             if (!reg.panels.length) {
36959                 continue;
36960             }
36961             reg.showPanel(reg.getPanel(0));
36962         }
36963         
36964         
36965         
36966         
36967     },
36968     
36969     /**
36970      * Returns the nested BorderLayout for this panel
36971      * @return {Roo.BorderLayout} 
36972      */
36973     getLayout : function(){
36974         return this.layout;
36975     },
36976     
36977      /**
36978      * Adds a xtype elements to the layout of the nested panel
36979      * <pre><code>
36980
36981 panel.addxtype({
36982        xtype : 'ContentPanel',
36983        region: 'west',
36984        items: [ .... ]
36985    }
36986 );
36987
36988 panel.addxtype({
36989         xtype : 'NestedLayoutPanel',
36990         region: 'west',
36991         layout: {
36992            center: { },
36993            west: { }   
36994         },
36995         items : [ ... list of content panels or nested layout panels.. ]
36996    }
36997 );
36998 </code></pre>
36999      * @param {Object} cfg Xtype definition of item to add.
37000      */
37001     addxtype : function(cfg) {
37002         return this.layout.addxtype(cfg);
37003     
37004     }
37005 });        /*
37006  * Based on:
37007  * Ext JS Library 1.1.1
37008  * Copyright(c) 2006-2007, Ext JS, LLC.
37009  *
37010  * Originally Released Under LGPL - original licence link has changed is not relivant.
37011  *
37012  * Fork - LGPL
37013  * <script type="text/javascript">
37014  */
37015 /**
37016  * @class Roo.TabPanel
37017  * @extends Roo.util.Observable
37018  * A lightweight tab container.
37019  * <br><br>
37020  * Usage:
37021  * <pre><code>
37022 // basic tabs 1, built from existing content
37023 var tabs = new Roo.TabPanel("tabs1");
37024 tabs.addTab("script", "View Script");
37025 tabs.addTab("markup", "View Markup");
37026 tabs.activate("script");
37027
37028 // more advanced tabs, built from javascript
37029 var jtabs = new Roo.TabPanel("jtabs");
37030 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37031
37032 // set up the UpdateManager
37033 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37034 var updater = tab2.getUpdateManager();
37035 updater.setDefaultUrl("ajax1.htm");
37036 tab2.on('activate', updater.refresh, updater, true);
37037
37038 // Use setUrl for Ajax loading
37039 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37040 tab3.setUrl("ajax2.htm", null, true);
37041
37042 // Disabled tab
37043 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37044 tab4.disable();
37045
37046 jtabs.activate("jtabs-1");
37047  * </code></pre>
37048  * @constructor
37049  * Create a new TabPanel.
37050  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37051  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37052  */
37053 Roo.bootstrap.panel.Tabs = function(config){
37054     /**
37055     * The container element for this TabPanel.
37056     * @type Roo.Element
37057     */
37058     this.el = Roo.get(config.el);
37059     delete config.el;
37060     if(config){
37061         if(typeof config == "boolean"){
37062             this.tabPosition = config ? "bottom" : "top";
37063         }else{
37064             Roo.apply(this, config);
37065         }
37066     }
37067     
37068     if(this.tabPosition == "bottom"){
37069         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37070         this.el.addClass("roo-tabs-bottom");
37071     }
37072     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37073     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37074     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37075     if(Roo.isIE){
37076         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37077     }
37078     if(this.tabPosition != "bottom"){
37079         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37080          * @type Roo.Element
37081          */
37082         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37083         this.el.addClass("roo-tabs-top");
37084     }
37085     this.items = [];
37086
37087     this.bodyEl.setStyle("position", "relative");
37088
37089     this.active = null;
37090     this.activateDelegate = this.activate.createDelegate(this);
37091
37092     this.addEvents({
37093         /**
37094          * @event tabchange
37095          * Fires when the active tab changes
37096          * @param {Roo.TabPanel} this
37097          * @param {Roo.TabPanelItem} activePanel The new active tab
37098          */
37099         "tabchange": true,
37100         /**
37101          * @event beforetabchange
37102          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37103          * @param {Roo.TabPanel} this
37104          * @param {Object} e Set cancel to true on this object to cancel the tab change
37105          * @param {Roo.TabPanelItem} tab The tab being changed to
37106          */
37107         "beforetabchange" : true
37108     });
37109
37110     Roo.EventManager.onWindowResize(this.onResize, this);
37111     this.cpad = this.el.getPadding("lr");
37112     this.hiddenCount = 0;
37113
37114
37115     // toolbar on the tabbar support...
37116     if (this.toolbar) {
37117         alert("no toolbar support yet");
37118         this.toolbar  = false;
37119         /*
37120         var tcfg = this.toolbar;
37121         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37122         this.toolbar = new Roo.Toolbar(tcfg);
37123         if (Roo.isSafari) {
37124             var tbl = tcfg.container.child('table', true);
37125             tbl.setAttribute('width', '100%');
37126         }
37127         */
37128         
37129     }
37130    
37131
37132
37133     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37134 };
37135
37136 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37137     /*
37138      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37139      */
37140     tabPosition : "top",
37141     /*
37142      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37143      */
37144     currentTabWidth : 0,
37145     /*
37146      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37147      */
37148     minTabWidth : 40,
37149     /*
37150      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37151      */
37152     maxTabWidth : 250,
37153     /*
37154      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37155      */
37156     preferredTabWidth : 175,
37157     /*
37158      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37159      */
37160     resizeTabs : false,
37161     /*
37162      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37163      */
37164     monitorResize : true,
37165     /*
37166      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37167      */
37168     toolbar : false,
37169
37170     /**
37171      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37172      * @param {String} id The id of the div to use <b>or create</b>
37173      * @param {String} text The text for the tab
37174      * @param {String} content (optional) Content to put in the TabPanelItem body
37175      * @param {Boolean} closable (optional) True to create a close icon on the tab
37176      * @return {Roo.TabPanelItem} The created TabPanelItem
37177      */
37178     addTab : function(id, text, content, closable, tpl)
37179     {
37180         var item = new Roo.bootstrap.panel.TabItem({
37181             panel: this,
37182             id : id,
37183             text : text,
37184             closable : closable,
37185             tpl : tpl
37186         });
37187         this.addTabItem(item);
37188         if(content){
37189             item.setContent(content);
37190         }
37191         return item;
37192     },
37193
37194     /**
37195      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37196      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37197      * @return {Roo.TabPanelItem}
37198      */
37199     getTab : function(id){
37200         return this.items[id];
37201     },
37202
37203     /**
37204      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37205      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37206      */
37207     hideTab : function(id){
37208         var t = this.items[id];
37209         if(!t.isHidden()){
37210            t.setHidden(true);
37211            this.hiddenCount++;
37212            this.autoSizeTabs();
37213         }
37214     },
37215
37216     /**
37217      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37218      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37219      */
37220     unhideTab : function(id){
37221         var t = this.items[id];
37222         if(t.isHidden()){
37223            t.setHidden(false);
37224            this.hiddenCount--;
37225            this.autoSizeTabs();
37226         }
37227     },
37228
37229     /**
37230      * Adds an existing {@link Roo.TabPanelItem}.
37231      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37232      */
37233     addTabItem : function(item){
37234         this.items[item.id] = item;
37235         this.items.push(item);
37236       //  if(this.resizeTabs){
37237     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37238   //         this.autoSizeTabs();
37239 //        }else{
37240 //            item.autoSize();
37241        // }
37242     },
37243
37244     /**
37245      * Removes a {@link Roo.TabPanelItem}.
37246      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37247      */
37248     removeTab : function(id){
37249         var items = this.items;
37250         var tab = items[id];
37251         if(!tab) { return; }
37252         var index = items.indexOf(tab);
37253         if(this.active == tab && items.length > 1){
37254             var newTab = this.getNextAvailable(index);
37255             if(newTab) {
37256                 newTab.activate();
37257             }
37258         }
37259         this.stripEl.dom.removeChild(tab.pnode.dom);
37260         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37261             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37262         }
37263         items.splice(index, 1);
37264         delete this.items[tab.id];
37265         tab.fireEvent("close", tab);
37266         tab.purgeListeners();
37267         this.autoSizeTabs();
37268     },
37269
37270     getNextAvailable : function(start){
37271         var items = this.items;
37272         var index = start;
37273         // look for a next tab that will slide over to
37274         // replace the one being removed
37275         while(index < items.length){
37276             var item = items[++index];
37277             if(item && !item.isHidden()){
37278                 return item;
37279             }
37280         }
37281         // if one isn't found select the previous tab (on the left)
37282         index = start;
37283         while(index >= 0){
37284             var item = items[--index];
37285             if(item && !item.isHidden()){
37286                 return item;
37287             }
37288         }
37289         return null;
37290     },
37291
37292     /**
37293      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37294      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37295      */
37296     disableTab : function(id){
37297         var tab = this.items[id];
37298         if(tab && this.active != tab){
37299             tab.disable();
37300         }
37301     },
37302
37303     /**
37304      * Enables a {@link Roo.TabPanelItem} that is disabled.
37305      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37306      */
37307     enableTab : function(id){
37308         var tab = this.items[id];
37309         tab.enable();
37310     },
37311
37312     /**
37313      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37314      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37315      * @return {Roo.TabPanelItem} The TabPanelItem.
37316      */
37317     activate : function(id){
37318         var tab = this.items[id];
37319         if(!tab){
37320             return null;
37321         }
37322         if(tab == this.active || tab.disabled){
37323             return tab;
37324         }
37325         var e = {};
37326         this.fireEvent("beforetabchange", this, e, tab);
37327         if(e.cancel !== true && !tab.disabled){
37328             if(this.active){
37329                 this.active.hide();
37330             }
37331             this.active = this.items[id];
37332             this.active.show();
37333             this.fireEvent("tabchange", this, this.active);
37334         }
37335         return tab;
37336     },
37337
37338     /**
37339      * Gets the active {@link Roo.TabPanelItem}.
37340      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37341      */
37342     getActiveTab : function(){
37343         return this.active;
37344     },
37345
37346     /**
37347      * Updates the tab body element to fit the height of the container element
37348      * for overflow scrolling
37349      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37350      */
37351     syncHeight : function(targetHeight){
37352         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37353         var bm = this.bodyEl.getMargins();
37354         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37355         this.bodyEl.setHeight(newHeight);
37356         return newHeight;
37357     },
37358
37359     onResize : function(){
37360         if(this.monitorResize){
37361             this.autoSizeTabs();
37362         }
37363     },
37364
37365     /**
37366      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37367      */
37368     beginUpdate : function(){
37369         this.updating = true;
37370     },
37371
37372     /**
37373      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37374      */
37375     endUpdate : function(){
37376         this.updating = false;
37377         this.autoSizeTabs();
37378     },
37379
37380     /**
37381      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37382      */
37383     autoSizeTabs : function(){
37384         var count = this.items.length;
37385         var vcount = count - this.hiddenCount;
37386         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37387             return;
37388         }
37389         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37390         var availWidth = Math.floor(w / vcount);
37391         var b = this.stripBody;
37392         if(b.getWidth() > w){
37393             var tabs = this.items;
37394             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37395             if(availWidth < this.minTabWidth){
37396                 /*if(!this.sleft){    // incomplete scrolling code
37397                     this.createScrollButtons();
37398                 }
37399                 this.showScroll();
37400                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37401             }
37402         }else{
37403             if(this.currentTabWidth < this.preferredTabWidth){
37404                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37405             }
37406         }
37407     },
37408
37409     /**
37410      * Returns the number of tabs in this TabPanel.
37411      * @return {Number}
37412      */
37413      getCount : function(){
37414          return this.items.length;
37415      },
37416
37417     /**
37418      * Resizes all the tabs to the passed width
37419      * @param {Number} The new width
37420      */
37421     setTabWidth : function(width){
37422         this.currentTabWidth = width;
37423         for(var i = 0, len = this.items.length; i < len; i++) {
37424                 if(!this.items[i].isHidden()) {
37425                 this.items[i].setWidth(width);
37426             }
37427         }
37428     },
37429
37430     /**
37431      * Destroys this TabPanel
37432      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37433      */
37434     destroy : function(removeEl){
37435         Roo.EventManager.removeResizeListener(this.onResize, this);
37436         for(var i = 0, len = this.items.length; i < len; i++){
37437             this.items[i].purgeListeners();
37438         }
37439         if(removeEl === true){
37440             this.el.update("");
37441             this.el.remove();
37442         }
37443     },
37444     
37445     createStrip : function(container)
37446     {
37447         var strip = document.createElement("nav");
37448         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37449         container.appendChild(strip);
37450         return strip;
37451     },
37452     
37453     createStripList : function(strip)
37454     {
37455         // div wrapper for retard IE
37456         // returns the "tr" element.
37457         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37458         //'<div class="x-tabs-strip-wrap">'+
37459           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37460           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37461         return strip.firstChild; //.firstChild.firstChild.firstChild;
37462     },
37463     createBody : function(container)
37464     {
37465         var body = document.createElement("div");
37466         Roo.id(body, "tab-body");
37467         //Roo.fly(body).addClass("x-tabs-body");
37468         Roo.fly(body).addClass("tab-content");
37469         container.appendChild(body);
37470         return body;
37471     },
37472     createItemBody :function(bodyEl, id){
37473         var body = Roo.getDom(id);
37474         if(!body){
37475             body = document.createElement("div");
37476             body.id = id;
37477         }
37478         //Roo.fly(body).addClass("x-tabs-item-body");
37479         Roo.fly(body).addClass("tab-pane");
37480          bodyEl.insertBefore(body, bodyEl.firstChild);
37481         return body;
37482     },
37483     /** @private */
37484     createStripElements :  function(stripEl, text, closable, tpl)
37485     {
37486         var td = document.createElement("li"); // was td..
37487         
37488         
37489         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37490         
37491         
37492         stripEl.appendChild(td);
37493         /*if(closable){
37494             td.className = "x-tabs-closable";
37495             if(!this.closeTpl){
37496                 this.closeTpl = new Roo.Template(
37497                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37498                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37499                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37500                 );
37501             }
37502             var el = this.closeTpl.overwrite(td, {"text": text});
37503             var close = el.getElementsByTagName("div")[0];
37504             var inner = el.getElementsByTagName("em")[0];
37505             return {"el": el, "close": close, "inner": inner};
37506         } else {
37507         */
37508         // not sure what this is..
37509 //            if(!this.tabTpl){
37510                 //this.tabTpl = new Roo.Template(
37511                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37512                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37513                 //);
37514 //                this.tabTpl = new Roo.Template(
37515 //                   '<a href="#">' +
37516 //                   '<span unselectable="on"' +
37517 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37518 //                            ' >{text}</span></a>'
37519 //                );
37520 //                
37521 //            }
37522
37523
37524             var template = tpl || this.tabTpl || false;
37525             
37526             if(!template){
37527                 
37528                 template = new Roo.Template(
37529                    '<a href="#">' +
37530                    '<span unselectable="on"' +
37531                             (this.disableTooltips ? '' : ' title="{text}"') +
37532                             ' >{text}</span></a>'
37533                 );
37534             }
37535             
37536             switch (typeof(template)) {
37537                 case 'object' :
37538                     break;
37539                 case 'string' :
37540                     template = new Roo.Template(template);
37541                     break;
37542                 default :
37543                     break;
37544             }
37545             
37546             var el = template.overwrite(td, {"text": text});
37547             
37548             var inner = el.getElementsByTagName("span")[0];
37549             
37550             return {"el": el, "inner": inner};
37551             
37552     }
37553         
37554     
37555 });
37556
37557 /**
37558  * @class Roo.TabPanelItem
37559  * @extends Roo.util.Observable
37560  * Represents an individual item (tab plus body) in a TabPanel.
37561  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37562  * @param {String} id The id of this TabPanelItem
37563  * @param {String} text The text for the tab of this TabPanelItem
37564  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37565  */
37566 Roo.bootstrap.panel.TabItem = function(config){
37567     /**
37568      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37569      * @type Roo.TabPanel
37570      */
37571     this.tabPanel = config.panel;
37572     /**
37573      * The id for this TabPanelItem
37574      * @type String
37575      */
37576     this.id = config.id;
37577     /** @private */
37578     this.disabled = false;
37579     /** @private */
37580     this.text = config.text;
37581     /** @private */
37582     this.loaded = false;
37583     this.closable = config.closable;
37584
37585     /**
37586      * The body element for this TabPanelItem.
37587      * @type Roo.Element
37588      */
37589     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37590     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37591     this.bodyEl.setStyle("display", "block");
37592     this.bodyEl.setStyle("zoom", "1");
37593     //this.hideAction();
37594
37595     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37596     /** @private */
37597     this.el = Roo.get(els.el);
37598     this.inner = Roo.get(els.inner, true);
37599     this.textEl = Roo.get(this.el.dom.firstChild, true);
37600     this.pnode = Roo.get(els.el.parentNode, true);
37601     this.el.on("mousedown", this.onTabMouseDown, this);
37602     this.el.on("click", this.onTabClick, this);
37603     /** @private */
37604     if(config.closable){
37605         var c = Roo.get(els.close, true);
37606         c.dom.title = this.closeText;
37607         c.addClassOnOver("close-over");
37608         c.on("click", this.closeClick, this);
37609      }
37610
37611     this.addEvents({
37612          /**
37613          * @event activate
37614          * Fires when this tab becomes the active tab.
37615          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37616          * @param {Roo.TabPanelItem} this
37617          */
37618         "activate": true,
37619         /**
37620          * @event beforeclose
37621          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37622          * @param {Roo.TabPanelItem} this
37623          * @param {Object} e Set cancel to true on this object to cancel the close.
37624          */
37625         "beforeclose": true,
37626         /**
37627          * @event close
37628          * Fires when this tab is closed.
37629          * @param {Roo.TabPanelItem} this
37630          */
37631          "close": true,
37632         /**
37633          * @event deactivate
37634          * Fires when this tab is no longer the active tab.
37635          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37636          * @param {Roo.TabPanelItem} this
37637          */
37638          "deactivate" : true
37639     });
37640     this.hidden = false;
37641
37642     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37643 };
37644
37645 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37646            {
37647     purgeListeners : function(){
37648        Roo.util.Observable.prototype.purgeListeners.call(this);
37649        this.el.removeAllListeners();
37650     },
37651     /**
37652      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37653      */
37654     show : function(){
37655         this.pnode.addClass("active");
37656         this.showAction();
37657         if(Roo.isOpera){
37658             this.tabPanel.stripWrap.repaint();
37659         }
37660         this.fireEvent("activate", this.tabPanel, this);
37661     },
37662
37663     /**
37664      * Returns true if this tab is the active tab.
37665      * @return {Boolean}
37666      */
37667     isActive : function(){
37668         return this.tabPanel.getActiveTab() == this;
37669     },
37670
37671     /**
37672      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37673      */
37674     hide : function(){
37675         this.pnode.removeClass("active");
37676         this.hideAction();
37677         this.fireEvent("deactivate", this.tabPanel, this);
37678     },
37679
37680     hideAction : function(){
37681         this.bodyEl.hide();
37682         this.bodyEl.setStyle("position", "absolute");
37683         this.bodyEl.setLeft("-20000px");
37684         this.bodyEl.setTop("-20000px");
37685     },
37686
37687     showAction : function(){
37688         this.bodyEl.setStyle("position", "relative");
37689         this.bodyEl.setTop("");
37690         this.bodyEl.setLeft("");
37691         this.bodyEl.show();
37692     },
37693
37694     /**
37695      * Set the tooltip for the tab.
37696      * @param {String} tooltip The tab's tooltip
37697      */
37698     setTooltip : function(text){
37699         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37700             this.textEl.dom.qtip = text;
37701             this.textEl.dom.removeAttribute('title');
37702         }else{
37703             this.textEl.dom.title = text;
37704         }
37705     },
37706
37707     onTabClick : function(e){
37708         e.preventDefault();
37709         this.tabPanel.activate(this.id);
37710     },
37711
37712     onTabMouseDown : function(e){
37713         e.preventDefault();
37714         this.tabPanel.activate(this.id);
37715     },
37716 /*
37717     getWidth : function(){
37718         return this.inner.getWidth();
37719     },
37720
37721     setWidth : function(width){
37722         var iwidth = width - this.pnode.getPadding("lr");
37723         this.inner.setWidth(iwidth);
37724         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37725         this.pnode.setWidth(width);
37726     },
37727 */
37728     /**
37729      * Show or hide the tab
37730      * @param {Boolean} hidden True to hide or false to show.
37731      */
37732     setHidden : function(hidden){
37733         this.hidden = hidden;
37734         this.pnode.setStyle("display", hidden ? "none" : "");
37735     },
37736
37737     /**
37738      * Returns true if this tab is "hidden"
37739      * @return {Boolean}
37740      */
37741     isHidden : function(){
37742         return this.hidden;
37743     },
37744
37745     /**
37746      * Returns the text for this tab
37747      * @return {String}
37748      */
37749     getText : function(){
37750         return this.text;
37751     },
37752     /*
37753     autoSize : function(){
37754         //this.el.beginMeasure();
37755         this.textEl.setWidth(1);
37756         /*
37757          *  #2804 [new] Tabs in Roojs
37758          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37759          */
37760         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37761         //this.el.endMeasure();
37762     //},
37763
37764     /**
37765      * Sets the text for the tab (Note: this also sets the tooltip text)
37766      * @param {String} text The tab's text and tooltip
37767      */
37768     setText : function(text){
37769         this.text = text;
37770         this.textEl.update(text);
37771         this.setTooltip(text);
37772         //if(!this.tabPanel.resizeTabs){
37773         //    this.autoSize();
37774         //}
37775     },
37776     /**
37777      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37778      */
37779     activate : function(){
37780         this.tabPanel.activate(this.id);
37781     },
37782
37783     /**
37784      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37785      */
37786     disable : function(){
37787         if(this.tabPanel.active != this){
37788             this.disabled = true;
37789             this.pnode.addClass("disabled");
37790         }
37791     },
37792
37793     /**
37794      * Enables this TabPanelItem if it was previously disabled.
37795      */
37796     enable : function(){
37797         this.disabled = false;
37798         this.pnode.removeClass("disabled");
37799     },
37800
37801     /**
37802      * Sets the content for this TabPanelItem.
37803      * @param {String} content The content
37804      * @param {Boolean} loadScripts true to look for and load scripts
37805      */
37806     setContent : function(content, loadScripts){
37807         this.bodyEl.update(content, loadScripts);
37808     },
37809
37810     /**
37811      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37812      * @return {Roo.UpdateManager} The UpdateManager
37813      */
37814     getUpdateManager : function(){
37815         return this.bodyEl.getUpdateManager();
37816     },
37817
37818     /**
37819      * Set a URL to be used to load the content for this TabPanelItem.
37820      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37821      * @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)
37822      * @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)
37823      * @return {Roo.UpdateManager} The UpdateManager
37824      */
37825     setUrl : function(url, params, loadOnce){
37826         if(this.refreshDelegate){
37827             this.un('activate', this.refreshDelegate);
37828         }
37829         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37830         this.on("activate", this.refreshDelegate);
37831         return this.bodyEl.getUpdateManager();
37832     },
37833
37834     /** @private */
37835     _handleRefresh : function(url, params, loadOnce){
37836         if(!loadOnce || !this.loaded){
37837             var updater = this.bodyEl.getUpdateManager();
37838             updater.update(url, params, this._setLoaded.createDelegate(this));
37839         }
37840     },
37841
37842     /**
37843      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37844      *   Will fail silently if the setUrl method has not been called.
37845      *   This does not activate the panel, just updates its content.
37846      */
37847     refresh : function(){
37848         if(this.refreshDelegate){
37849            this.loaded = false;
37850            this.refreshDelegate();
37851         }
37852     },
37853
37854     /** @private */
37855     _setLoaded : function(){
37856         this.loaded = true;
37857     },
37858
37859     /** @private */
37860     closeClick : function(e){
37861         var o = {};
37862         e.stopEvent();
37863         this.fireEvent("beforeclose", this, o);
37864         if(o.cancel !== true){
37865             this.tabPanel.removeTab(this.id);
37866         }
37867     },
37868     /**
37869      * The text displayed in the tooltip for the close icon.
37870      * @type String
37871      */
37872     closeText : "Close this tab"
37873 });
37874 /*
37875  * - LGPL
37876  *
37877  * element
37878  * 
37879  */
37880
37881 /**
37882  * @class Roo.bootstrap.PhoneInput
37883  * @extends Roo.bootstrap.TriggerField
37884  * Bootstrap PhoneInput class
37885  * 
37886  * @constructor
37887  * Create a new PhoneInput
37888  * @param {Object} config The config object
37889 */
37890
37891 Roo.bootstrap.PhoneInput = function(config){
37892     
37893     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37894     
37895     this.addEvents({
37896         /**
37897          * @event expand
37898          * Fires when the dropdown list is expanded
37899              * @param {Roo.bootstrap.ComboBox} combo This combo box
37900              */
37901         'expand' : true,
37902         /**
37903          * @event collapse
37904          * Fires when the dropdown list is collapsed
37905              * @param {Roo.bootstrap.ComboBox} combo This combo box
37906              */
37907         'collapse' : true,
37908         /**
37909          * @event beforeselect
37910          * Fires before a list item is selected. Return false to cancel the selection.
37911              * @param {Roo.bootstrap.ComboBox} combo This combo box
37912              * @param {Roo.data.Record} record The data record returned from the underlying store
37913              * @param {Number} index The index of the selected item in the dropdown list
37914              */
37915         'beforeselect' : true,
37916         /**
37917          * @event select
37918          * Fires when a list item is selected
37919              * @param {Roo.bootstrap.ComboBox} combo This combo box
37920              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37921              * @param {Number} index The index of the selected item in the dropdown list
37922              */
37923         'select' : true,
37924         /**
37925          * @event beforequery
37926          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37927          * The event object passed has these properties:
37928              * @param {Roo.bootstrap.ComboBox} combo This combo box
37929              * @param {String} query The query
37930              * @param {Boolean} forceAll true to force "all" query
37931              * @param {Boolean} cancel true to cancel the query
37932              * @param {Object} e The query event object
37933              */
37934         'beforequery': true,
37935          /**
37936          * @event add
37937          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37938              * @param {Roo.bootstrap.ComboBox} combo This combo box
37939              */
37940         'add' : true,
37941         /**
37942          * @event edit
37943          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37944              * @param {Roo.bootstrap.ComboBox} combo This combo box
37945              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37946              */
37947         'edit' : true,
37948         /**
37949          * @event remove
37950          * Fires when the remove value from the combobox array
37951              * @param {Roo.bootstrap.ComboBox} combo This combo box
37952              */
37953         'remove' : true,
37954         /**
37955          * @event afterremove
37956          * Fires when the remove value from the combobox array
37957              * @param {Roo.bootstrap.ComboBox} combo This combo box
37958              */
37959         'afterremove' : true,
37960         /**
37961          * @event specialfilter
37962          * Fires when specialfilter
37963             * @param {Roo.bootstrap.ComboBox} combo This combo box
37964             */
37965         'touchviewdisplay' : true
37966     });
37967     
37968     this.country = []; //fetch country JSON
37969 };
37970
37971 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37972      
37973      listWidth: undefined,
37974      
37975      modalTitle : '', 
37976      
37977      selectedClass: 'active',
37978      
37979      maxHeight: 300,
37980      
37981      minListWidth : 70,
37982      
37983      triggerAction: 'query',
37984      
37985      validClass : "has-success",
37986      
37987      invalidClass: "has-warning",
37988      
37989      //new settings
37990      defaultCountry: 'hk',
37991      
37992      preferedCountries: undefined, //array
37993      
37994      filterCountries: undefined, //array
37995      
37996      displayMode: undefined, //string
37997      
37998      getAutoCreate : function(){
37999     
38000          this.list = Roo.bootstrap.PhoneInput.List;
38001     
38002         if(this.filterCountries) {
38003             for(var i = 0; i < this.filterCountries.length; i++) {
38004                 delete this.list[this.filterCountries[i]];
38005             }
38006         }
38007         
38008         if (this.preferedCountries) {
38009             //another list??
38010         }
38011         
38012          var align = this.labelAlign || this.parentLabelAlign();
38013          
38014          var id = Roo.id(); //all el??
38015          
38016          var cfg = {
38017              cls: 'form-group'
38018          };
38019          
38020          var input =  {
38021              tag: 'input',
38022              id : id,
38023              type : this.inputType,
38024              cls : 'form-control',
38025              style: 'padding-left: 60px;',
38026              placeholder : this.placeholder || ''
38027          };
38028          
38029          if (this.name) {
38030              input.name = this.name;
38031          }
38032          if (this.size) {
38033              input.cls += ' input-' + this.size;
38034          }
38035          
38036          if (this.disabled) {
38037              input.disabled=true;
38038          }
38039          
38040          var inputblock = input;
38041          
38042          if(this.hasFeedback && !this.allowBlank){
38043              var feedback = {
38044                  tag: 'span',
38045                  cls: 'glyphicon form-control-feedback'
38046              };
38047          }
38048          
38049          inputblock = {
38050              cn :  []
38051          };
38052          
38053          inputblock.cn.push(input);
38054          
38055          if(this.hasFeedback && !this.allowBlank){
38056              inputblock.cls += 'has-feedback';
38057              inputblock.cn.push(feedback);
38058          }
38059          
38060          var box = {
38061              tag: 'div',
38062              cn: [
38063                  {
38064                      tag: 'input',
38065                      type : 'hidden',
38066                      cls: 'form-hidden-field'
38067                  },
38068                  inputblock
38069              ]
38070          };
38071          
38072          var flag = {
38073              tag: 'span',
38074              html: 'flag',
38075              style: 'margin-right:5px',
38076              cls: 'roo-selected-region',
38077              cn: [] //flag position ... (iti-flag-us)
38078          };
38079          
38080          var caret = {
38081              tag: 'span',
38082              cls: 'caret'
38083           };
38084           
38085          if (this.caret != false) {
38086              caret = {
38087                   tag: 'i',
38088                   cls: 'fa fa-' + this.caret
38089              };
38090          }
38091          
38092          var combobox = {
38093              cls: 'roo-select2-container input-group',
38094              cn: []
38095          };
38096          
38097          combobox.cn.push({
38098              tag :'span',
38099              cls : 'input-group-addon btn dropdown-toggle',
38100              style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38101              cn : [
38102                  flag,
38103                  caret,
38104                  {
38105                      tag: 'span',
38106                      cls: 'combobox-clear',
38107                      cn  : [
38108                          {
38109                              tag : 'i',
38110                              cls: 'icon-remove'
38111                          }
38112                      ]
38113                  }
38114              ]
38115          });
38116          
38117          combobox.cn.push(box);
38118          
38119          if (align ==='left' && this.fieldLabel.length) {
38120              
38121              cfg.cls += ' roo-form-group-label-left';
38122
38123              cfg.cn = [
38124                  {
38125                      tag : 'i',
38126                      cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38127                      tooltip : 'This field is required'
38128                  },
38129                  {
38130                      tag: 'label',
38131                      'for' :  id,
38132                      cls : 'control-label',
38133                      html : this.fieldLabel
38134
38135                  },
38136                  {
38137                      cls : "", 
38138                      cn: [
38139                          combobox
38140                      ]
38141                  }
38142              ];
38143              
38144              var labelCfg = cfg.cn[1];
38145              var contentCfg = cfg.cn[2];
38146              
38147              if(this.indicatorpos == 'right'){
38148                  cfg.cn = [
38149                      {
38150                          tag: 'label',
38151                          'for' :  id,
38152                          cls : 'control-label',
38153                          cn : [
38154                              {
38155                                  tag : 'span',
38156                                  html : this.fieldLabel
38157                              },
38158                              {
38159                                  tag : 'i',
38160                                  cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38161                                  tooltip : 'This field is required'
38162                              }
38163                          ]
38164                      },
38165                      {
38166                          cls : "", 
38167                          cn: [
38168                              combobox
38169                          ]
38170                      }
38171
38172                  ];
38173                  
38174                  labelCfg = cfg.cn[0];
38175                  contentCfg = cfg.cn[1];
38176              }
38177              
38178              if(this.labelWidth > 12){
38179                  labelCfg.style = "width: " + this.labelWidth + 'px';
38180              }
38181              
38182              if(this.labelWidth < 13 && this.labelmd == 0){
38183                  this.labelmd = this.labelWidth;
38184              }
38185              
38186              if(this.labellg > 0){
38187                  labelCfg.cls += ' col-lg-' + this.labellg;
38188                  contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38189              }
38190              
38191              if(this.labelmd > 0){
38192                  labelCfg.cls += ' col-md-' + this.labelmd;
38193                  contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38194              }
38195              
38196              if(this.labelsm > 0){
38197                  labelCfg.cls += ' col-sm-' + this.labelsm;
38198                  contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38199              }
38200              
38201              if(this.labelxs > 0){
38202                  labelCfg.cls += ' col-xs-' + this.labelxs;
38203                  contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38204              }
38205              
38206          } else if ( this.fieldLabel.length) {
38207  //                Roo.log(" label");
38208              cfg.cn = [
38209                  {
38210                     tag : 'i',
38211                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38212                     tooltip : 'This field is required'
38213                 },
38214                 {
38215                     tag: 'label',
38216                     //cls : 'input-group-addon',
38217                     html : this.fieldLabel
38218
38219                 },
38220
38221                 combobox
38222
38223              ];
38224              
38225              if(this.indicatorpos == 'right'){
38226                  
38227                  cfg.cn = [
38228                      {
38229                         tag: 'label',
38230                         cn : [
38231                             {
38232                                 tag : 'span',
38233                                 html : this.fieldLabel
38234                             },
38235                             {
38236                                tag : 'i',
38237                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38238                                tooltip : 'This field is required'
38239                             }
38240                         ]
38241                      },
38242                      combobox
38243                  ];
38244              }
38245          } else {
38246                  cfg = combobox
38247          }
38248          
38249          var settings=this;
38250          ['xs','sm','md','lg'].map(function(size){
38251              if (settings[size]) {
38252                  cfg.cls += ' col-' + size + '-' + settings[size];
38253              }
38254          });
38255          
38256          return cfg;
38257      },
38258      
38259      _initEventsCalled : false,
38260      
38261      initEvents: function()
38262      {   
38263          if (this._initEventsCalled) {
38264              return;
38265          }
38266          
38267          this._initEventsCalled = true;
38268          
38269          this.store =  new Roo.data.SimpleStore({
38270              data : this.list,
38271              fields : ['name','iso','dial_code','order','area_code']
38272          });
38273          
38274          this.store = Roo.factory(this.store, Roo.data);
38275          this.store.parent = this;
38276          
38277          Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38278          
38279          var _this = this;
38280          
38281          (function(){
38282              var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38283              _this.list.setWidth(lw);
38284          }).defer(100);
38285          
38286          this.list.on('mouseover', this.onViewOver, this);
38287          this.list.on('mousemove', this.onViewMove, this);
38288          this.list.on('scroll', this.onViewScroll, this);
38289          
38290          if(!this.tpl){
38291              this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38292          }
38293
38294          this.view = new Roo.View(this.list, this.tpl, {
38295              singleSelect:true, store: this.store, selectedClass: this.selectedClass
38296          });
38297          
38298          this.view.on('click', this.onViewClick, this);
38299          
38300          this.store.on('beforeload', this.onBeforeLoad, this);
38301          this.store.on('load', this.onLoad, this);
38302          this.store.on('loadexception', this.onLoadException, this);
38303          
38304          this.keyNav = new Roo.KeyNav(this.inputEl(), {
38305              "up" : function(e){
38306                  this.inKeyMode = true;
38307                  this.selectPrev();
38308              },
38309
38310              "down" : function(e){
38311                  if(!this.isExpanded()){
38312                      this.onTriggerClick();
38313                  }else{
38314                      this.inKeyMode = true;
38315                      this.selectNext();
38316                  }
38317              },
38318
38319              "enter" : function(e){
38320  //                this.onViewClick();
38321                  //return true;
38322                  this.collapse();
38323                  
38324                  if(this.fireEvent("specialkey", this, e)){
38325                      this.onViewClick(false);
38326                  }
38327                  
38328                  return true;
38329              },
38330
38331              "esc" : function(e){
38332                  this.collapse();
38333              },
38334
38335              "tab" : function(e){
38336                  this.collapse();
38337                  
38338                  if(this.fireEvent("specialkey", this, e)){
38339                      this.onViewClick(false);
38340                  }
38341                  
38342                  return true;
38343              },
38344
38345              scope : this,
38346
38347              doRelay : function(foo, bar, hname){
38348                  if(hname == 'down' || this.scope.isExpanded()){
38349                     return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38350                  }
38351                  return true;
38352              },
38353
38354              forceKeyDown: true
38355          });
38356          
38357     },
38358     
38359     onViewOver : function(e, t){
38360         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38361             return;
38362         }
38363         var item = this.view.findItemFromChild(t);
38364         
38365         if(item){
38366             var index = this.view.indexOf(item);
38367             this.select(index, false);
38368         }
38369     },
38370     
38371     onViewMove : function(e, t){
38372         this.inKeyMode = false;
38373     },
38374     
38375     onViewScroll : function(e, t){
38376         
38377         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){
38378             return;
38379         }
38380         
38381         this.hasQuery = true;
38382         
38383         this.loading = this.list.select('.loading', true).first();
38384         
38385         if(this.loading === null){
38386             this.list.createChild({
38387                 tag: 'div',
38388                 cls: 'loading roo-select2-more-results roo-select2-active',
38389                 html: 'Loading more results...'
38390             });
38391             
38392             this.loading = this.list.select('.loading', true).first();
38393             
38394             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38395             
38396             this.loading.hide();
38397         }
38398         
38399         this.loading.show();
38400         
38401         var _combo = this;
38402         
38403         this.page++;
38404         this.loadNext = true;
38405         
38406         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38407         
38408         return;
38409     },
38410     
38411     onTriggerClick : function(e)
38412     {
38413         Roo.log('trigger click');
38414         
38415         if(this.disabled || !this.triggerList){
38416             return;
38417         }
38418         
38419         this.page = 0;
38420         this.loadNext = false;
38421         
38422         if(this.isExpanded()){
38423             this.collapse();
38424             if (!this.blockFocus) {
38425                 this.inputEl().focus();
38426             }
38427             
38428         }else {
38429             this.hasFocus = true;
38430             if(this.triggerAction == 'all') {
38431                 this.doQuery(this.allQuery, true);
38432             } else {
38433                 this.doQuery(this.getRawValue());
38434             }
38435             if (!this.blockFocus) {
38436                 this.inputEl().focus();
38437             }
38438         }
38439     }
38440     
38441  });
38442
38443  Roo.apply(Roo.bootstrap.PhoneInput, {
38444      
38445      /**
38446       * iso2 and abbr for all countries
38447       * @type Object
38448       */
38449      List : [
38450          ["Afghanistan (‫افغانستان‬‎)", "af", "93"],
38451          ["Albania (Shqipëri)", "al", "355"],
38452          ["Algeria (‫الجزائر‬‎)", "dz", "213"],
38453          ["American Samoa", "as", "1684"],
38454          ["Andorra", "ad", "376"],
38455          ["Angola", "ao", "244"],
38456          ["Anguilla", "ai", "1264"],
38457          ["Antigua and Barbuda", "ag", "1268"],
38458          ["Argentina", "ar", "54"],
38459          ["Armenia (Հայաստան)", "am", "374"],
38460          ["Aruba", "aw", "297"],
38461          ["Australia", "au", "61", 0],
38462          ["Austria (Österreich)", "at", "43"],
38463          ["Azerbaijan (Azərbaycan)", "az", "994"],
38464          ["Bahamas", "bs", "1242"],
38465          ["Bahrain (‫البحرين‬‎)", "bh", "973"],
38466          ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38467          ["Barbados", "bb", "1246"],
38468          ["Belarus (Беларусь)", "by", "375"],
38469          ["Belgium (België)", "be", "32"],
38470          ["Belize", "bz", "501"],
38471          ["Benin (Bénin)", "bj", "229"],
38472          ["Bermuda", "bm", "1441"],
38473          ["Bhutan (འབྲུག)", "bt", "975"],
38474          ["Bolivia", "bo", "591"],
38475          ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38476          ["Botswana", "bw", "267"],
38477          ["Brazil (Brasil)", "br", "55"],
38478          ["British Indian Ocean Territory", "io", "246"],
38479          ["British Virgin Islands", "vg", "1284"],
38480          ["Brunei", "bn", "673"],
38481          ["Bulgaria (България)", "bg", "359"],
38482          ["Burkina Faso", "bf", "226"],
38483          ["Burundi (Uburundi)", "bi", "257"],
38484          ["Cambodia (កម្ពុជា)", "kh", "855"],
38485          ["Cameroon (Cameroun)", "cm", "237"],
38486          ["Canada", "ca", "1", 1, ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]],
38487          ["Cape Verde (Kabu Verdi)", "cv", "238"],
38488          ["Caribbean Netherlands", "bq", "599", 1],
38489          ["Cayman Islands", "ky", "1345"],
38490          ["Central African Republic (République centrafricaine)", "cf", "236"],
38491          ["Chad (Tchad)", "td", "235"],
38492          ["Chile", "cl", "56"],
38493          ["China (中国)", "cn", "86"],
38494          ["Christmas Island", "cx", "61", 2],
38495          ["Cocos (Keeling) Islands", "cc", "61", 1],
38496          ["Colombia", "co", "57"],
38497          ["Comoros (‫جزر القمر‬‎)", "km", "269"],
38498          ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38499          ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38500          ["Cook Islands", "ck", "682"],
38501          ["Costa Rica", "cr", "506"],
38502          ["Côte d’Ivoire", "ci", "225"],
38503          ["Croatia (Hrvatska)", "hr", "385"],
38504          ["Cuba", "cu", "53"],
38505          ["Curaçao", "cw", "599", 0],
38506          ["Cyprus (Κύπρος)", "cy", "357"],
38507          ["Czech Republic (Česká republika)", "cz", "420"],
38508          ["Denmark (Danmark)", "dk", "45"],
38509          ["Djibouti", "dj", "253"],
38510          ["Dominica", "dm", "1767"],
38511          ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38512          ["Ecuador", "ec", "593"],
38513          ["Egypt (‫مصر‬‎)", "eg", "20"],
38514          ["El Salvador", "sv", "503"],
38515          ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38516          ["Eritrea", "er", "291"],
38517          ["Estonia (Eesti)", "ee", "372"],
38518          ["Ethiopia", "et", "251"],
38519          ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38520          ["Faroe Islands (Føroyar)", "fo", "298"],
38521          ["Fiji", "fj", "679"],
38522          ["Finland (Suomi)", "fi", "358", 0],
38523          ["France", "fr", "33"],
38524          ["French Guiana (Guyane française)", "gf", "594"],
38525          ["French Polynesia (Polynésie française)", "pf", "689"],
38526          ["Gabon", "ga", "241"],
38527          ["Gambia", "gm", "220"],
38528          ["Georgia (საქართველო)", "ge", "995"],
38529          ["Germany (Deutschland)", "de", "49"],
38530          ["Ghana (Gaana)", "gh", "233"],
38531          ["Gibraltar", "gi", "350"],
38532          ["Greece (Ελλάδα)", "gr", "30"],
38533          ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38534          ["Grenada", "gd", "1473"],
38535          ["Guadeloupe", "gp", "590", 0],
38536          ["Guam", "gu", "1671"],
38537          ["Guatemala", "gt", "502"],
38538          ["Guernsey", "gg", "44", 1],
38539          ["Guinea (Guinée)", "gn", "224"],
38540          ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38541          ["Guyana", "gy", "592"],
38542          ["Haiti", "ht", "509"],
38543          ["Honduras", "hn", "504"],
38544          ["Hong Kong (香港)", "hk", "852"],
38545          ["Hungary (Magyarország)", "hu", "36"],
38546          ["Iceland (Ísland)", "is", "354"],
38547          ["India (भारत)", "in", "91"],
38548          ["Indonesia", "id", "62"],
38549          ["Iran (‫ایران‬‎)", "ir", "98"],
38550          ["Iraq (‫العراق‬‎)", "iq", "964"],
38551          ["Ireland", "ie", "353"],
38552          ["Isle of Man", "im", "44", 2],
38553          ["Israel (‫ישראל‬‎)", "il", "972"],
38554          ["Italy (Italia)", "it", "39", 0],
38555          ["Jamaica", "jm", "1876"],
38556          ["Japan (日本)", "jp", "81"],
38557          ["Jersey", "je", "44", 3],
38558          ["Jordan (‫الأردن‬‎)", "jo", "962"],
38559          ["Kazakhstan (Казахстан)", "kz", "7", 1],
38560          ["Kenya", "ke", "254"],
38561          ["Kiribati", "ki", "686"],
38562          ["Kosovo", "xk", "383"],
38563          ["Kuwait (‫الكويت‬‎)", "kw", "965"],
38564          ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38565          ["Laos (ລາວ)", "la", "856"],
38566          ["Latvia (Latvija)", "lv", "371"],
38567          ["Lebanon (‫لبنان‬‎)", "lb", "961"],
38568          ["Lesotho", "ls", "266"],
38569          ["Liberia", "lr", "231"],
38570          ["Libya (‫ليبيا‬‎)", "ly", "218"],
38571          ["Liechtenstein", "li", "423"],
38572          ["Lithuania (Lietuva)", "lt", "370"],
38573          ["Luxembourg", "lu", "352"],
38574          ["Macau (澳門)", "mo", "853"],
38575          ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38576          ["Madagascar (Madagasikara)", "mg", "261"],
38577          ["Malawi", "mw", "265"],
38578          ["Malaysia", "my", "60"],
38579          ["Maldives", "mv", "960"],
38580          ["Mali", "ml", "223"],
38581          ["Malta", "mt", "356"],
38582          ["Marshall Islands", "mh", "692"],
38583          ["Martinique", "mq", "596"],
38584          ["Mauritania (‫موريتانيا‬‎)", "mr", "222"],
38585          ["Mauritius (Moris)", "mu", "230"],
38586          ["Mayotte", "yt", "262", 1],
38587          ["Mexico (México)", "mx", "52"],
38588          ["Micronesia", "fm", "691"],
38589          ["Moldova (Republica Moldova)", "md", "373"],
38590          ["Monaco", "mc", "377"],
38591          ["Mongolia (Монгол)", "mn", "976"],
38592          ["Montenegro (Crna Gora)", "me", "382"],
38593          ["Montserrat", "ms", "1664"],
38594          ["Morocco (‫المغرب‬‎)", "ma", "212", 0],
38595          ["Mozambique (Moçambique)", "mz", "258"],
38596          ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38597          ["Namibia (Namibië)", "na", "264"],
38598          ["Nauru", "nr", "674"],
38599          ["Nepal (नेपाल)", "np", "977"],
38600          ["Netherlands (Nederland)", "nl", "31"],
38601          ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38602          ["New Zealand", "nz", "64"],
38603          ["Nicaragua", "ni", "505"],
38604          ["Niger (Nijar)", "ne", "227"],
38605          ["Nigeria", "ng", "234"],
38606          ["Niue", "nu", "683"],
38607          ["Norfolk Island", "nf", "672"],
38608          ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38609          ["Northern Mariana Islands", "mp", "1670"],
38610          ["Norway (Norge)", "no", "47", 0],
38611          ["Oman (‫عُمان‬‎)", "om", "968"],
38612          ["Pakistan (‫پاکستان‬‎)", "pk", "92"],
38613          ["Palau", "pw", "680"],
38614          ["Palestine (‫فلسطين‬‎)", "ps", "970"],
38615          ["Panama (Panamá)", "pa", "507"],
38616          ["Papua New Guinea", "pg", "675"],
38617          ["Paraguay", "py", "595"],
38618          ["Peru (Perú)", "pe", "51"],
38619          ["Philippines", "ph", "63"],
38620          ["Poland (Polska)", "pl", "48"],
38621          ["Portugal", "pt", "351"],
38622          ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38623          ["Qatar (‫قطر‬‎)", "qa", "974"],
38624          ["Réunion (La Réunion)", "re", "262", 0],
38625          ["Romania (România)", "ro", "40"],
38626          ["Russia (Россия)", "ru", "7", 0],
38627          ["Rwanda", "rw", "250"],
38628          ["Saint Barthélemy", "bl", "590", 1],
38629          ["Saint Helena", "sh", "290"],
38630          ["Saint Kitts and Nevis", "kn", "1869"],
38631          ["Saint Lucia", "lc", "1758"],
38632          ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38633          ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38634          ["Saint Vincent and the Grenadines", "vc", "1784"],
38635          ["Samoa", "ws", "685"],
38636          ["San Marino", "sm", "378"],
38637          ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38638          ["Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966"],
38639          ["Senegal (Sénégal)", "sn", "221"],
38640          ["Serbia (Србија)", "rs", "381"],
38641          ["Seychelles", "sc", "248"],
38642          ["Sierra Leone", "sl", "232"],
38643          ["Singapore", "sg", "65"],
38644          ["Sint Maarten", "sx", "1721"],
38645          ["Slovakia (Slovensko)", "sk", "421"],
38646          ["Slovenia (Slovenija)", "si", "386"],
38647          ["Solomon Islands", "sb", "677"],
38648          ["Somalia (Soomaaliya)", "so", "252"],
38649          ["South Africa", "za", "27"],
38650          ["South Korea (대한민국)", "kr", "82"],
38651          ["South Sudan (‫جنوب السودان‬‎)", "ss", "211"],
38652          ["Spain (España)", "es", "34"],
38653          ["Sri Lanka (ශ්‍රී ලංකාව)", "lk", "94"],
38654          ["Sudan (‫السودان‬‎)", "sd", "249"],
38655          ["Suriname", "sr", "597"],
38656          ["Svalbard and Jan Mayen", "sj", "47", 1],
38657          ["Swaziland", "sz", "268"],
38658          ["Sweden (Sverige)", "se", "46"],
38659          ["Switzerland (Schweiz)", "ch", "41"],
38660          ["Syria (‫سوريا‬‎)", "sy", "963"],
38661          ["Taiwan (台灣)", "tw", "886"],
38662          ["Tajikistan", "tj", "992"],
38663          ["Tanzania", "tz", "255"],
38664          ["Thailand (ไทย)", "th", "66"],
38665          ["Timor-Leste", "tl", "670"],
38666          ["Togo", "tg", "228"],
38667          ["Tokelau", "tk", "690"],
38668          ["Tonga", "to", "676"],
38669          ["Trinidad and Tobago", "tt", "1868"],
38670          ["Tunisia (‫تونس‬‎)", "tn", "216"],
38671          ["Turkey (Türkiye)", "tr", "90"],
38672          ["Turkmenistan", "tm", "993"],
38673          ["Turks and Caicos Islands", "tc", "1649"],
38674          ["Tuvalu", "tv", "688"],
38675          ["U.S. Virgin Islands", "vi", "1340"],
38676          ["Uganda", "ug", "256"],
38677          ["Ukraine (Україна)", "ua", "380"],
38678          ["United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971"],
38679          ["United Kingdom", "gb", "44", 0],
38680          ["United States", "us", "1", 0],
38681          ["Uruguay", "uy", "598"],
38682          ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38683          ["Vanuatu", "vu", "678"],
38684          ["Vatican City (Città del Vaticano)", "va", "39", 1],
38685          ["Venezuela", "ve", "58"],
38686          ["Vietnam (Việt Nam)", "vn", "84"],
38687          ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38688          ["Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1],
38689          ["Yemen (‫اليمن‬‎)", "ye", "967"],
38690          ["Zambia", "zm", "260"],
38691          ["Zimbabwe", "zw", "263"],
38692          ["Åland Islands", "ax", "358", 1]
38693      ]
38694  });